From b9124b049a47323cafa8807db4d501fb32a53297 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Sun, 12 Oct 2025 17:38:12 -0300 Subject: fix problem with outfit comparisons - fixes #39 --- src/common.hh | 2 +- src/cr.hh | 10 +++++++--- src/cract.cc | 5 ++--- src/crmain.cc | 9 ++++----- src/crnonpl.cc | 2 +- src/magic.cc | 4 ++-- src/sending.cc | 4 ++-- 7 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/common.hh b/src/common.hh index a8f1460..a22a511 100644 --- a/src/common.hh +++ b/src/common.hh @@ -22,7 +22,7 @@ typedef uint64_t uint64; typedef uintptr_t uintptr; typedef size_t usize; -#define STATIC_ASSERT(expr) static_assert((expr), "static assertion failed: " #expr) +#define STATIC_ASSERT(expr) static_assert((expr), #expr) #define NARRAY(arr) (int)(sizeof(arr) / sizeof(arr[0])) #define ISPOW2(x) ((x) != 0 && ((x) & ((x) - 1)) == 0) #define KB(x) ((usize)(x) << 10) diff --git a/src/cr.hh b/src/cr.hh index c4034e5..8bf6ce8 100644 --- a/src/cr.hh +++ b/src/cr.hh @@ -17,14 +17,18 @@ struct TPlayer; struct TOutfit{ int OutfitID; union{ - uint16 ObjectType; + int ObjectType; uint8 Colors[4]; - uint32 PackedData; }; constexpr bool operator==(const TOutfit &Other) const { + // IMPORTANT(fusion): We don't need to have special comparisson cases + // if all members of the union have the same size. This is not true + // otherwise when smaller active fields leave trailing bytes of the + // union filled with whatever data was there before. + STATIC_ASSERT(sizeof(this->ObjectType) == sizeof(this->Colors)); return this->OutfitID == Other.OutfitID - && this->PackedData == Other.PackedData; + && this->ObjectType == Other.ObjectType; } static constexpr TOutfit Invisible(void){ diff --git a/src/cract.cc b/src/cract.cc index dea0ae2..e0af29f 100644 --- a/src/cract.cc +++ b/src/cract.cc @@ -1566,12 +1566,11 @@ void TCreature::NotifyChangeInventory(void){ AnnounceChangedCreature(this->ID, CREATURE_SPEED_CHANGED); }else if(SkillNr == SKILL_ILLUSION){ if(NewDelta[SKILL_ILLUSION] > 0){ - if(this->Outfit.OutfitID != 0 || this->Outfit.ObjectType != 0){ + if(!this->IsInvisible()){ if(Skill->TimerValue() != 0){ this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); } - this->Outfit.OutfitID = 0; - this->Outfit.ObjectType = 0; + this->Outfit = TOutfit::Invisible(); } }else if(Skill->TimerValue() == 0){ this->Outfit = this->OrgOutfit; diff --git a/src/crmain.cc b/src/crmain.cc index 15c72b8..0c63c7f 100644 --- a/src/crmain.cc +++ b/src/crmain.cc @@ -1196,8 +1196,7 @@ TRaceData::TRaceData(void) : { this->Name[0] = 0; this->Article[0] = 0; - this->Outfit.OutfitID = 0; - this->Outfit.ObjectType = 0; + this->Outfit = TOutfit{}; this->Blood = BT_BLOOD; this->ExperiencePoints = 0; this->FleeThreshold = 0; @@ -1334,7 +1333,7 @@ TOutfit ReadOutfit(TReadScriptFile *Script){ Outfit.OutfitID = Script->readNumber(); Script->readSymbol(','); if(Outfit.OutfitID == 0){ - Outfit.ObjectType = (uint16)Script->readNumber(); + Outfit.ObjectType = Script->readNumber(); }else{ memcpy(Outfit.Colors, Script->readBytesequence(), sizeof(Outfit.Colors)); } @@ -1348,7 +1347,7 @@ void WriteOutfit(TWriteScriptFile *Script, TOutfit Outfit){ Script->writeNumber(Outfit.OutfitID); Script->writeText(","); if(Outfit.OutfitID == 0){ - Script->writeNumber((int)Outfit.ObjectType); + Script->writeNumber(Outfit.ObjectType); }else{ Script->writeBytesequence(Outfit.Colors, sizeof(Outfit.Colors)); } @@ -1647,7 +1646,7 @@ void LoadRace(const char *FileName){ Script.readSymbol('('); TOutfit Outfit = ReadOutfit(&Script); SpellData->ImpactParam1 = Outfit.OutfitID; - SpellData->ImpactParam2 = (int)Outfit.PackedData; + SpellData->ImpactParam2 = Outfit.ObjectType; Script.readSymbol(','); SpellData->ImpactParam3 = Script.readNumber(); Script.readSymbol(')'); diff --git a/src/crnonpl.cc b/src/crnonpl.cc index f334626..cfd36b3 100644 --- a/src/crnonpl.cc +++ b/src/crnonpl.cc @@ -2578,7 +2578,7 @@ void TMonster::IdleStimulus(void){ case IMPACT_OUTFIT:{ TOutfit Outfit = {}; Outfit.OutfitID = SpellData->ImpactParam1; - Outfit.PackedData = (uint32)SpellData->ImpactParam2; + Outfit.ObjectType = SpellData->ImpactParam2; int Duration = SpellData->ImpactParam3; Impact = new TOutfitImpact(this, Outfit, Duration); break; diff --git a/src/magic.cc b/src/magic.cc index f7fbe70..c069b68 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -2344,7 +2344,7 @@ void Invisibility(TCreature *Actor, int ManaPoints, int SoulPoints, int Duration } CheckMana(Actor, ManaPoints, SoulPoints, 1000); - Actor->Outfit = TOutfit{}; + Actor->Outfit = TOutfit::Invisible(); Actor->SetTimer(SKILL_ILLUSION, 1, Duration, Duration, -1); GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); } @@ -2496,7 +2496,7 @@ void ObjectIllusion(TCreature *Actor, int ManaPoints, int SoulPoints, Object Tar CheckMana(Actor, ManaPoints, SoulPoints, 1000); if(Actor->Skills[SKILL_ILLUSION]->Get() == 0){ Actor->Outfit.OutfitID = 0; - Actor->Outfit.ObjectType = (uint16)TargetType.getDisguise().TypeID; + Actor->Outfit.ObjectType = TargetType.getDisguise().TypeID; Actor->SetTimer(SKILL_ILLUSION, 1, Duration, Duration, -1); } diff --git a/src/sending.cc b/src/sending.cc index 5a5a969..eb78e82 100644 --- a/src/sending.cc +++ b/src/sending.cc @@ -162,7 +162,7 @@ static void SendString(TConnection *Connection, const char *String){ static void SendOutfit(TConnection *Connection, TOutfit Outfit){ SendWord(Connection, (uint16)Outfit.OutfitID); if(Outfit.OutfitID == 0){ - SendWord(Connection, Outfit.ObjectType); + SendWord(Connection, (uint16)Outfit.ObjectType); }else{ SendBytes(Connection, Outfit.Colors, sizeof(Outfit.Colors)); } @@ -1166,7 +1166,7 @@ void SendPlayerData(TConnection *Connection){ return; } - // TODO(fusion): I don't even think skills can even be NULL. + // TODO(fusion): I don't think skills can even be NULL. int Level = 0; int LevelPercent = 0; int Experience = 0; -- cgit v1.2.3