From ebfa80ab573b1a0ef9aec65a45fd0ae751196207 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Thu, 5 Jun 2025 12:00:34 -0300 Subject: merge creature headers --- Makefile | 14 +- TODO.md | 18 +- reference/types.hh | 100 --------- src/cr.hh | 619 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/crcombat.cc | 4 +- src/crcombat.hh | 68 ------ src/creature.cc | 488 ----------------------------------------- src/creature.hh | 154 ------------- src/crmain.cc | 589 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/crplayer.cc | 72 +++++++ src/crskill.cc | 3 +- src/crskill.hh | 173 --------------- src/info.cc | 2 +- src/magic.cc | 2 - src/magic.hh | 2 +- src/monster.hh | 93 -------- src/player.cc | 73 ------- src/player.hh | 136 ------------ src/stubs.hh | 3 +- 19 files changed, 1307 insertions(+), 1306 deletions(-) create mode 100644 src/cr.hh delete mode 100644 src/crcombat.hh delete mode 100644 src/creature.cc delete mode 100644 src/creature.hh create mode 100644 src/crmain.cc create mode 100644 src/crplayer.cc delete mode 100644 src/crskill.hh delete mode 100644 src/monster.hh delete mode 100644 src/player.cc delete mode 100644 src/player.hh diff --git a/Makefile b/Makefile index 99b89aa..c958d5b 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,9 @@ else CFLAGS += -O2 endif -HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/crcombat.hh $(SRCDIR)/creature.hh $(SRCDIR)/crskill.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/monster.hh $(SRCDIR)/objects.hh $(SRCDIR)/player.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh +HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/cr.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/objects.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh -$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/creature.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/player.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj +$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/crmain.obj $(BUILDDIR)/crplayer.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj $(CC) -c $(CFLAGS) $(LFLAGS) -o $@ $^ $(BUILDDIR)/config.obj: $(SRCDIR)/config.cc $(HEADERS) @@ -26,7 +26,11 @@ $(BUILDDIR)/crcombat.obj: $(SRCDIR)/crcombat.cc $(HEADERS) @mkdir -p $(@D) $(CC) -c $(CFLAGS) -o $@ $< -$(BUILDDIR)/creature.obj: $(SRCDIR)/creature.cc $(HEADERS) +$(BUILDDIR)/crmain.obj: $(SRCDIR)/crmain.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/crplayer.obj: $(SRCDIR)/crplayer.cc $(HEADERS) @mkdir -p $(@D) $(CC) -c $(CFLAGS) -o $@ $< @@ -54,10 +58,6 @@ $(BUILDDIR)/objects.obj: $(SRCDIR)/objects.cc $(HEADERS) @mkdir -p $(@D) $(CC) -c $(CFLAGS) -o $@ $< -$(BUILDDIR)/player.obj: $(SRCDIR)/player.cc $(HEADERS) - @mkdir -p $(@D) - $(CC) -c $(CFLAGS) -o $@ $< - $(BUILDDIR)/script.obj: $(SRCDIR)/script.cc $(HEADERS) @mkdir -p $(@D) $(CC) -c $(CFLAGS) -o $@ $< diff --git a/TODO.md b/TODO.md index 76f56d2..e237083 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,20 @@ ## TODO NEXT -- MAGIC.CC - merge creature headers into a single one CR.HH - this should help preventing dependency cycles and centralize creature data structures and globals in a single place -- CRMAIN.CC +- CRMAIN.CC, CRPLAYER.CC, CRNONPL.CC, CRACT.CC +- HOUSES.CC +- INFO.CC +- OPERATE.CC +- MOVEUSE.CC +- SENDING.CC +- RECEIVING.CC +- CONNECTIONS.CC +- COMMUNICATION.CC +- READER.CC +- WRITER.CC +- QUERY.CC +- DBFUNCS.CC ## Stack allocations Any functions that use `alloca` or some other form of dynamic stack allocations will cause decompiled functions to be an absolute mess. It usually shows up in the decompiled code as both a size computation like `-(VAR + CONST & 0xfffffff0)`, followed by some assignment. It doesn't make total sense without looking at the disassembly. I've encountered ~30 such computations and expect the functions containing them to be amongt the most challenging/annoying to be properly decompiled. @@ -15,8 +26,9 @@ I didn't dive into how exceptions are handled, but it seems that the ones relate The decompiled file has ~115K lines of C. If we take ~15K lines to be rubbish, this can be round to ~100K. Considering a low estimate of 200 lines per day, the whole process could take up to 500 days which is quite a bit but not impossible. Now considering a high estimate of 1K lines per day, it could take 100 days which is also quite a bit. ## TODO AFTER FIRST PASS +- Review dividing comments. I feel like the current "//========" blends in too easily, making it hard to see. - Trim rough edges. -- Avoid unsafe libc functions like `strcpy`, `strncpy`, `strcat`, `sprintf` etc... +- Replace unsafe libc functions like `strcpy`, `strncpy`, `strcat`, `sprintf` etc... - Handle connections inline with `poll`/`epoll` (probably?). - Remove exceptions. - Review signal usage for timing (SIGALRM, etc...). diff --git a/reference/types.hh b/reference/types.hh index 507ea78..c970e35 100644 --- a/reference/types.hh +++ b/reference/types.hh @@ -188,106 +188,6 @@ struct THelpDepot { int DepotNr; }; -struct TPlayerData { - ulong CharacterID; - pid_t Locked; - int Sticky; - bool Dirty; - int Race; - struct TOutfit OriginalOutfit; - struct TOutfit CurrentOutfit; - time_t LastLoginTime; - time_t LastLogoutTime; - int startx; - int starty; - int startz; - int posx; - int posy; - int posz; - int Profession; - int PlayerkillerEnd; - int Actual[25]; - int Maximum[25]; - int Minimum[25]; - int DeltaAct[25]; - int MagicDeltaAct[25]; - int Cycle[25]; - int MaxCycle[25]; - int Count[25]; - int MaxCount[25]; - int AddLevel[25]; - int Experience[25]; - int FactorPercent[25]; - int NextLevel[25]; - int Delta[25]; - uchar SpellList[256]; - int QuestValues[500]; - int MurderTimestamps[20]; - uchar *Inventory; - int InventorySize; - uchar *Depot[9]; - int DepotSize[9]; - ulong AccountID; - int Sex; - char Name[30]; - uchar Rights[12]; - char Guild[31]; - char Rank[31]; - char Title[31]; - int Buddies; - ulong Buddy[100]; - char BuddyName[100][30]; - ulong EarliestYellRound; - ulong EarliestTradeChannelRound; - ulong EarliestSpellTime; - ulong EarliestMultiuseTime; - ulong TalkBufferFullTime; - ulong MutingEndRound; - ulong Addressees[20]; - ulong AddresseesTimes[20]; - int NumberOfMutings; -}; - -struct TPlayer { - struct TCreature super_TCreature; - ulong AccountID; - char Guild[31]; - char Rank[31]; - char Title[31]; - char IPAddress[16]; - uchar Rights[12]; - struct Object Depot; - int DepotNr; - int DepotSpace; - enum RESULT ConstructError; - struct TPlayerData *PlayerData; - struct Object TradeObject; - ulong TradePartner; - bool TradeAccepted; - int OldState; - ulong Request; - int RequestTimestamp; - ulong RequestProcessingGamemaster; - int TutorActivities; - uchar SpellList[256]; - int QuestValues[500]; - struct Object OpenContainer[16]; - struct vector AttackedPlayers; - int NumberOfAttackedPlayers; - bool Aggressor; - struct vector FormerAttackedPlayers; - int NumberOfFormerAttackedPlayers; - bool FormerAggressor; - ulong FormerLogoutRound; - ulong PartyLeader; - ulong PartyLeavingRound; - ulong TalkBufferFullTime; - ulong MutingEndRound; - int NumberOfMutings; - ulong Addressees[20]; - ulong AddresseesTimes[20]; -}; - struct TParty { ulong Leader; struct vector Member; diff --git a/src/cr.hh b/src/cr.hh new file mode 100644 index 0000000..0d10cd3 --- /dev/null +++ b/src/cr.hh @@ -0,0 +1,619 @@ +#ifndef TIBIA_CREATURE_HH_ +#define TIBIA_CREATURE_HH_ 1 + +#include "common.hh" +#include "connection.hh" +#include "containers.hh" +#include "map.hh" + +struct TCreature; + +// TRaceData +// ============================================================================= +struct TOutfit{ + int OutfitID; + union{ + uint16 ObjectType; + uint8 Colors[4]; + }; +}; + +struct TSkillData { + int Nr; + int Actual; + int Minimum; + int Maximum; + int NextLevel; + int FactorPercent; + int AddLevel; +}; + +struct TItemData { + ObjectType Type; + int Maximum; + int Probability; +}; + +struct TSpellData { + SpellShapeType Shape; + int ShapeParam1; + int ShapeParam2; + int ShapeParam3; + int ShapeParam4; + SpellImpactType Impact; + int ImpactParam1; + int ImpactParam2; + int ImpactParam3; + int ImpactParam4; + int Delay; +}; + +struct TRaceData { + TRaceData(void); + + char Name[30]; + char Article[3]; + TOutfit Outfit; + ObjectType MaleCorpse; + ObjectType FemaleCorpse; + BloodType Blood; + int ExperiencePoints; + int FleeThreshold; + int Attack; + int Defend; + int Armor; + int Poison; + int SummonCost; + int LoseTarget; + int Strategy[4]; + bool KickBoxes; + bool KickCreatures; + bool SeeInvisible; + bool Unpushable; + bool DistanceFighting; + bool NoSummon; + bool NoIllusion; + bool NoConvince; + bool NoBurning; + bool NoPoison; + bool NoEnergy; + bool NoHit; + bool NoLifeDrain; + bool NoParalyze; + int Skills; + vector Skill; + int Talks; + vector Talk; // POINTER? Probably a reference from `AddDynamicString`? + int Items; + vector Item; + int Spells; + vector Spell; +}; + +// TSkillBase +// ============================================================================= +struct TSkill{ + // REGULAR FUNCTIONS + // ========================================================================= + TSkill(int SkNr, TCreature *Master); + int Get(void); + int GetProgress(void); + void Check(void); + void Change(int Amount); + void SetMDAct(int MDAct); + void Load(int Act, int Max, int Min, int DAct, int MDAct, + int Cycle, int MaxCycle, int Count, int MaxCount, int AddLevel, + int Exp, int FactorPercent, int NextLevel, int Delta); + void Save(int *Act, int *Max, int *Min, int *DAct, int *MDAct, + int *Cycle, int *MaxCycle, int *Count, int *MaxCount, int *AddLevel, + int *Exp, int *FactorPercent, int *NextLevel, int *Delta); + + // VIRTUAL FUNCTIONS + // ========================================================================= + virtual ~TSkill(void); // VTABLE[ 0] + // Duplicate destructor that also calls operator delete. // VTABLE[ 1] + virtual void Set(int Value); // VTABLE[ 2] + virtual void Increase(int Amount); // VTABLE[ 3] + virtual void Decrease(int Amount); // VTABLE[ 4] + virtual int GetExpForLevel(int Level); // VTABLE[ 5] + virtual void Advance(int Range); // VTABLE[ 6] + virtual void ChangeSkill(int FactorPercent, int Delta); // VTABLE[ 7] + virtual int ProbeValue(int Max, bool Increase); // VTABLE[ 8] + virtual bool Probe(int Diff, int Prob, bool Increase); // VTABLE[ 9] + virtual bool Process(void); // VTABLE[10] + virtual bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue); // VTABLE[11] + virtual bool DelTimer(void); // VTABLE[12] + virtual int TimerValue(void); // VTABLE[13] + virtual bool Jump(int Range); // VTABLE[14] + virtual void Event(int Range); // VTABLE[15] + virtual void Reset(void); // VTABLE[16] + + // DATA + // ========================================================================= + //void *VTABLE; // IMPLICIT + int DAct; // Delta Value - Probably from equipment. + int MDAct; // Delta Magic Value - Probably from spells. + uint16 SkNr; + TCreature *Master; + int Act; // Actual Value (?) + int Max; + int Min; + int FactorPercent; + int LastLevel; + int NextLevel; + int Delta; + int Exp; + int Cycle; + int MaxCycle; + int Count; + int MaxCount; + int AddLevel; +}; + +struct TSkillLevel: TSkill { + TSkillLevel(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Increase(int Amount) override; + void Decrease(int Amount) override; + int GetExpForLevel(int Level) override; + bool Jump(int Range) override; +}; + +struct TSkillProbe: TSkill { + TSkillProbe(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Increase(int Amount) override; + void Decrease(int Amount) override; + int GetExpForLevel(int Level) override; + void ChangeSkill(int FactorPercent, int Delta) override; + int ProbeValue(int Max, bool Increase) override; + bool Probe(int Diff, int Prob, bool Increase) override; + bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; + bool Jump(int Range) override; + void Event(int Range) override; +}; + +struct TSkillAdd: TSkill { + TSkillAdd(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Advance(int Range) override; +}; + +struct TSkillHitpoints: TSkillAdd { + TSkillHitpoints(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} + void Set(int Value) override; +}; + +struct TSkillMana: TSkillAdd { + TSkillMana(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} + void Set(int Value) override; +}; + +struct TSkillGoStrength: TSkillAdd { + TSkillGoStrength(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} + bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; + void Event(int Range) override; +}; + +struct TSkillCarryStrength: TSkillAdd { + TSkillCarryStrength(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} + void Set(int Value) override; +}; + +struct TSkillSoulpoints: TSkillAdd { + TSkillSoulpoints(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} + void Set(int Value) override; + int TimerValue(void) override; + void Event(int Range) override; +}; + +struct TSkillFed: TSkill { + TSkillFed(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Event(int Range) override; +}; + +struct TSkillLight: TSkill { + TSkillLight(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; + void Event(int Range) override; +}; + +struct TSkillIllusion: TSkill { + TSkillIllusion(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; + void Event(int Range) override; +}; + +struct TSkillPoison: TSkill { + TSkillPoison(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + bool Process(void) override; + bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; + void Event(int Range) override; + void Reset(void) override; +}; + +struct TSkillBurning: TSkill { + TSkillBurning(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Event(int Range) override; +}; + +struct TSkillEnergy: TSkill { + TSkillEnergy(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} + void Event(int Range) override; +}; + +struct TSkillBase{ + // REGULAR FUNCTIONS + // ========================================================================= + TSkillBase(void); + ~TSkillBase(void); + bool NewSkill(uint16 SkillNo, TCreature *Creature); + bool SetSkills(int Race); + void ProcessSkills(void); + bool SetTimer(uint16 SkNr, int Cycle, int Count, int MaxCount, int AdditionalValue); + void DelTimer(uint16 SkNr); + + // DATA + // ========================================================================= + TSkill *Skills[25]; + TSkill *TimerList[25]; + uint16 FirstFreeTimer; +}; + +// TCombat +// ============================================================================= +struct TCombatEntry{ + uint32 ID; + uint32 Damage; + uint32 TimeStamp; +}; + +struct TCombat{ + // REGULAR FUNCTIONS + // ========================================================================= + TCombat(void); + void GetWeapon(void); + void GetAmmo(void); + void CheckCombatValues(void); + void GetAttackValue(int *Value, int *SkillNr); + void GetDefendValue(int *Value, int *SkillNr); + int GetAttackDamage(void); + int GetDefendDamage(void); + int GetArmorStrength(void); + int GetDistance(void); + void ActivateLearning(void); + void SetAttackMode(uint8 AttackMode); + void SetChaseMode(uint8 ChaseMode); + void SetSecureMode(uint8 SecureMode); + void SetAttackDest(uint32 TargetID, bool Follow); + void CanToDoAttack(void); + void StopAttack(int Delay); + void DelayAttack(int Milliseconds); + void Attack(void); + void CloseAttack(TCreature *Target); + void DistanceAttack(TCreature *Target); + void WandAttack(TCreature *Target); + void AddDamageToCombatList(uint32 Attacker, uint32 Damage); + uint32 GetDamageByCreature(uint32 CreatureID); + uint32 GetMostDangerousAttacker(void); + void DistributeExperiencePoints(uint32 Exp); + + + // DATA + // ========================================================================= + TCreature *Master; + uint32 EarliestAttackTime; + uint32 EarliestDefendTime; + uint32 LastDefendTime; + uint32 LatestAttackTime; + uint8 AttackMode; + uint8 ChaseMode; + uint8 SecureMode; + uint32 AttackDest; + bool Following; + Object Shield; + Object Close; + Object Missile; + Object Throw; + Object Wand; + Object Ammo; + bool Fist; + uint32 CombatDamage; + int ActCombatEntry; + TCombatEntry CombatList[20]; + int LearningPoints; +}; + +// TCreature +// ============================================================================= + +struct TToDoEntry { + ToDoType Code; + union{ + struct{ + uint32 Time; + } Wait; + + struct{ + int x; + int y; + int z; + } Go; + + struct{ + int Direction; + } Rotate; + + struct{ + uint32 Obj; + int x; + int y; + int z; + int Count; + } Move; + + struct{ + uint32 Obj; + uint32 Partner; + } Trade; + + struct{ + uint32 Obj1; + uint32 Obj2; + int Dummy; + } Use; + + struct{ + uint32 Obj; + } Turn; + + struct{ + uint32 Text; // POINTER? Probably a reference from `AddDynamicString`? + int Mode; + uint32 Addressee; + bool CheckSpamming; + } Talk; + + struct{ + int NewState; + } ChangeState; + }; +}; + +struct TCreature: TSkillBase { + // REGULAR FUNCTIONS + // ========================================================================= + TCreature(void); + void Attack(void); + int Damage(TCreature *Attacker, int Damage, int DamageType); + void StartLogout(bool Force, bool StopFight); + void BlockLogout(int Delay, bool BlockProtectionZone); + void ToDoGo(int DestX, int DestY, int DestZ, bool Dest, int MaxSteps); + + // VIRTUAL FUNCTIONS + // ========================================================================= + virtual ~TCreature(void); // VTABLE[ 0] + // Duplicate destructor that also calls operator delete. // VTABLE[ 1] + virtual void Death(void); // VTABLE[ 2] + virtual bool MovePossible(int x, int y, int z, bool Execute, bool Jump); // VTABLE[ 3] + virtual bool IsPeaceful(void); // VTABLE[ 4] + virtual uint32 GetMaster(void); // VTABLE[ 5] + virtual void TalkStimulus(uint32 SpeakerID, const char *Text); // VTABLE[ 6] + virtual void DamageStimulus(uint32 AttackerID, int Damage, int DamageType); // VTABLE[ 7] + virtual void IdleStimulus(void); // VTABLE[ 8] + virtual void CreatureMoveStimulus(uint32 CreatureID, int Type); // VTABLE[ 9] + virtual void AttackStimulus(uint32 AttackerID); // VTABLE[10] + + // DATA + // ========================================================================= + //void *VTABLE; // IMPLICIT + //TSkillBase super_TSkillBase; // IMPLICIT + TCombat Combat; + uint32 ID; + TCreature *NextHashEntry; + uint32 NextChainCreature; + char Name[31]; + char Murderer[31]; + TOutfit OrgOutfit; + TOutfit Outfit; + int startx; + int starty; + int startz; + int posx; + int posy; + int posz; + int Sex; + int Race; + int Direction; + int Radius; + CreatureType Type; + bool IsDead; + int LoseInventory; + bool LoggingOut; + bool LogoutAllowed; + uint32 EarliestLogoutRound; + uint32 EarliestProtectionZoneRound; + uint32 EarliestYellRound; + uint32 EarliestTradeChannelRound; + uint32 EarliestSpellTime; + uint32 EarliestMultiuseTime; + uint32 EarliestWalkTime; + uint32 LifeEndRound; + TKnownCreature *FirstKnowingConnection; + int SummonedCreatures; + uint32 FireDamageOrigin; + uint32 PoisonDamageOrigin; + uint32 EnergyDamageOrigin; + Object CrObject; + vector ToDoList; + int ActToDo; + int NrToDo; + uint32 NextWakeup; + bool Stop; + bool LockToDo; + uint8 Profession; + TConnection *Connection; +}; + +// TNonPlayer +// ============================================================================= + +// TNPC +// ============================================================================= + +// TMonster +// ============================================================================= +#if 0 +struct TMonster: TNonplayer { + + // DATA + int Home; + uint32 Master; + uint32 Target; +}; +#endif + +// TPlayer +// ============================================================================= +struct TPlayerData { + uint32 CharacterID; + pid_t Locked; + int Sticky; + bool Dirty; + int Race; + TOutfit OriginalOutfit; + TOutfit CurrentOutfit; + time_t LastLoginTime; + time_t LastLogoutTime; + int startx; + int starty; + int startz; + int posx; + int posy; + int posz; + int Profession; + int PlayerkillerEnd; + int Actual[25]; + int Maximum[25]; + int Minimum[25]; + int DeltaAct[25]; + int MagicDeltaAct[25]; + int Cycle[25]; + int MaxCycle[25]; + int Count[25]; + int MaxCount[25]; + int AddLevel[25]; + int Experience[25]; + int FactorPercent[25]; + int NextLevel[25]; + int Delta[25]; + uint8 SpellList[256]; + int QuestValues[500]; + int MurderTimestamps[20]; + uint8 *Inventory; + int InventorySize; + uint8 *Depot[9]; + int DepotSize[9]; + uint32 AccountID; + int Sex; + char Name[30]; + uint8 Rights[12]; + char Guild[31]; + char Rank[31]; + char Title[31]; + int Buddies; + uint32 Buddy[100]; + char BuddyName[100][30]; + uint32 EarliestYellRound; + uint32 EarliestTradeChannelRound; + uint32 EarliestSpellTime; + uint32 EarliestMultiuseTime; + uint32 TalkBufferFullTime; + uint32 MutingEndRound; + uint32 Addressees[20]; + uint32 AddresseesTimes[20]; + int NumberOfMutings; +}; + +struct TPlayer: TCreature { + // REGULAR FUNCTIONS + // ========================================================================= + uint8 GetRealProfession(void); + uint8 GetEffectiveProfession(void); + uint8 GetActiveProfession(void); + bool GetActivePromotion(void); + void ClearProfession(void); + void SetProfession(uint8 Profession); + + int GetQuestValue(int Number); + void SetQuestValue(int Number, int Value); + + uint32 GetPartyLeader(bool CheckFormer); + + bool SpellKnown(int SpellNr); + + bool IsAttackJustified(uint32 Victim); + void RecordAttack(uint32 Victim); + void RecordMurder(uint32 Victim); + + void CheckState(void); + + // VIRTUAL FUNCTIONS + // ========================================================================= + // TODO + + // DATA + // ========================================================================= + //TCreature super_TCreature; // IMPLICIT + uint32 AccountID; + char Guild[31]; + char Rank[31]; + char Title[31]; + char IPAddress[16]; + uint8 Rights[12]; + Object Depot; + int DepotNr; + int DepotSpace; + RESULT ConstructError; + TPlayerData *PlayerData; + Object TradeObject; + uint32 TradePartner; + bool TradeAccepted; + int OldState; + uint32 Request; + int RequestTimestamp; + uint32 RequestProcessingGamemaster; + int TutorActivities; + uint8 SpellList[256]; + int QuestValues[500]; + Object OpenContainer[16]; + vector AttackedPlayers; + int NumberOfAttackedPlayers; + bool Aggressor; + vector FormerAttackedPlayers; + int NumberOfFormerAttackedPlayers; + bool FormerAggressor; + uint32 FormerLogoutRound; + uint32 PartyLeader; + uint32 PartyLeavingRound; + uint32 TalkBufferFullTime; + uint32 MutingEndRound; + int NumberOfMutings; + uint32 Addressees[20]; + uint32 AddresseesTimes[20]; +}; + +// Creature API +// ============================================================================= + +#define MAX_RACES 512 + +// crmain.cc +extern TRaceData RaceData[MAX_RACES]; +extern int KilledCreatures[MAX_RACES]; +extern int KilledPlayers[MAX_RACES]; + +// TODO(fusion): These probably belong elsewhere but we should come back to +// this when we're wrapping up creature files. +bool IsCreaturePlayer(uint32 CreatureID); +void AddKillStatistics(int AttackerRace, int DefenderRace); +int GetRaceByName(const char *RaceName); + +#endif //TIBIA_CREATURE_HH_ diff --git a/src/crcombat.cc b/src/crcombat.cc index e1bf72f..9edfe9b 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -1,6 +1,4 @@ -#include "creature.hh" -#include "monster.hh" -#include "player.hh" +#include "cr.hh" #include "config.hh" #include "magic.hh" diff --git a/src/crcombat.hh b/src/crcombat.hh deleted file mode 100644 index b5db3c3..0000000 --- a/src/crcombat.hh +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef TIBIA_CRCOMBAT_HH_ -#define TIBIA_CRCOMBAT_HH_ 1 - -struct TCreature; - -struct TCombatEntry{ - uint32 ID; - uint32 Damage; - uint32 TimeStamp; -}; - -struct TCombat{ - // REGULAR FUNCTIONS - // ========================================================================= - TCombat(void); - void GetWeapon(void); - void GetAmmo(void); - void CheckCombatValues(void); - void GetAttackValue(int *Value, int *SkillNr); - void GetDefendValue(int *Value, int *SkillNr); - int GetAttackDamage(void); - int GetDefendDamage(void); - int GetArmorStrength(void); - int GetDistance(void); - void ActivateLearning(void); - void SetAttackMode(uint8 AttackMode); - void SetChaseMode(uint8 ChaseMode); - void SetSecureMode(uint8 SecureMode); - void SetAttackDest(uint32 TargetID, bool Follow); - void CanToDoAttack(void); - void StopAttack(int Delay); - void DelayAttack(int Milliseconds); - void Attack(void); - void CloseAttack(TCreature *Target); - void DistanceAttack(TCreature *Target); - void WandAttack(TCreature *Target); - void AddDamageToCombatList(uint32 Attacker, uint32 Damage); - uint32 GetDamageByCreature(uint32 CreatureID); - uint32 GetMostDangerousAttacker(void); - void DistributeExperiencePoints(uint32 Exp); - - - // DATA - // ========================================================================= - TCreature *Master; - uint32 EarliestAttackTime; - uint32 EarliestDefendTime; - uint32 LastDefendTime; - uint32 LatestAttackTime; - uint8 AttackMode; - uint8 ChaseMode; - uint8 SecureMode; - uint32 AttackDest; - bool Following; - Object Shield; - Object Close; - Object Missile; - Object Throw; - Object Wand; - Object Ammo; - bool Fist; - uint32 CombatDamage; - int ActCombatEntry; - TCombatEntry CombatList[20]; - int LearningPoints; -}; - -#endif //TIBIA_CRCOMBAT_HH_ diff --git a/src/creature.cc b/src/creature.cc deleted file mode 100644 index 9eadc6e..0000000 --- a/src/creature.cc +++ /dev/null @@ -1,488 +0,0 @@ -#include "creature.hh" -#include "monster.hh" -#include "enums.hh" - -#include "stubs.hh" - -TCreature::TCreature(void) : - TSkillBase(), - Combat(), - ToDoList(0, 20, 10) -{ - this->Combat.Master = this; - this->ID = 0; - this->NextHashEntry = NULL; - this->NextChainCreature = 0; - this->Murderer[0] = 0; - this->startx = 0; - this->starty = 0; - this->startz = 0; - this->posx = 0; - this->posy = 0; - this->posz = 0; - this->Direction = 0; - this->Radius = INT_MAX; - this->IsDead = false; - this->LoseInventory = 2; - this->LoggingOut = false; - this->LogoutAllowed = false; - this->EarliestLogoutRound = 0; - this->EarliestProtectionZoneRound = 0; - this->EarliestYellRound = 0; - this->EarliestTradeChannelRound = 0; - this->EarliestSpellTime = 0; - this->EarliestMultiuseTime = 0; - this->EarliestWalkTime = 0; - this->LifeEndRound = 0; - this->FirstKnowingConnection = NULL; - this->SummonedCreatures = 0; - this->FireDamageOrigin = 0; - this->PoisonDamageOrigin = 0; - this->EnergyDamageOrigin = 0; - this->CrObject = NONE; - this->ActToDo = 0; - this->NrToDo = 0; - this->NextWakeup = 0; - this->Stop = false; - this->LockToDo = false; - this->Connection = NULL; - - for(int i = 0; i < NARRAY(this->Skills); i += 1){ - this->NewSkill((uint16)i, this); - } -} - -void TCreature::Attack(void){ - this->Combat.Attack(); -} - -// Temporary Location -// ============================================================================= -// ============================================================================= -// TODO(fusion): These probably belong elsewhere but we should come back to -// this when we're wrapping up creature files. - -bool IsCreaturePlayer(uint32 CreatureID){ - return CreatureID < 0x40000000; -} - -void AddKillStatistics(int AttackerRace, int DefenderRace){ - // NOTE(fusion): I think the race name can be "human" only for players, - // which means we're probably tracking how many creatures are killed by - // players with `KilledCreatures`, and how many players are killed by - // creatures with `KilledPlayers`. - - if(strcmp(RaceData[AttackerRace].Name, "human") == 0){ - KilledCreatures[DefenderRace] += 1; - } - - // NOTE(fusion): This seems to track how many players are killed by - if(strcmp(RaceData[DefenderRace].Name, "human") == 0){ - KilledPlayers[AttackerRace] += 1; - } -} - -int GetRaceByName(const char *RaceName){ - int Result = 0; - for(int Race = 1; Race < NARRAY(RaceData); Race += 1){ - if(stricmp(RaceName, RaceData[Race].Name) == 0){ - Result = Race; - break; - } - } - return Result; -} - -// ============================================================================= -// ============================================================================= - -int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){ - if(this->IsDead || this->Type == NPC){ - return 0; - } - - if(this->Type == PLAYER){ - if(this->Connection != NULL && Attacker != NULL){ - SendMarkCreature(this->Connection, Attacker->ID, COLOR_BLACK); - } - - if(Attacker != NULL && Attacker->Type == PLAYER - && DamageType != DAMAGE_POISON_PERIODIC - && DamageType != DAMAGE_FIRE_PERIODIC - && DamageType != DAMAGE_ENERGY_PERIODIC){ - Damage = (Damage + 1) / 2; - } - } - - // NOTE(fusion): `Responsible` is either the attacker or its master. It is - // always valid if `Attacker` is not NULL. - TCreature *Responsible = Attacker; - if(Attacker != NULL){ - uint32 MasterID = Attacker->GetMaster(); - if(MasterID != 0){ - Responsible = GetCreature(MasterID); - } - - Attacker->BlockLogout(60, this->Type == PLAYER); - if(Responsible != Attacker){ - Responsible->BlockLogout(60, this->Type == PLAYER); - } - - if(this->Type == PLAYER && Responsible->Type == PLAYER){ - ((TPlayer*)Responsible)->RecordAttack(this->ID); - } - } - - if(this->Type == PLAYER){ - if(CheckRight(this->ID, INVULNERABLE)){ - Damage = 0; - } - - // NOTE(fusion): We're iterating over the victim's inventory to apply - // damage reductions, while the damage is greater than zero. - for(int Position = 1; - Position <= 10 && Damage > 0; - Position += 1){ - Object Obj = GetBodyObject(this->ID, Position); - if(Obj == NONE){ - continue; - } - - ObjectType ObjType = Obj.getObjectType(); - if(ObjType.getFlag(PROTECTION) && ObjType.getFlag(CLOTHES) - && (int)ObjType.getAttribute(BODYPOSITION) == Position - && (ObjType.getAttribute(PROTECTIONDAMAGETYPES) & DamageType) != 0){ - int DamageReduction = ObjType.getAttribute(DAMAGEREDUCTION); - Damage = (Damage * (100 - DamageReduction)) / 100; - if(ObjType.getFlag(WEAROUT)){ - // TODO(fusion): Ugh... The try..catch block might be used only - // when changing the object's type. - try{ - uint32 RemainingUses = Obj.getAttribute(REMAININGUSES); - if(RemainingUses > 1){ - Change(Obj, REMAININGUSES, RemainingUses - 1); - }else{ - ObjectType WearOutType = ObjType.getAttribute(WEAROUTTARGET); - Change(Obj, WearOutType, 0); - } - }catch(RESULT r){ - error("TCreature::Damage: Exception %d beim Abnutzen von Objekt %d.\n", - r, ObjType.TypeID); - } - } - }else if(ObjType.getFlag(PROTECTION) && !ObjType.getFlag(CLOTHES)){ - error("TCreature::Damage: Objekt %d hat PROTECTION, aber nicht CLOTHES.\n", - ObjType.TypeID); - } - } - } - - if(Damage <= 0){ - GraphicalEffect(this->CrObject, EFFECT_POFF); - return 0; - } - - if(DamageType == DAMAGE_POISON_PERIODIC){ - if(RaceData[this->Race].NoPoison){ - return 0; - } - - if(Damage > this->Skills[SKILL_POISON]->TimerValue()){ - this->PoisonDamageOrigin = Attacker->ID; - this->SetTimer(SKILL_POISON, Damage, 3, 3, -1); - } - - this->DamageStimulus(Attacker->ID, Damage, DamageType); - return Damage; - }else if(DamageType == DAMAGE_FIRE_PERIODIC){ - if(RaceData[this->Race].NoBurning){ - return 0; - } - - this->FireDamageOrigin = Attacker->ID; - this->SetTimer(SKILL_BURNING, Damage / 10, 8, 8, -1); - this->DamageStimulus(Attacker->ID, Damage, DamageType); - return Damage; - }else if(DamageType == DAMAGE_ENERGY_PERIODIC){ - if(RaceData[this->Race].NoEnergy){ - return 0; - } - - // TODO(fusion): Shouldn't we use `Damage / 25` here? - this->EnergyDamageOrigin = Attacker->ID; - this->SetTimer(SKILL_ENERGY, Damage / 20, 10, 10, -1); - this->DamageStimulus(Attacker->ID, Damage, DamageType); - return Damage; - } - - if((DamageType == DAMAGE_PHYSICAL && RaceData[this->Race].NoHit) - || (DamageType == DAMAGE_POISON && RaceData[this->Race].NoPoison) - || (DamageType == DAMAGE_FIRE && RaceData[this->Race].NoBurning) - || (DamageType == DAMAGE_ENERGY && RaceData[this->Race].NoEnergy) - || (DamageType == DAMAGE_LIFEDRAIN && RaceData[this->Race].NoLifeDrain)){ - GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT); - return 0; - } - - if(DamageType == DAMAGE_PHYSICAL){ - Damage -= this->Combat.GetArmorStrength(); - if(Damage <= 0){ - GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT); - return 0; - } - } - - this->DamageStimulus(Attacker->ID, Damage, DamageType); - - // NOTE(fusion): Remove non-player illusion. Might as well be an inlined - // function. - if(this->Type != PLAYER - && this->Outfit.OutfitID == 0 - && this->Outfit.ObjectType == 0){ - this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); - this->Outfit = this->OrgOutfit; - AnnounceChangedCreature(this->ID, 3); // CREATURE_OUTFIT_CHANGED ? - NotifyAllCreatures(this->CrObject, 2, NONE); // CREATURE_APPEAR ? - } - - if(DamageType == DAMAGE_MANADRAIN){ - int ManaPoints = this->Skills[SKILL_MANA]->Get(); - if(Damage > ManaPoints){ - Damage = ManaPoints; - } - - if(Damage > 0){ - this->Skills[SKILL_MANA]->Change(-Damage); - if(this->Type == PLAYER && this->Connection != NULL){ - SendMessage(this->Connection, TALK_STATUS_MESSAGE, - "You lose %d mana.", Damage); - } - GraphicalEffect(this->CrObject, EFFECT_MAGIC_RED); - TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage); - } - - return Damage; - } - - if(this->Skills[SKILL_MANASHIELD]->TimerValue() > 0 - || this->Skills[SKILL_MANASHIELD]->Get() > 0){ - // NOTE(fusion): We only send these if the attack was fully absorbed, - // else it'd be overwritten by whatever effect and messages we send - // next, when the victim's hitpoints are actually touched. - int ManaPoints = this->Skills[SKILL_MANA]->Get(); - if(Damage <= ManaPoints){ - this->Skills[SKILL_MANA]->Change(-Damage); - GraphicalEffect(this->CrObject, EFFECT_MANA_HIT); - TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage); - if(this->Type == PLAYER && this->Connection != NULL){ - if(Attacker != NULL){ - SendMessage(this->Connection, TALK_STATUS_MESSAGE, - "You lose %d mana blocking an attack by %s.", - Damage, Attacker->Name); - }else{ - SendMessage(this->Connection, TALK_STATUS_MESSAGE, - "You lose %d mana.", Damage); - } - SendPlayerData(this->Connection); - } - return Damage; - } - - this->Skills[SKILL_MANA]->Set(0); - Damage -= ManaPoints; - } - - int HitPoints = this->Skills[SKILL_HITPOINTS]->Get(); - if(Damage > HitPoints){ - Damage = HitPoints; - } - - this->Skills[SKILL_HITPOINTS]->Change(-Damage); - if(Responsible != NULL){ - ASSERT(Attacker != NULL); - if(Responsible == Attacker){ - this->Combat.AddDamageToCombatList(Attacker->ID, Damage); - }else{ - this->Combat.AddDamageToCombatList(Attacker->ID, Damage / 2); - this->Combat.AddDamageToCombatList(Responsible->ID, Damage / 2); - } - } - - int HitEffect = EFFECT_NONE; - int TextColor = COLOR_BLACK; - int SplashLiquid = LIQUID_NONE; - if(DamageType == DAMAGE_PHYSICAL){ - switch(RaceData[this->Race].Blood){ - case BT_BLOOD:{ - HitEffect = EFFECT_BLOOD_HIT; - TextColor = COLOR_RED; - SplashLiquid = LIQUID_BLOOD; - break; - } - case BT_SLIME:{ - HitEffect = EFFECT_POISON_HIT; - TextColor = COLOR_LIGHTGREEN; - SplashLiquid = LIQUID_SLIME; - break; - } - case BT_BONES:{ - HitEffect = EFFECT_BONE_HIT; - TextColor = COLOR_LIGHTGRAY; - break; - } - case BT_FIRE:{ - HitEffect = EFFECT_FIRE_HIT; - TextColor = COLOR_ORANGE; - break; - } - case BT_ENERGY:{ - HitEffect = EFFECT_ENERGY_HIT; - TextColor = COLOR_LIGHTBLUE; - break; - } - default:{ - error("TCreature::Damage: Ungültiger Bluttyp %d für Rasse %d.\n", - RaceData[this->Race].Blood, this->Race); - break; - } - } - }else if(Damage == DAMAGE_POISON){ - HitEffect = EFFECT_POISON; - TextColor = COLOR_LIGHTGREEN; - }else if(DamageType == DAMAGE_FIRE){ - HitEffect = EFFECT_FIRE_HIT; - TextColor = COLOR_ORANGE; - }else if(DamageType == DAMAGE_ENERGY){ - HitEffect = EFFECT_ENERGY_HIT; - TextColor = COLOR_LIGHTBLUE; - }else if(DamageType == DAMAGE_LIFEDRAIN){ - HitEffect = EFFECT_MAGIC_RED; - TextColor = COLOR_RED; - }else{ - // TODO(fusion): The original decompiled function would return here but - // I don't think it's a good idea because it would skip death handling. - error("TCreature::Damage: Ungültiger Schadenstyp %d.\n", DamageType); - //return Damage; - } - - if(HitEffect != EFFECT_NONE){ - GraphicalEffect(this->CrObject, HitEffect); - TextualEffect(this->CrObject, TextColor, "%d", Damage); - if(SplashLiquid != LIQUID_NONE){ - CreatePool(GetMapContainer(this->CrObject), - GetSpecialObject(BLOOD_SPLASH), - SplashLiquid); - } - } - - if(this->Type == PLAYER && this->Connection != NULL){ - if(Attacker != NULL){ - SendMessage(this->Connection, TALK_STATUS_MESSAGE, - "You lose %d hitpoint%s due to an attack by %s.", - Damage, (Damage == 1 ? "" : "s"), Attacker->Name); - }else{ - SendMessage(this->Connection, TALK_STATUS_MESSAGE, - "You lose %d hitpoint%s.", - Damage, (Damage == 1 ? "" : "s")); - } - SendPlayerData(this->Connection); - } - - if(Damage == HitPoints){ - if(this->Type == PLAYER){ - for(int Position = 1; Position <= 10; Position += 1){ - Object Obj = GetBodyObject(this->ID, Position); - if(Obj == NONE){ - continue; - } - - ObjectType ObjType = Obj.getObjectType(); - if(ObjType.getFlag(CLOTHES) && (int)ObjType.getAttribute(BODYPOSITION) == Position){ - ObjectType AmuletOfLossType = GetNewObjectType(77, 12); - if(ObjType == AmuletOfLossType){ - Log("game", "%s stirbt mit Amulett of Loss.\n", this->Name); - this->LoseInventory = 0; - Delete(Obj, -1); - // TODO(fusion): Shouldn't we break here? We could also - // just check if there is an amulet of loss in the necklace - // container instead of iterating over all of them. - } - } - } - } - - int OldLevel = this->Skills[SKILL_LEVEL]->Get(); - this->Death(); - if(Attacker != NULL && this->Type == PLAYER){ - Attacker->BlockLogout(900, true); - if(Responsible != Attacker){ - Responsible->BlockLogout(900, true); - } - } - - uint32 MurdererID = 0; - char Remark[30] = {}; - if(Attacker == NULL){ - AddKillStatistics(0, this->Race); - if(DamageType == DAMAGE_PHYSICAL){ - strcpy(Remark, "a hit"); - }else if(DamageType == DAMAGE_POISON){ - strcpy(Remark, "poison"); - }else if(DamageType == DAMAGE_FIRE){ - strcpy(Remark, "fire"); - }else if(DamageType == DAMAGE_ENERGY){ - strcpy(Remark, "energy"); - }else{ - // NOTE(fusion): We probably don't expect any other damage type - // as the cause of death when there is no attacker. - error("TCreature::Damage: Ungültiger Schadenstyp %d als Todesursache.\n", DamageType); - } - }else{ - AddKillStatistics(Attacker->Race, this->Race); - strcpy(this->Murderer, Attacker->Name); - - if(Responsible->Type == PLAYER){ - MurdererID = Responsible->ID; - } - - if(Attacker->Type != PLAYER){ - strcpy(Remark, Attacker->Name); - } - } - - if(this->Type == PLAYER){ - if(MurdererID != 0 && MurdererID != this->ID){ - bool Justified = true; - if(IsCreaturePlayer(MurdererID)){ - TPlayer *Murderer = GetPlayer(MurdererID); - if(Murderer != NULL){ - Justified = Murderer->IsAttackJustified(this->ID); - Murderer->RecordMurder(this->ID); - } - } - CharacterDeathOrder(this, OldLevel, MurdererID, Remark, !Justified); - } - - uint32 MostDangerousID = this->Combat.GetMostDangerousAttacker(); - if(MostDangerousID != 0 - && MostDangerousID != MurdererID - && MostDangerousID != this->ID - && IsCreaturePlayer(MostDangerousID)){ - bool Justified = true; - TPlayer *MostDangerous = GetPlayer(MostDangerousID); - if(MostDangerous != NULL){ - Justified = MostDangerous->IsAttackJustified(this->ID); - MostDangerous->RecordMurder(this->ID); - } - - // TODO(fusion): The original function is confusing at this point - // but it seems correct that the remark is included only with the - // murderer. - CharacterDeathOrder(this, OldLevel, MostDangerousID, "", !Justified); - } - } - } - - AnnounceChangedCreature(this->ID, 1); // CREATURE_HITPOINTS_CHANGED ? - return Damage; -} diff --git a/src/creature.hh b/src/creature.hh deleted file mode 100644 index cfa0293..0000000 --- a/src/creature.hh +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef TIBIA_CREATURE_HH_ -#define TIBIA_CREATURE_HH_ 1 - -#include "common.hh" -#include "connection.hh" -#include "containers.hh" -#include "map.hh" - -#include "crcombat.hh" -#include "crskill.hh" - -struct TOutfit{ - int OutfitID; - union{ - uint16 ObjectType; - uint8 Colors[4]; - }; -}; - -struct TToDoEntry { - ToDoType Code; - union{ - struct{ - uint32 Time; - } Wait; - - struct{ - int x; - int y; - int z; - } Go; - - struct{ - int Direction; - } Rotate; - - struct{ - uint32 Obj; - int x; - int y; - int z; - int Count; - } Move; - - struct{ - uint32 Obj; - uint32 Partner; - } Trade; - - struct{ - uint32 Obj1; - uint32 Obj2; - int Dummy; - } Use; - - struct{ - uint32 Obj; - } Turn; - - struct{ - uint32 Text; // POINTER? Probably a reference from `AddDynamicString`? - int Mode; - uint32 Addressee; - bool CheckSpamming; - } Talk; - - struct{ - int NewState; - } ChangeState; - }; -}; - -struct TCreature: TSkillBase { - // REGULAR FUNCTIONS - // ========================================================================= - TCreature(void); - void Attack(void); - int Damage(TCreature *Attacker, int Damage, int DamageType); - void StartLogout(bool Force, bool StopFight); - void BlockLogout(int Delay, bool BlockProtectionZone); - void ToDoGo(int DestX, int DestY, int DestZ, bool Dest, int MaxSteps); - - // VIRTUAL FUNCTIONS - // ========================================================================= - virtual ~TCreature(void); // VTABLE[ 0] - // Duplicate destructor that also calls operator delete. // VTABLE[ 1] - virtual void Death(void); // VTABLE[ 2] - virtual bool MovePossible(int x, int y, int z, bool Execute, bool Jump); // VTABLE[ 3] - virtual bool IsPeaceful(void); // VTABLE[ 4] - virtual uint32 GetMaster(void); // VTABLE[ 5] - virtual void TalkStimulus(uint32 SpeakerID, const char *Text); // VTABLE[ 6] - virtual void DamageStimulus(uint32 AttackerID, int Damage, int DamageType); // VTABLE[ 7] - virtual void IdleStimulus(void); // VTABLE[ 8] - virtual void CreatureMoveStimulus(uint32 CreatureID, int Type); // VTABLE[ 9] - virtual void AttackStimulus(uint32 AttackerID); // VTABLE[10] - - // DATA - // ========================================================================= - //void *VTABLE; // IMPLICIT - //TSkillBase super_TSkillBase; // IMPLICIT - TCombat Combat; - uint32 ID; - TCreature *NextHashEntry; - uint32 NextChainCreature; - char Name[31]; - char Murderer[31]; - TOutfit OrgOutfit; - TOutfit Outfit; - int startx; - int starty; - int startz; - int posx; - int posy; - int posz; - int Sex; - int Race; - int Direction; - int Radius; - CreatureType Type; - bool IsDead; - int LoseInventory; - bool LoggingOut; - bool LogoutAllowed; - uint32 EarliestLogoutRound; - uint32 EarliestProtectionZoneRound; - uint32 EarliestYellRound; - uint32 EarliestTradeChannelRound; - uint32 EarliestSpellTime; - uint32 EarliestMultiuseTime; - uint32 EarliestWalkTime; - uint32 LifeEndRound; - TKnownCreature *FirstKnowingConnection; - int SummonedCreatures; - uint32 FireDamageOrigin; - uint32 PoisonDamageOrigin; - uint32 EnergyDamageOrigin; - Object CrObject; - vector ToDoList; - int ActToDo; - int NrToDo; - uint32 NextWakeup; - bool Stop; - bool LockToDo; - uint8 Profession; - TConnection *Connection; -}; - -// TODO(fusion): These probably belong elsewhere but we should come back to -// this when we're wrapping up creature files. -bool IsCreaturePlayer(uint32 CreatureID); -void AddKillStatistics(int AttackerRace, int DefenderRace); -int GetRaceByName(const char *RaceName); - -#endif //TIBIA_CREATURE_HH_ diff --git a/src/crmain.cc b/src/crmain.cc new file mode 100644 index 0000000..31d93fb --- /dev/null +++ b/src/crmain.cc @@ -0,0 +1,589 @@ +#include "cr.hh" +#include "enums.hh" + +#include "stubs.hh" + +static uint32 NextCreatureID; +static matrix *FirstChainCreature; +static TCreature *HashList[1000]; + +static int FirstFreeCreature; +static vector CreatureList(0, 10000, 1000); + +static priority_queue ToDoQueue(5000, 1000); +//static priority_queue AttackWaveQueue(100, 100); + +TRaceData RaceData[MAX_RACES]; +int KilledCreatures[MAX_RACES]; +int KilledPlayers[MAX_RACES]; + +// TCreature +// ============================================================================= +TCreature::TCreature(void) : + TSkillBase(), + Combat(), + ToDoList(0, 20, 10) +{ + this->Combat.Master = this; + this->ID = 0; + this->NextHashEntry = NULL; + this->NextChainCreature = 0; + this->Murderer[0] = 0; + this->startx = 0; + this->starty = 0; + this->startz = 0; + this->posx = 0; + this->posy = 0; + this->posz = 0; + this->Direction = 0; + this->Radius = INT_MAX; + this->IsDead = false; + this->LoseInventory = 2; + this->LoggingOut = false; + this->LogoutAllowed = false; + this->EarliestLogoutRound = 0; + this->EarliestProtectionZoneRound = 0; + this->EarliestYellRound = 0; + this->EarliestTradeChannelRound = 0; + this->EarliestSpellTime = 0; + this->EarliestMultiuseTime = 0; + this->EarliestWalkTime = 0; + this->LifeEndRound = 0; + this->FirstKnowingConnection = NULL; + this->SummonedCreatures = 0; + this->FireDamageOrigin = 0; + this->PoisonDamageOrigin = 0; + this->EnergyDamageOrigin = 0; + this->CrObject = NONE; + this->ActToDo = 0; + this->NrToDo = 0; + this->NextWakeup = 0; + this->Stop = false; + this->LockToDo = false; + this->Connection = NULL; + + for(int i = 0; i < NARRAY(this->Skills); i += 1){ + this->NewSkill((uint16)i, this); + } +} + +void TCreature::Attack(void){ + this->Combat.Attack(); +} + +int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){ + if(this->IsDead || this->Type == NPC){ + return 0; + } + + if(this->Type == PLAYER){ + if(this->Connection != NULL && Attacker != NULL){ + SendMarkCreature(this->Connection, Attacker->ID, COLOR_BLACK); + } + + if(Attacker != NULL && Attacker->Type == PLAYER + && DamageType != DAMAGE_POISON_PERIODIC + && DamageType != DAMAGE_FIRE_PERIODIC + && DamageType != DAMAGE_ENERGY_PERIODIC){ + Damage = (Damage + 1) / 2; + } + } + + // NOTE(fusion): `Responsible` is either the attacker or its master. It is + // always valid if `Attacker` is not NULL. + TCreature *Responsible = Attacker; + if(Attacker != NULL){ + uint32 MasterID = Attacker->GetMaster(); + if(MasterID != 0){ + Responsible = GetCreature(MasterID); + } + + Attacker->BlockLogout(60, this->Type == PLAYER); + if(Responsible != Attacker){ + Responsible->BlockLogout(60, this->Type == PLAYER); + } + + if(this->Type == PLAYER && Responsible->Type == PLAYER){ + ((TPlayer*)Responsible)->RecordAttack(this->ID); + } + } + + if(this->Type == PLAYER){ + if(CheckRight(this->ID, INVULNERABLE)){ + Damage = 0; + } + + // NOTE(fusion): We're iterating over the victim's inventory to apply + // damage reductions, while the damage is greater than zero. + for(int Position = 1; + Position <= 10 && Damage > 0; + Position += 1){ + Object Obj = GetBodyObject(this->ID, Position); + if(Obj == NONE){ + continue; + } + + ObjectType ObjType = Obj.getObjectType(); + if(ObjType.getFlag(PROTECTION) && ObjType.getFlag(CLOTHES) + && (int)ObjType.getAttribute(BODYPOSITION) == Position + && (ObjType.getAttribute(PROTECTIONDAMAGETYPES) & DamageType) != 0){ + int DamageReduction = ObjType.getAttribute(DAMAGEREDUCTION); + Damage = (Damage * (100 - DamageReduction)) / 100; + if(ObjType.getFlag(WEAROUT)){ + // TODO(fusion): Ugh... The try..catch block might be used only + // when changing the object's type. + try{ + uint32 RemainingUses = Obj.getAttribute(REMAININGUSES); + if(RemainingUses > 1){ + Change(Obj, REMAININGUSES, RemainingUses - 1); + }else{ + ObjectType WearOutType = ObjType.getAttribute(WEAROUTTARGET); + Change(Obj, WearOutType, 0); + } + }catch(RESULT r){ + error("TCreature::Damage: Exception %d beim Abnutzen von Objekt %d.\n", + r, ObjType.TypeID); + } + } + }else if(ObjType.getFlag(PROTECTION) && !ObjType.getFlag(CLOTHES)){ + error("TCreature::Damage: Objekt %d hat PROTECTION, aber nicht CLOTHES.\n", + ObjType.TypeID); + } + } + } + + if(Damage <= 0){ + GraphicalEffect(this->CrObject, EFFECT_POFF); + return 0; + } + + if(DamageType == DAMAGE_POISON_PERIODIC){ + if(RaceData[this->Race].NoPoison){ + return 0; + } + + if(Damage > this->Skills[SKILL_POISON]->TimerValue()){ + this->PoisonDamageOrigin = Attacker->ID; + this->SetTimer(SKILL_POISON, Damage, 3, 3, -1); + } + + this->DamageStimulus(Attacker->ID, Damage, DamageType); + return Damage; + }else if(DamageType == DAMAGE_FIRE_PERIODIC){ + if(RaceData[this->Race].NoBurning){ + return 0; + } + + this->FireDamageOrigin = Attacker->ID; + this->SetTimer(SKILL_BURNING, Damage / 10, 8, 8, -1); + this->DamageStimulus(Attacker->ID, Damage, DamageType); + return Damage; + }else if(DamageType == DAMAGE_ENERGY_PERIODIC){ + if(RaceData[this->Race].NoEnergy){ + return 0; + } + + // TODO(fusion): Shouldn't we use `Damage / 25` here? + this->EnergyDamageOrigin = Attacker->ID; + this->SetTimer(SKILL_ENERGY, Damage / 20, 10, 10, -1); + this->DamageStimulus(Attacker->ID, Damage, DamageType); + return Damage; + } + + if((DamageType == DAMAGE_PHYSICAL && RaceData[this->Race].NoHit) + || (DamageType == DAMAGE_POISON && RaceData[this->Race].NoPoison) + || (DamageType == DAMAGE_FIRE && RaceData[this->Race].NoBurning) + || (DamageType == DAMAGE_ENERGY && RaceData[this->Race].NoEnergy) + || (DamageType == DAMAGE_LIFEDRAIN && RaceData[this->Race].NoLifeDrain)){ + GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT); + return 0; + } + + if(DamageType == DAMAGE_PHYSICAL){ + Damage -= this->Combat.GetArmorStrength(); + if(Damage <= 0){ + GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT); + return 0; + } + } + + this->DamageStimulus(Attacker->ID, Damage, DamageType); + + // NOTE(fusion): Remove non-player illusion. Might as well be an inlined + // function. + if(this->Type != PLAYER + && this->Outfit.OutfitID == 0 + && this->Outfit.ObjectType == 0){ + this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); + this->Outfit = this->OrgOutfit; + AnnounceChangedCreature(this->ID, 3); // CREATURE_OUTFIT_CHANGED ? + NotifyAllCreatures(this->CrObject, 2, NONE); // CREATURE_APPEAR ? + } + + if(DamageType == DAMAGE_MANADRAIN){ + int ManaPoints = this->Skills[SKILL_MANA]->Get(); + if(Damage > ManaPoints){ + Damage = ManaPoints; + } + + if(Damage > 0){ + this->Skills[SKILL_MANA]->Change(-Damage); + if(this->Type == PLAYER && this->Connection != NULL){ + SendMessage(this->Connection, TALK_STATUS_MESSAGE, + "You lose %d mana.", Damage); + } + GraphicalEffect(this->CrObject, EFFECT_MAGIC_RED); + TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage); + } + + return Damage; + } + + if(this->Skills[SKILL_MANASHIELD]->TimerValue() > 0 + || this->Skills[SKILL_MANASHIELD]->Get() > 0){ + // NOTE(fusion): We only send these if the attack was fully absorbed, + // else it'd be overwritten by whatever effect and messages we send + // next, when the victim's hitpoints are actually touched. + int ManaPoints = this->Skills[SKILL_MANA]->Get(); + if(Damage <= ManaPoints){ + this->Skills[SKILL_MANA]->Change(-Damage); + GraphicalEffect(this->CrObject, EFFECT_MANA_HIT); + TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage); + if(this->Type == PLAYER && this->Connection != NULL){ + if(Attacker != NULL){ + SendMessage(this->Connection, TALK_STATUS_MESSAGE, + "You lose %d mana blocking an attack by %s.", + Damage, Attacker->Name); + }else{ + SendMessage(this->Connection, TALK_STATUS_MESSAGE, + "You lose %d mana.", Damage); + } + SendPlayerData(this->Connection); + } + return Damage; + } + + this->Skills[SKILL_MANA]->Set(0); + Damage -= ManaPoints; + } + + int HitPoints = this->Skills[SKILL_HITPOINTS]->Get(); + if(Damage > HitPoints){ + Damage = HitPoints; + } + + this->Skills[SKILL_HITPOINTS]->Change(-Damage); + if(Responsible != NULL){ + ASSERT(Attacker != NULL); + if(Responsible == Attacker){ + this->Combat.AddDamageToCombatList(Attacker->ID, Damage); + }else{ + this->Combat.AddDamageToCombatList(Attacker->ID, Damage / 2); + this->Combat.AddDamageToCombatList(Responsible->ID, Damage / 2); + } + } + + int HitEffect = EFFECT_NONE; + int TextColor = COLOR_BLACK; + int SplashLiquid = LIQUID_NONE; + if(DamageType == DAMAGE_PHYSICAL){ + switch(RaceData[this->Race].Blood){ + case BT_BLOOD:{ + HitEffect = EFFECT_BLOOD_HIT; + TextColor = COLOR_RED; + SplashLiquid = LIQUID_BLOOD; + break; + } + case BT_SLIME:{ + HitEffect = EFFECT_POISON_HIT; + TextColor = COLOR_LIGHTGREEN; + SplashLiquid = LIQUID_SLIME; + break; + } + case BT_BONES:{ + HitEffect = EFFECT_BONE_HIT; + TextColor = COLOR_LIGHTGRAY; + break; + } + case BT_FIRE:{ + HitEffect = EFFECT_FIRE_HIT; + TextColor = COLOR_ORANGE; + break; + } + case BT_ENERGY:{ + HitEffect = EFFECT_ENERGY_HIT; + TextColor = COLOR_LIGHTBLUE; + break; + } + default:{ + error("TCreature::Damage: Ungültiger Bluttyp %d für Rasse %d.\n", + RaceData[this->Race].Blood, this->Race); + break; + } + } + }else if(Damage == DAMAGE_POISON){ + HitEffect = EFFECT_POISON; + TextColor = COLOR_LIGHTGREEN; + }else if(DamageType == DAMAGE_FIRE){ + HitEffect = EFFECT_FIRE_HIT; + TextColor = COLOR_ORANGE; + }else if(DamageType == DAMAGE_ENERGY){ + HitEffect = EFFECT_ENERGY_HIT; + TextColor = COLOR_LIGHTBLUE; + }else if(DamageType == DAMAGE_LIFEDRAIN){ + HitEffect = EFFECT_MAGIC_RED; + TextColor = COLOR_RED; + }else{ + // TODO(fusion): The original decompiled function would return here but + // I don't think it's a good idea because it would skip death handling. + error("TCreature::Damage: Ungültiger Schadenstyp %d.\n", DamageType); + //return Damage; + } + + if(HitEffect != EFFECT_NONE){ + GraphicalEffect(this->CrObject, HitEffect); + TextualEffect(this->CrObject, TextColor, "%d", Damage); + if(SplashLiquid != LIQUID_NONE){ + CreatePool(GetMapContainer(this->CrObject), + GetSpecialObject(BLOOD_SPLASH), + SplashLiquid); + } + } + + if(this->Type == PLAYER && this->Connection != NULL){ + if(Attacker != NULL){ + SendMessage(this->Connection, TALK_STATUS_MESSAGE, + "You lose %d hitpoint%s due to an attack by %s.", + Damage, (Damage == 1 ? "" : "s"), Attacker->Name); + }else{ + SendMessage(this->Connection, TALK_STATUS_MESSAGE, + "You lose %d hitpoint%s.", + Damage, (Damage == 1 ? "" : "s")); + } + SendPlayerData(this->Connection); + } + + if(Damage == HitPoints){ + if(this->Type == PLAYER){ + for(int Position = 1; Position <= 10; Position += 1){ + Object Obj = GetBodyObject(this->ID, Position); + if(Obj == NONE){ + continue; + } + + ObjectType ObjType = Obj.getObjectType(); + if(ObjType.getFlag(CLOTHES) && (int)ObjType.getAttribute(BODYPOSITION) == Position){ + ObjectType AmuletOfLossType = GetNewObjectType(77, 12); + if(ObjType == AmuletOfLossType){ + Log("game", "%s stirbt mit Amulett of Loss.\n", this->Name); + this->LoseInventory = 0; + Delete(Obj, -1); + // TODO(fusion): Shouldn't we break here? We could also + // just check if there is an amulet of loss in the necklace + // container instead of iterating over all of them. + } + } + } + } + + int OldLevel = this->Skills[SKILL_LEVEL]->Get(); + this->Death(); + if(Attacker != NULL && this->Type == PLAYER){ + Attacker->BlockLogout(900, true); + if(Responsible != Attacker){ + Responsible->BlockLogout(900, true); + } + } + + uint32 MurdererID = 0; + char Remark[30] = {}; + if(Attacker == NULL){ + AddKillStatistics(0, this->Race); + if(DamageType == DAMAGE_PHYSICAL){ + strcpy(Remark, "a hit"); + }else if(DamageType == DAMAGE_POISON){ + strcpy(Remark, "poison"); + }else if(DamageType == DAMAGE_FIRE){ + strcpy(Remark, "fire"); + }else if(DamageType == DAMAGE_ENERGY){ + strcpy(Remark, "energy"); + }else{ + // NOTE(fusion): We probably don't expect any other damage type + // as the cause of death when there is no attacker. + error("TCreature::Damage: Ungültiger Schadenstyp %d als Todesursache.\n", DamageType); + } + }else{ + AddKillStatistics(Attacker->Race, this->Race); + strcpy(this->Murderer, Attacker->Name); + + if(Responsible->Type == PLAYER){ + MurdererID = Responsible->ID; + } + + if(Attacker->Type != PLAYER){ + strcpy(Remark, Attacker->Name); + } + } + + if(this->Type == PLAYER){ + if(MurdererID != 0 && MurdererID != this->ID){ + bool Justified = true; + if(IsCreaturePlayer(MurdererID)){ + TPlayer *Murderer = GetPlayer(MurdererID); + if(Murderer != NULL){ + Justified = Murderer->IsAttackJustified(this->ID); + Murderer->RecordMurder(this->ID); + } + } + CharacterDeathOrder(this, OldLevel, MurdererID, Remark, !Justified); + } + + uint32 MostDangerousID = this->Combat.GetMostDangerousAttacker(); + if(MostDangerousID != 0 + && MostDangerousID != MurdererID + && MostDangerousID != this->ID + && IsCreaturePlayer(MostDangerousID)){ + bool Justified = true; + TPlayer *MostDangerous = GetPlayer(MostDangerousID); + if(MostDangerous != NULL){ + Justified = MostDangerous->IsAttackJustified(this->ID); + MostDangerous->RecordMurder(this->ID); + } + + // TODO(fusion): The original function is confusing at this point + // but it seems correct that the remark is included only with the + // murderer. + CharacterDeathOrder(this, OldLevel, MostDangerousID, "", !Justified); + } + } + } + + AnnounceChangedCreature(this->ID, 1); // CREATURE_HITPOINTS_CHANGED ? + return Damage; +} + +// Creature +// ============================================================================= +bool IsCreaturePlayer(uint32 CreatureID){ + return CreatureID < 0x40000000; +} + +// + +void DeleteChainCreature(TCreature *Creature){ + if(Creature == NULL){ + error("DeleteChainCreature: Übegebene Kreatur existiert nicht.\n"); + return; + } + + // NOTE(fusion): All creatures in each 16x16 region form a creature linked + // list, despite its current floor. Whether that is a good idea is a whole + // other matter. + int BlockX = Creature->posx / 16; + int BlockY = Creature->posy / 16; + uint32 *FirstID = FirstChainCreature->at(BlockX, BlockY); + + if(*FirstID == Creature->ID){ + *FirstID = Creature->NextChainCreature; + }else{ + uint32 CurrentID = *FirstID; + while(true){ + if(CurrentID == 0){ + error("DeleteChainCreature: Kreatur nicht gefunden.\n"); + return; + } + + TCreature *Current = GetCreature(CurrentID); + if(Current == NULL){ + error("DeleteChainCreature: Kreatur existiert nicht.\n"); + return; + } + + if(Current->NextChainCreature == Creature->ID){ + Current->NextChainCreature = Creature->NextChainCreature; + break; + } + + CurrentID = Current->NextChainCreature; + } + } +} + +// Kill Statistics +// ============================================================================= +void AddKillStatistics(int AttackerRace, int DefenderRace){ + // NOTE(fusion): I think the race name can be "human" only for players, + // which means we're probably tracking how many creatures are killed by + // players with `KilledCreatures`, and how many players are killed by + // creatures with `KilledPlayers`. + + if(strcmp(RaceData[AttackerRace].Name, "human") == 0){ + KilledCreatures[DefenderRace] += 1; + } + + // NOTE(fusion): This seems to track how many players are killed by + if(strcmp(RaceData[DefenderRace].Name, "human") == 0){ + KilledPlayers[AttackerRace] += 1; + } +} + +// Race +// ============================================================================= +TRaceData::TRaceData(void) : + Skill(1, 5, 5), + Talk(1, 5, 5), + Item(1, 5, 5), + Spell(1, 5, 5) +{ + this->Name[0] = 0; + this->Article[0] = 0; + this->Outfit.OutfitID = 0; + this->Outfit.ObjectType = 0; + this->Blood = BT_BLOOD; + this->ExperiencePoints = 0; + this->FleeThreshold = 0; + this->Attack = 0; + this->Defend = 0; + this->Armor = 0; + this->Poison = 0; + this->SummonCost = 0; + this->LoseTarget = 0; + this->Strategy[0] = 100; + this->Strategy[1] = 0; + this->Strategy[2] = 0; + this->Strategy[3] = 0; + this->KickBoxes = false; + this->KickCreatures = false; + this->SeeInvisible = false; + this->Unpushable = false; + this->DistanceFighting = false; + this->NoSummon = false; + this->NoIllusion = false; + this->NoConvince = false; + this->NoBurning = false; + this->NoPoison = false; + this->NoEnergy = false; + this->NoHit = false; + this->NoLifeDrain = false; + this->NoParalyze = false; + this->Skills = 0; + this->Talks = 0; + this->Items = 0; + this->Spells = 0; +} + +int GetRaceByName(const char *RaceName){ + int Result = 0; + for(int Race = 1; Race < NARRAY(RaceData); Race += 1){ + if(stricmp(RaceName, RaceData[Race].Name) == 0){ + Result = Race; + break; + } + } + return Result; +} + +// Raid +// ============================================================================= + + diff --git a/src/crplayer.cc b/src/crplayer.cc new file mode 100644 index 0000000..3193843 --- /dev/null +++ b/src/crplayer.cc @@ -0,0 +1,72 @@ +#include "cr.hh" + +#include "stubs.hh" + +uint8 TPlayer::GetRealProfession(void){ + return this->Profession; +} + +uint8 TPlayer::GetEffectiveProfession(void){ + uint8 Profession = this->Profession; + if(Profession >= 10){ + Profession -= 10; + } + return Profession; +} + +uint8 TPlayer::GetActiveProfession(void){ + uint8 Profession; + if(CheckRight(this->ID, PREMIUM_ACCOUNT)){ + Profession = this->Profession; + }else{ + Profession = this->GetEffectiveProfession(); + } + return Profession; +} + +bool TPlayer::GetActivePromotion(void){ + return CheckRight(this->ID, PREMIUM_ACCOUNT) + && this->Profession >= 10; +} + +void TPlayer::CheckState(void){ + if(this->Connection != NULL){ + uint8 State = 0; + + if(this->Skills[SKILL_POISON]->TimerValue() > 0){ + State |= 0x01; + } + + if(this->Skills[SKILL_BURNING]->TimerValue() > 0){ + State |= 0x02; + } + + if(this->Skills[SKILL_ENERGY]->TimerValue() > 0){ + State |= 0x04; + } + + // TODO(fusion): Not sure about this one. + if(this->Skills[SKILL_DRUNK]->TimerValue() > 0 && this->Skills[SKILL_DRUNK]->Get() == 0){ + State |= 0x08; + } + + if(this->Skills[SKILL_MANASHIELD]->TimerValue() > 0 || this->Skills[SKILL_MANASHIELD]->Get() > 0){ + State |= 0x10; + } + + if(this->Skills[SKILL_GO_STRENGTH]->MDAct < 0){ + State |= 0x20; + }else if(this->Skills[SKILL_GO_STRENGTH]->MDAct > 0){ + State |= 0x40; + } + + if(RoundNr < this->EarliestLogoutRound){ + State |= 0x80; + } + + if(State != this->OldState){ + SendPlayerState(this->Connection, State); + this->OldState = State; + } + } +} diff --git a/src/crskill.cc b/src/crskill.cc index 384e1fc..d2eb067 100644 --- a/src/crskill.cc +++ b/src/crskill.cc @@ -1,5 +1,4 @@ -#include "creature.hh" -#include "monster.hh" +#include "cr.hh" #include "stubs.hh" diff --git a/src/crskill.hh b/src/crskill.hh deleted file mode 100644 index abb2476..0000000 --- a/src/crskill.hh +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef TIBIA_CRSKILL_HH_ -#define TIBIA_CRSKILL_HH_ 1 - -#include "common.hh" - -struct TCreature; - -struct TSkill{ - // REGULAR FUNCTIONS - // ========================================================================= - TSkill(int SkNr, TCreature *Master); - int Get(void); - int GetProgress(void); - void Check(void); - void Change(int Amount); - void SetMDAct(int MDAct); - void Load(int Act, int Max, int Min, int DAct, int MDAct, - int Cycle, int MaxCycle, int Count, int MaxCount, int AddLevel, - int Exp, int FactorPercent, int NextLevel, int Delta); - void Save(int *Act, int *Max, int *Min, int *DAct, int *MDAct, - int *Cycle, int *MaxCycle, int *Count, int *MaxCount, int *AddLevel, - int *Exp, int *FactorPercent, int *NextLevel, int *Delta); - - // VIRTUAL FUNCTIONS - // ========================================================================= - virtual ~TSkill(void); // VTABLE[ 0] - // Duplicate destructor that also calls operator delete. // VTABLE[ 1] - virtual void Set(int Value); // VTABLE[ 2] - virtual void Increase(int Amount); // VTABLE[ 3] - virtual void Decrease(int Amount); // VTABLE[ 4] - virtual int GetExpForLevel(int Level); // VTABLE[ 5] - virtual void Advance(int Range); // VTABLE[ 6] - virtual void ChangeSkill(int FactorPercent, int Delta); // VTABLE[ 7] - virtual int ProbeValue(int Max, bool Increase); // VTABLE[ 8] - virtual bool Probe(int Diff, int Prob, bool Increase); // VTABLE[ 9] - virtual bool Process(void); // VTABLE[10] - virtual bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue); // VTABLE[11] - virtual bool DelTimer(void); // VTABLE[12] - virtual int TimerValue(void); // VTABLE[13] - virtual bool Jump(int Range); // VTABLE[14] - virtual void Event(int Range); // VTABLE[15] - virtual void Reset(void); // VTABLE[16] - - // DATA - // ========================================================================= - //void *VTABLE; // IMPLICIT - int DAct; // Delta Value - Probably from equipment. - int MDAct; // Delta Magic Value - Probably from spells. - uint16 SkNr; - TCreature *Master; - int Act; // Actual Value (?) - int Max; - int Min; - int FactorPercent; - int LastLevel; - int NextLevel; - int Delta; - int Exp; - int Cycle; - int MaxCycle; - int Count; - int MaxCount; - int AddLevel; -}; - -struct TSkillLevel: TSkill { - TSkillLevel(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Increase(int Amount) override; - void Decrease(int Amount) override; - int GetExpForLevel(int Level) override; - bool Jump(int Range) override; -}; - -struct TSkillProbe: TSkill { - TSkillProbe(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Increase(int Amount) override; - void Decrease(int Amount) override; - int GetExpForLevel(int Level) override; - void ChangeSkill(int FactorPercent, int Delta) override; - int ProbeValue(int Max, bool Increase) override; - bool Probe(int Diff, int Prob, bool Increase) override; - bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; - bool Jump(int Range) override; - void Event(int Range) override; -}; - -struct TSkillAdd: TSkill { - TSkillAdd(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Advance(int Range) override; -}; - -struct TSkillHitpoints: TSkillAdd { - TSkillHitpoints(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} - void Set(int Value) override; -}; - -struct TSkillMana: TSkillAdd { - TSkillMana(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} - void Set(int Value) override; -}; - -struct TSkillGoStrength: TSkillAdd { - TSkillGoStrength(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} - bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; - void Event(int Range) override; -}; - -struct TSkillCarryStrength: TSkillAdd { - TSkillCarryStrength(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} - void Set(int Value) override; -}; - -struct TSkillSoulpoints: TSkillAdd { - TSkillSoulpoints(int SkNr, TCreature *Master) : TSkillAdd(SkNr, Master) {} - void Set(int Value) override; - int TimerValue(void) override; - void Event(int Range) override; -}; - -struct TSkillFed: TSkill { - TSkillFed(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Event(int Range) override; -}; - -struct TSkillLight: TSkill { - TSkillLight(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; - void Event(int Range) override; -}; - -struct TSkillIllusion: TSkill { - TSkillIllusion(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; - void Event(int Range) override; -}; - -struct TSkillPoison: TSkill { - TSkillPoison(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - bool Process(void) override; - bool SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue) override; - void Event(int Range) override; - void Reset(void) override; -}; - -struct TSkillBurning: TSkill { - TSkillBurning(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Event(int Range) override; -}; - -struct TSkillEnergy: TSkill { - TSkillEnergy(int SkNr, TCreature *Master) : TSkill(SkNr, Master) {} - void Event(int Range) override; -}; - -struct TSkillBase{ - // REGULAR FUNCTIONS - // ========================================================================= - TSkillBase(void); - ~TSkillBase(void); - bool NewSkill(uint16 SkillNo, TCreature *Creature); - bool SetSkills(int Race); - void ProcessSkills(void); - bool SetTimer(uint16 SkNr, int Cycle, int Count, int MaxCount, int AdditionalValue); - void DelTimer(uint16 SkNr); - - // DATA - // ========================================================================= - TSkill *Skills[25]; - TSkill *TimerList[25]; - uint16 FirstFreeTimer; -}; - -#endif //TIBIA_CRSKILL_HH_ diff --git a/src/info.cc b/src/info.cc index 7105535..808133b 100644 --- a/src/info.cc +++ b/src/info.cc @@ -1,5 +1,5 @@ #include "info.hh" -#include "creature.hh" +#include "cr.hh" #include "stubs.hh" diff --git a/src/magic.cc b/src/magic.cc index b3d70b1..ea1ba73 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -1,8 +1,6 @@ #include "magic.hh" #include "config.hh" -#include "creature.hh" #include "info.hh" -#include "monster.hh" #include "stubs.hh" diff --git a/src/magic.hh b/src/magic.hh index e1dc354..8946153 100644 --- a/src/magic.hh +++ b/src/magic.hh @@ -2,7 +2,7 @@ #define TIBIA_MAGIC_HH_ 1 #include "common.hh" -#include "creature.hh" +#include "cr.hh" enum : int { FIELD_TYPE_FIRE = 1, diff --git a/src/monster.hh b/src/monster.hh deleted file mode 100644 index f7ce3f5..0000000 --- a/src/monster.hh +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef TIBIA_MONSTER_HH_ -#define TIBIA_MONSTER_HH_ 1 - -#include "common.hh" -#include "creature.hh" -#include "containers.hh" -#include "enums.hh" - -struct TSkillData { - int Nr; - int Actual; - int Minimum; - int Maximum; - int NextLevel; - int FactorPercent; - int AddLevel; -}; - -struct TItemData { - ObjectType Type; - int Maximum; - int Probability; -}; - -struct TSpellData { - SpellShapeType Shape; - int ShapeParam1; - int ShapeParam2; - int ShapeParam3; - int ShapeParam4; - SpellImpactType Impact; - int ImpactParam1; - int ImpactParam2; - int ImpactParam3; - int ImpactParam4; - int Delay; -}; - -struct TRaceData { - char Name[30]; - char Article[3]; - TOutfit Outfit; - ObjectType MaleCorpse; - ObjectType FemaleCorpse; - BloodType Blood; - int ExperiencePoints; - int FleeThreshold; - int Attack; - int Defend; - int Armor; - int Poison; - int SummonCost; - int LoseTarget; - int Strategy[4]; - bool KickBoxes; - bool KickCreatures; - bool SeeInvisible; - bool Unpushable; - bool DistanceFighting; - bool NoSummon; - bool NoIllusion; - bool NoConvince; - bool NoBurning; - bool NoPoison; - bool NoEnergy; - bool NoHit; - bool NoLifeDrain; - bool NoParalyze; - int Skills; - vector Skill; - int Talks; - vector Talk; // POINTER? Probably a reference from `AddDynamicString`? - int Items; - vector Item; - int Spells; - vector Spell; -}; - -#if 0 -// TODO(fusion): -struct TMonster: TNonplayer { - int Home; - uint32 Master; - uint32 Target; -}; -#endif - -//#define MAX_RACES 512 -extern TRaceData RaceData[512]; -extern int KilledCreatures[512]; -extern int KilledPlayers[512]; - -#endif //TIBIA_MONSTER_HH_ diff --git a/src/player.cc b/src/player.cc deleted file mode 100644 index 04298c6..0000000 --- a/src/player.cc +++ /dev/null @@ -1,73 +0,0 @@ -#include "player.hh" -#include "enums.hh" - -#include "stubs.hh" - -uint8 TPlayer::GetRealProfession(void){ - return this->Profession; -} - -uint8 TPlayer::GetEffectiveProfession(void){ - uint8 Profession = this->Profession; - if(Profession >= 10){ - Profession -= 10; - } - return Profession; -} - -uint8 TPlayer::GetActiveProfession(void){ - uint8 Profession; - if(CheckRight(this->ID, PREMIUM_ACCOUNT)){ - Profession = this->Profession; - }else{ - Profession = this->GetEffectiveProfession(); - } - return Profession; -} - -bool TPlayer::GetActivePromotion(void){ - return CheckRight(this->ID, PREMIUM_ACCOUNT) - && this->Profession >= 10; -} - -void TPlayer::CheckState(void){ - if(this->Connection != NULL){ - uint8 State = 0; - - if(this->Skills[SKILL_POISON]->TimerValue() > 0){ - State |= 0x01; - } - - if(this->Skills[SKILL_BURNING]->TimerValue() > 0){ - State |= 0x02; - } - - if(this->Skills[SKILL_ENERGY]->TimerValue() > 0){ - State |= 0x04; - } - - // TODO(fusion): Not sure about this one. - if(this->Skills[SKILL_DRUNK]->TimerValue() > 0 && this->Skills[SKILL_DRUNK]->Get() == 0){ - State |= 0x08; - } - - if(this->Skills[SKILL_MANASHIELD]->TimerValue() > 0 || this->Skills[SKILL_MANASHIELD]->Get() > 0){ - State |= 0x10; - } - - if(this->Skills[SKILL_GO_STRENGTH]->MDAct < 0){ - State |= 0x20; - }else if(this->Skills[SKILL_GO_STRENGTH]->MDAct > 0){ - State |= 0x40; - } - - if(RoundNr < this->EarliestLogoutRound){ - State |= 0x80; - } - - if(State != this->OldState){ - SendPlayerState(this->Connection, State); - this->OldState = State; - } - } -} diff --git a/src/player.hh b/src/player.hh deleted file mode 100644 index 0731f11..0000000 --- a/src/player.hh +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef TIBIA_PLAYER_HH_ -#define TIBIA_PLAYER_HH_ 1 - -#include "common.hh" -#include "containers.hh" -#include "creature.hh" - -struct TPlayerData { - uint32 CharacterID; - pid_t Locked; - int Sticky; - bool Dirty; - int Race; - TOutfit OriginalOutfit; - TOutfit CurrentOutfit; - time_t LastLoginTime; - time_t LastLogoutTime; - int startx; - int starty; - int startz; - int posx; - int posy; - int posz; - int Profession; - int PlayerkillerEnd; - int Actual[25]; - int Maximum[25]; - int Minimum[25]; - int DeltaAct[25]; - int MagicDeltaAct[25]; - int Cycle[25]; - int MaxCycle[25]; - int Count[25]; - int MaxCount[25]; - int AddLevel[25]; - int Experience[25]; - int FactorPercent[25]; - int NextLevel[25]; - int Delta[25]; - uint8 SpellList[256]; - int QuestValues[500]; - int MurderTimestamps[20]; - uint8 *Inventory; - int InventorySize; - uint8 *Depot[9]; - int DepotSize[9]; - uint32 AccountID; - int Sex; - char Name[30]; - uint8 Rights[12]; - char Guild[31]; - char Rank[31]; - char Title[31]; - int Buddies; - uint32 Buddy[100]; - char BuddyName[100][30]; - uint32 EarliestYellRound; - uint32 EarliestTradeChannelRound; - uint32 EarliestSpellTime; - uint32 EarliestMultiuseTime; - uint32 TalkBufferFullTime; - uint32 MutingEndRound; - uint32 Addressees[20]; - uint32 AddresseesTimes[20]; - int NumberOfMutings; -}; - -struct TPlayer: TCreature { - // REGULAR FUNCTIONS - // ========================================================================= - uint8 GetRealProfession(void); - uint8 GetEffectiveProfession(void); - uint8 GetActiveProfession(void); - bool GetActivePromotion(void); - void ClearProfession(void); - void SetProfession(uint8 Profession); - - int GetQuestValue(int Number); - void SetQuestValue(int Number, int Value); - - uint32 GetPartyLeader(bool CheckFormer); - - bool SpellKnown(int SpellNr); - - bool IsAttackJustified(uint32 Victim); - void RecordAttack(uint32 Victim); - void RecordMurder(uint32 Victim); - - void CheckState(void); - - // VIRTUAL FUNCTIONS - // ========================================================================= - // TODO - - // DATA - // ========================================================================= - //TCreature super_TCreature; // IMPLICIT - uint32 AccountID; - char Guild[31]; - char Rank[31]; - char Title[31]; - char IPAddress[16]; - uint8 Rights[12]; - Object Depot; - int DepotNr; - int DepotSpace; - RESULT ConstructError; - TPlayerData *PlayerData; - Object TradeObject; - uint32 TradePartner; - bool TradeAccepted; - int OldState; - uint32 Request; - int RequestTimestamp; - uint32 RequestProcessingGamemaster; - int TutorActivities; - uint8 SpellList[256]; - int QuestValues[500]; - Object OpenContainer[16]; - vector AttackedPlayers; - int NumberOfAttackedPlayers; - bool Aggressor; - vector FormerAttackedPlayers; - int NumberOfFormerAttackedPlayers; - bool FormerAggressor; - uint32 FormerLogoutRound; - uint32 PartyLeader; - uint32 PartyLeavingRound; - uint32 TalkBufferFullTime; - uint32 MutingEndRound; - int NumberOfMutings; - uint32 Addressees[20]; - uint32 AddresseesTimes[20]; -}; - -#endif //TIBIA_PLAYER_HH_ diff --git a/src/stubs.hh b/src/stubs.hh index a1b451a..030f502 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -4,10 +4,9 @@ #include "common.hh" #include "enums.hh" #include "connection.hh" -#include "creature.hh" +#include "cr.hh" #include "magic.hh" #include "map.hh" -#include "player.hh" // IMPORTANT(fusion): These function definitions exist to test compilation. They're // not yet implemented and will cause the linker to fail with unresolved symbols. -- cgit v1.2.3