fix problem with outfit comparisons - fixes #39

This commit is contained in:
fusion32 2025-10-12 17:38:12 -03:00
parent 60531bf63e
commit b9124b049a
7 changed files with 19 additions and 17 deletions

View File

@ -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)

View File

@ -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){

View File

@ -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;

View File

@ -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(')');

View File

@ -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;

View File

@ -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);
}

View File

@ -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;