- use TArrays instead of TMaps to store damage factors and pain chances.

For these fields maps have no advantage. Linearly searching a small array with up to 10 entries is nearly always faster than generating a hash for finding the entry in the map.
This commit is contained in:
Christoph Oelckers 2017-04-11 23:29:37 +02:00
parent 4afe2d4218
commit 854053a14f
12 changed files with 90 additions and 102 deletions

View file

@ -876,7 +876,7 @@ static int PatchThing (int thingy)
else
{
info = GetDefaultByType (type);
ednum = &type->DoomEdNum;
ednum = &type->ActorInfo()->DoomEdNum;
}
}
}

View file

@ -98,9 +98,10 @@ CCMD (dumpactors)
PClassActor *acls = dyn_cast<PClassActor>(cls);
if (acls != NULL)
{
auto ainfo = acls->ActorInfo();
Printf("%s\t%i\t%i\t%s\t%s\n",
acls->TypeName.GetChars(), acls->DoomEdNum,
acls->SpawnID, filters[acls->GameFilter & 31],
acls->TypeName.GetChars(), ainfo->DoomEdNum,
ainfo->SpawnID, filters[ainfo->GameFilter & 31],
acls->SourceLumpName.GetChars());
}
else if (cls != NULL)

View file

@ -985,7 +985,8 @@ void FWeaponSlots::AddExtraWeapons()
continue;
}
auto weapdef = ((AWeapon*)GetDefaultByType(cls));
if ((cls->GameFilter == GAME_Any || (cls->GameFilter & gameinfo.gametype)) &&
auto gf = cls->ActorInfo()->GameFilter;
if ((gf == GAME_Any || (gf & gameinfo.gametype)) &&
cls->ActorInfo()->Replacement == nullptr && // Replaced weapons don't get slotted.
!(weapdef->WeaponFlags & WIF_POWERED_UP) &&
!LocateWeapon(cls, nullptr, nullptr) // Don't duplicate it if it's already present.
@ -1470,8 +1471,8 @@ static int ntoh_cmp(const void *a, const void *b)
{
PClassActor *c1 = *(PClassActor **)a;
PClassActor *c2 = *(PClassActor **)b;
int g1 = c1->GameFilter == GAME_Any ? 1 : (c1->GameFilter & gameinfo.gametype) ? 0 : 2;
int g2 = c2->GameFilter == GAME_Any ? 1 : (c2->GameFilter & gameinfo.gametype) ? 0 : 2;
int g1 = c1->ActorInfo()->GameFilter == GAME_Any ? 1 : (c1->ActorInfo()->GameFilter & gameinfo.gametype) ? 0 : 2;
int g2 = c2->ActorInfo()->GameFilter == GAME_Any ? 1 : (c2->ActorInfo()->GameFilter & gameinfo.gametype) ? 0 : 2;
if (g1 != g2)
{
return g1 - g2;

View file

@ -294,12 +294,7 @@ void PClassActor::StaticSetActorNums()
PClassActor::PClassActor()
{
GameFilter = GAME_Any;
SpawnID = 0;
DoomEdNum = -1;
StateList = NULL;
DamageFactors = NULL;
PainChances = NULL;
DropItems = NULL;
// Record this in the master list.
@ -314,14 +309,6 @@ PClassActor::PClassActor()
PClassActor::~PClassActor()
{
if (DamageFactors != NULL)
{
delete DamageFactors;
}
if (PainChances != NULL)
{
delete PainChances;
}
if (StateList != NULL)
{
StateList->Destroy();
@ -340,26 +327,14 @@ void PClassActor::DeriveData(PClass *newclass)
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassActor)));
PClassActor *newa = static_cast<PClassActor *>(newclass);
newa->DefaultStateUsage = DefaultStateUsage;
newa->distancecheck = distancecheck;
newa->DropItems = DropItems;
newa->VisibleToPlayerClass = VisibleToPlayerClass;
if (DamageFactors != NULL)
{
// copy damage factors from parent
newa->DamageFactors = new DmgFactors;
*newa->DamageFactors = *DamageFactors;
}
if (PainChances != NULL)
{
// copy pain chances from parent
newa->PainChances = new PainChanceList;
*newa->PainChances = *PainChances;
}
newa->DamageFactors = DamageFactors;
newa->PainChances = PainChances;
newa->DisplayName = DisplayName;
}
@ -450,16 +425,18 @@ void PClassActor::RegisterIDs()
}
// Conversation IDs have never been filtered by game so we cannot start doing that.
auto ConversationID = ActorInfo()->ConversationID;
if (ConversationID > 0)
{
StrifeTypes[ConversationID] = cls;
if (cls != this)
{
Printf(TEXTCOLOR_RED"Conversation ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars());
Printf(TEXTCOLOR_RED"Conversation ID %d refers to hidden class type '%s'\n", ConversationID, cls->TypeName.GetChars());
}
}
if (GameFilter == GAME_Any || (GameFilter & gameinfo.gametype))
if (ActorInfo()->GameFilter == GAME_Any || (ActorInfo()->GameFilter & gameinfo.gametype))
{
auto SpawnID = ActorInfo()->SpawnID;
if (SpawnID > 0)
{
SpawnableThings[SpawnID] = cls;
@ -468,6 +445,7 @@ void PClassActor::RegisterIDs()
Printf(TEXTCOLOR_RED"Spawn ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars());
}
}
auto DoomEdNum = ActorInfo()->DoomEdNum;
if (DoomEdNum != -1)
{
FDoomEdEntry *oldent = DoomEdMap.CheckKey(DoomEdNum);
@ -602,11 +580,12 @@ DEFINE_ACTION_FUNCTION(AActor, GetReplacee)
void PClassActor::SetDamageFactor(FName type, double factor)
{
if (DamageFactors == NULL)
for (auto & p : DamageFactors)
{
DamageFactors = new DmgFactors;
if (p.first == type) p.second = factor;
return;
}
DamageFactors->Insert(type, factor);
DamageFactors.Push({ type, factor });
}
//==========================================================================
@ -617,17 +596,15 @@ void PClassActor::SetDamageFactor(FName type, double factor)
void PClassActor::SetPainChance(FName type, int chance)
{
for (auto & p : PainChances)
{
if (p.first == type) p.second = chance;
return;
}
if (chance >= 0)
{
if (PainChances == NULL)
{
PainChances = new PainChanceList;
}
PainChances->Insert(type, MIN(chance, 256));
}
else if (PainChances != NULL)
{
PainChances->Remove(type);
PainChances.Push({ type, MIN(chance, 256) });
}
}
@ -642,13 +619,22 @@ void PClassActor::SetPainChance(FName type, int chance)
int DmgFactors::Apply(FName type, int damage)
{
auto pdf = CheckKey(type);
if (pdf == NULL && type != NAME_None)
double factor = -1.;
for (auto & p : *this)
{
pdf = CheckKey(NAME_None);
if (p.first == type)
{
factor = p.second;
break;
}
if (p.first == NAME_None)
{
factor = p.second;
}
}
if (!pdf) return damage;
return int(damage * *pdf);
if (factor < 0.) return damage;
return int(damage * factor);
}
@ -755,13 +741,15 @@ FString DamageTypeDefinition::GetObituary(FName type)
double DamageTypeDefinition::GetMobjDamageFactor(FName type, DmgFactors const * const factors)
{
double defaultfac = -1.;
if (factors)
{
// If the actor has named damage factors, look for a specific factor
auto pdf = factors->CheckKey(type);
if (pdf) return *pdf; // type specific damage type
for (auto & p : *factors)
{
if (p.first == type) return p.second; // type specific damage type
if (p.first == NAME_None) defaultfac = p.second;
}
// If this was nonspecific damage, don't fall back to nonspecific search
if (type == NAME_None) return 1.;
}
@ -779,18 +767,17 @@ double DamageTypeDefinition::GetMobjDamageFactor(FName type, DmgFactors const *
}
{
auto pdf = factors->CheckKey(NAME_None);
DamageTypeDefinition *dtd = Get(type);
// Here we are looking for modifications to untyped damage
// If the calling actor defines untyped damage factor, that is contained in "pdf".
if (pdf) // normal damage available
if (defaultfac >= 0.) // normal damage available
{
if (dtd)
{
if (dtd->ReplaceFactor) return dtd->DefaultFactor; // use default instead of untyped factor
return *pdf * dtd->DefaultFactor; // use default as modification of untyped factor
return defaultfac * dtd->DefaultFactor; // use default as modification of untyped factor
}
return *pdf; // there was no default, so actor default is used
return defaultfac; // there was no default, so actor default is used
}
else if (dtd)
{

View file

@ -201,11 +201,11 @@ struct FStateLabels
#include "gametype.h"
struct DmgFactors : public TMap<FName, double>
struct DmgFactors : public TArray<std::pair<FName, double>>
{
int Apply(FName type, int damage);
};
typedef TMap<FName, int> PainChanceList;
typedef TArray<std::pair<FName, int>> PainChanceList;
struct DamageTypeDefinition
{
@ -244,11 +244,18 @@ struct FActorInfo
PClassActor *Replacee = nullptr;
FState *OwnedStates = nullptr;
int NumOwnedStates = 0;
uint8_t GameFilter = GAME_Any;
uint16_t SpawnID = 0;
uint16_t ConversationID = 0;
int16_t DoomEdNum = 0;
uint8_t DefaultStateUsage = 0; // state flag defaults for blocks without a qualifier.
FActorInfo() {}
FActorInfo(const FActorInfo & other)
{
LightAssociations = other.LightAssociations;
DefaultStateUsage = other.DefaultStateUsage;
}
};
@ -293,14 +300,9 @@ public:
PClassActor *GetReplacement(bool lookskill=true);
PClassActor *GetReplacee(bool lookskill=true);
uint8_t GameFilter;
uint8_t DefaultStateUsage; // state flag defaults for blocks without a qualifier.
uint16_t SpawnID;
uint16_t ConversationID;
int16_t DoomEdNum;
FStateLabels *StateList;
DmgFactors *DamageFactors;
PainChanceList *PainChances;
DmgFactors DamageFactors;
PainChanceList PainChances;
TArray<PClassActor *> VisibleToPlayerClass;

View file

@ -916,7 +916,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
int temp;
int painchance = 0;
FState * woundstate = NULL;
PainChanceList * pc = NULL;
bool justhit = false;
bool plrDontThrust = false;
bool invulpain = false;
@ -1500,14 +1499,13 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig
if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) &&
(target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY))
{
pc = target->GetClass()->PainChances;
painchance = target->PainChance;
if (pc != NULL)
for (auto & pc : target->GetClass()->PainChances)
{
int *ppc = pc->CheckKey(mod);
if (ppc != NULL)
if (pc.first == mod)
{
painchance = *ppc;
painchance = pc.second;
break;
}
}

View file

@ -7822,7 +7822,7 @@ int AActor::ApplyDamageFactor(FName damagetype, int damage) const
damage = int(damage * DamageFactor);
if (damage > 0)
{
damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, damagetype, GetClass()->DamageFactors);
damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, damagetype, &GetClass()->DamageFactors);
}
return damage;
}
@ -8273,10 +8273,10 @@ DEFINE_ACTION_FUNCTION(AActor, ApplyDamageFactors)
PARAM_INT(damage);
PARAM_INT(defdamage);
DmgFactors *df = itemcls->DamageFactors;
if (df != nullptr && df->CountUsed() != 0)
DmgFactors &df = itemcls->DamageFactors;
if (df.Size() != 0)
{
ACTION_RETURN_INT(df->Apply(damagetype, damage));
ACTION_RETURN_INT(df.Apply(damagetype, damage));
}
else
{

View file

@ -124,7 +124,6 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
bag.ClassName = type->TypeName;
#endif
type->GameFilter = GAME_Any;
sc.MustGetStringName("{");
memset (&extra, 0, sizeof(extra));
@ -334,7 +333,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
{
sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
bag.Info->DoomEdNum = (int16_t)sc.Number;
bag.Info->ActorInfo()->DoomEdNum = (int16_t)sc.Number;
}
else if (sc.Compare ("SpawnNum"))
{
@ -343,7 +342,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
{
sc.ScriptError ("SpawnNum must be in the range [0,255]");
}
bag.Info->SpawnID = (uint8_t)sc.Number;
bag.Info->ActorInfo()->SpawnID = (uint8_t)sc.Number;
}
else if (sc.Compare ("Sprite") || (
(def == DEF_BreakableDecoration || def == DEF_Projectile) &&
@ -622,7 +621,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, TArray
char *token = strtok (sc.String, ",\t\n\r");
memset (&state, 0, sizeof(state));
state.UseFlags = info->DefaultStateUsage;
state.UseFlags = info->ActorInfo()->DefaultStateUsage;
while (token != nullptr)
{

View file

@ -1016,7 +1016,7 @@ PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName par
ti = DecoDerivedClass(sc, parent, typeName);
ti->bDecorateClass = true; // we only set this for 'modern' DECORATE. The original stuff is so limited that it cannot do anything that may require flagging.
ti->DoomEdNum = -1;
ti->ActorInfo()->DoomEdNum = -1;
return ti;
}
@ -1108,7 +1108,7 @@ static PClassActor *ParseActorHeader(FScanner &sc, Baggage *bag)
try
{
PClassActor *info = CreateNewActor(sc, typeName, parentName);
info->DoomEdNum = DoomEdNum > 0 ? DoomEdNum : -1;
info->ActorInfo()->DoomEdNum = DoomEdNum > 0 ? DoomEdNum : -1;
info->SourceLumpName = Wads.GetLumpFullPath(sc.LumpNum);
if (!info->SetReplacement(replaceName))

View file

@ -142,7 +142,7 @@ void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage &
char lastsprite[5] = "";
FxExpression *ScriptCode;
FArgumentList *args = nullptr;
int flagdef = actor->DefaultStateUsage;
int flagdef = actor->ActorInfo()->DefaultStateUsage;
FScriptPosition scp;
if (sc.CheckString("("))

View file

@ -391,10 +391,9 @@ bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index)
case DEPF_QUARTERGRAVITY:
return actor->Gravity == 1./4;
case DEPF_FIRERESIST:
if (info->DamageFactors)
for (auto &df : info->DamageFactors)
{
double *df = info->DamageFactors->CheckKey(NAME_Fire);
return df && (*df) == 0.5;
if (df.first == NAME_Fire) return df.second == 0.5;
}
return false;
@ -464,33 +463,34 @@ static bool PointerCheck(PType *symtype, PType *checktype)
DEFINE_INFO_PROPERTY(game, S, Actor)
{
PROP_STRING_PARM(str, 0);
auto & GameFilter = info->ActorInfo()->GameFilter;
if (!stricmp(str, "Doom"))
{
info->GameFilter |= GAME_Doom;
GameFilter |= GAME_Doom;
}
else if (!stricmp(str, "Heretic"))
{
info->GameFilter |= GAME_Heretic;
GameFilter |= GAME_Heretic;
}
else if (!stricmp(str, "Hexen"))
{
info->GameFilter |= GAME_Hexen;
GameFilter |= GAME_Hexen;
}
else if (!stricmp(str, "Raven"))
{
info->GameFilter |= GAME_Raven;
GameFilter |= GAME_Raven;
}
else if (!stricmp(str, "Strife"))
{
info->GameFilter |= GAME_Strife;
GameFilter |= GAME_Strife;
}
else if (!stricmp(str, "Chex"))
{
info->GameFilter |= GAME_Chex;
GameFilter |= GAME_Chex;
}
else if (!stricmp(str, "Any"))
{
info->GameFilter = GAME_Any;
GameFilter = GAME_Any;
}
else
{
@ -508,7 +508,7 @@ DEFINE_INFO_PROPERTY(spawnid, I, Actor)
{
I_Error ("SpawnID must be in the range [0,65535]");
}
else info->SpawnID=(uint16_t)id;
else info->ActorInfo()->SpawnID=(uint16_t)id;
}
//==========================================================================
@ -521,7 +521,7 @@ DEFINE_INFO_PROPERTY(conversationid, IiI, Actor)
PROP_INT_PARM(id2, 2);
if (convid <= 0 || convid > 65535) return; // 0 is not usable because the dialogue scripts use it as 'no object'.
else info->ConversationID=(uint16_t)convid;
else info->ActorInfo()->ConversationID=(uint16_t)convid;
}
//==========================================================================
@ -559,7 +559,7 @@ DEFINE_PROPERTY(skip_super, 0, Actor)
DEFINE_PROPERTY(defaultstateusage, I, Actor)
{
PROP_INT_PARM(use, 0);
static_cast<PClassActor*>(bag.Info)->DefaultStateUsage = use;
static_cast<PClassActor*>(bag.Info)->ActorInfo()->DefaultStateUsage = use;
}
//==========================================================================
@ -740,7 +740,7 @@ DEFINE_PROPERTY(translation, L, Actor)
if (type == 0)
{
PROP_INT_PARM(trans, 1);
int max = 6;// (gameinfo.gametype == GAME_Strife || (info->GameFilter&GAME_Strife)) ? 6 : 2;
int max = 6;
if (trans < 0 || trans > max)
{
I_Error ("Translation must be in the range [0,%d]", max);
@ -1133,7 +1133,7 @@ static void SetIcon(FTextureID &icon, Baggage &bag, const char *i)
{
// Don't print warnings if the item is for another game or if this is a shareware IWAD.
// Strife's teaser doesn't contain all the icon graphics of the full game.
if ((bag.Info->GameFilter == GAME_Any || bag.Info->GameFilter & gameinfo.gametype) &&
if ((bag.Info->ActorInfo()->GameFilter == GAME_Any || bag.Info->ActorInfo()->GameFilter & gameinfo.gametype) &&
!(gameinfo.flags&GI_SHAREWARE) && Wads.GetLumpFile(bag.Lumpnum) != 0)
{
bag.ScriptPosition.Message(MSG_WARNING,

View file

@ -2889,7 +2889,7 @@ void ZCCCompiler::CompileStates()
}
else
{
flags = static_cast<PClassActor *>(c->Type())->DefaultStateUsage;
flags = static_cast<PClassActor *>(c->Type())->ActorInfo()->DefaultStateUsage;
}
auto st = s->Body;
if (st != nullptr) do