aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-05-16 18:46:18 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-05-16 18:46:18 -0300
commit717f5cd9dacf30fe6bf1561db9297e75fd27fdef (patch)
tree4785a3bf770da880d514fc232a9972bd8473a72e /src
downloadgame-717f5cd9dacf30fe6bf1561db9297e75fd27fdef.tar.gz
game-717f5cd9dacf30fe6bf1561db9297e75fd27fdef.zip
initial commit - getting at TSkill structs
Diffstat (limited to 'src')
-rw-r--r--src/creature.cc429
-rw-r--r--src/creature.hh76
-rw-r--r--src/enums.hh219
-rw-r--r--src/main.cc38
-rw-r--r--src/main.hh78
5 files changed, 840 insertions, 0 deletions
diff --git a/src/creature.cc b/src/creature.cc
new file mode 100644
index 0000000..b6b046b
--- /dev/null
+++ b/src/creature.cc
@@ -0,0 +1,429 @@
+#include "creature.hh"
+#include "enums.hh"
+
+// TSkill REGULAR FUNCTIONS
+//==============================================================================
+TSkill::TSkill(int SkNr, TCreature *Master){
+ this->Reset();
+ this->SkNr = (uint16)SkNr;
+ this->Master = Master;
+}
+
+int TSkill::Get(void){
+ int Value = this->Act;
+ if(Value < this->Min)
+ Value = this->Min;
+ Value += this->MDAct + this->DAct;
+ return Value;
+}
+
+int TSkill::GetProgress(void){
+ int Result = 0;
+ if(this->NextLevel > this->LastLevel){
+ Result = (this->NextLevel - this->Exp) * 100 / (this->NextLevel - this->LastLevel);
+
+ // TODO(fusion): This feels too much for reporting a mostly *impossible* error.
+ if(Result < 0 || Result > 100){
+ error("TSkill::GetProgress: Berechnungsfehler Exp %d, Last %d, Next %d, Prozent %d.\n",
+ this->Exp, this->LastLevel, this->NextLevel, Result);
+
+ const char *MasterName = "(Unknown";
+ if(this->Master != NULL){
+ MasterName = this->Master->Name;
+ }
+ error("# Spieler %s - Skill %d\n", MasterName, this->SkNr);
+ Result = 0;
+ }
+ }
+ return Result;
+}
+
+void TSkill::Check(void){
+ if(this->Act > this->Max){
+ this->Act = this->Max;
+ }
+}
+
+void TSkill::Change(int Amount){
+ this->Set(this->Act + Amount);
+
+ // TODO(fusion): Probably `TSkill::Check` inlined? It doesn't make a whole
+ // lot of sense because `TSkill::Set` also checks the value. Maybe a custom
+ // version of it does something differently?
+ if(this->Act > this->Max){
+ this->Act = this->Max;
+ }
+}
+
+void TSkill::SetMDAct(int MDAct){
+ this->MDAct = MDAct;
+ if(this->SkNr == 4 && this->Master && this->Master->Type == PLAYER){
+ // TODO(fusion): Same as `TSkill::Process`.
+ ((TPlayer*)this->Master)->CheckState();
+ }
+}
+
+void TSkill::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){
+ this->Act = Act;
+ this->Max = Max;
+ this->Min = Min;
+ this->DAct = DAct;
+ this->MDAct = MDAct;
+ this->Cycle = Cycle;
+ this->MaxCycle = MaxCycle;
+ this->Count = Count;
+ this->MaxCount = MaxCount;
+ this->AddLevel = AddLevel;
+ this->Exp = Exp;
+ this->FactorPercent = FactorPercent;
+ this->NextLevel = NextLevel;
+ this->Delta = Delta;
+
+ // TODO(fusion): Shouldn't we also do the same for `NextLevel`?
+ this->LastLevel = this->GetExpForLevel(Act);
+
+ TCreature *Master = this->Master;
+ if(Master && Cycle != 0){
+ int SkNr = this->SkNr;
+ if(SkNr >= NARRAY(Master->Skills)){
+ error("TSkillBase::SetTimer: Ungueltige SkNr: %d\n", SkNr);
+ return;
+ }
+
+ // TODO(fusion): I'm not entirely sure wtf is going on here.
+ TSkill *Other = Master->Skills[SkNr];
+ Master->DelTimer(SkNr);
+ if(Other->SetTimer(Cycle, Count, MaxCount, FactorPercent)){
+ Master->TimerList[Master->FirstFreeTimer] = Other;
+ Master->FirstFreeTimer += 1;
+ }
+ }
+}
+
+
+void TSkill::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){
+ *Act = this->Act;
+ *Max = this->Max;
+ *Min = this->Min;
+ *DAct = this->DAct;
+ *MDAct = this->MDAct;
+ *Cycle = this->Cycle;
+ *MaxCycle = this->MaxCycle;
+ *Count = this->Count;
+ *MaxCount = this->MaxCount;
+ *AddLevel = this->AddLevel;
+ *Exp = this->Exp;
+ *FactorPercent = this->FactorPercent;
+ *NextLevel = this->NextLevel;
+ *Delta = this->Delta;
+}
+
+// TSkill VIRTUAL FUNCTIONS
+//==============================================================================
+TSkill::~TSkill(void){
+ if(this->Master != NULL){
+ this->Master->DelTimer(this->SkNr);
+ }
+}
+
+void TSkill::Set(int Value){
+ if(Value > this->Max)
+ Value = this->Max;
+ this->Act = Value;
+}
+
+void TSkill::Increase(int Amount){
+ // no-op
+}
+
+void TSkill::Decrease(int Amount){
+ // no-op
+}
+
+int TSkill::GetExpForLevel(int Level){
+ return 0;
+}
+
+void TSkill::Advance(int Range){
+ // no-op
+}
+
+void TSkill::ChangeSkill(int FactorPercent, int Delta){
+ // no-op
+}
+
+int TSkill::ProbeValue(int Max, bool Increase){
+ return 0;
+}
+
+bool TSkill::Probe(int Diff, int Prob, bool Increase){
+ return false;
+}
+
+bool TSkill::Process(void){
+ bool Result = this->Cycle == 0;
+ if(Result){
+ if(this->Master && this->Master->Type == PLAYER){
+ // TODO(fusion): Verify if a simple pointer cast works. Maybe `dynamic_cast`?
+ ((TPlayer*)this->Master)->CheckState();
+ }
+ }else if(this->Count <= 0){
+ this->Count = this->MaxCount;
+ // NOTE(fusion): `Range` should move `Cycle` towards ZERO.
+ int Range = (this->Cycle < 1) ? +1 : -1;
+ this->Cycle += Range;
+ this->Event(Range);
+ }else{
+ this->Count -= 1;
+ }
+
+ return Result;
+}
+
+bool TSkill::SetTimer(int Cycle, int Count, int MaxCount, int AdditionalValue){
+ this->Cycle = Cycle;
+ this->Count = Count;
+ this->MaxCount = MaxCount;
+ if(this->Master && this->Master->Type == PLAYER){
+ // TODO(fusion): Same as `TSkill::Process`.
+ ((TPlayer*)this->Master)->CheckState();
+ }
+ return true;
+}
+
+bool TSkill::DelTimer(void){
+ this->Cycle = 0;
+ this->Count = 0;
+ this->MaxCount = 0;
+ if(this->Master && this->Master->Type == PLAYER){
+ // TODO(fusion): Same as `TSkill::Process`.
+ ((TPlayer*)this->Master)->CheckState();
+ }
+ return true;
+}
+
+int TSkill::TimerValue(void){
+ return this->Cycle;
+}
+
+bool TSkill::Jump(int Range){
+ return true;
+}
+
+void TSkill::Event(int Range){
+ // no-op
+}
+
+void TSkill::Reset(void){
+ this->Act = 0;
+ this->Max = INT_MAX;
+ this->Min = 0;
+ this->DAct = 0;
+ this->MDAct = 0;
+ this->FactorPercent = 1000;
+ this->LastLevel = 0;
+ this->NextLevel = INT_MAX;
+ this->Exp = 0;
+ this->Delta = INT_MAX;
+ this->Cycle = 0;
+ this->MaxCycle = 0;
+ this->Count = 0;
+ this->MaxCount = 0;
+ this->AddLevel = 0;
+}
+
+// TSkillLevel
+//==============================================================================
+TSkillLevel::~TSkillLevel(void){
+ if(this->Master != NULL){
+ this->Master->DelTimer(this->SkNr);
+ }
+}
+
+void TSkillLevel::Increase(int Amount){
+ if (Amount < 0) {
+ error("TSkillLevel::Increase: Amount negativ (%d).\n", Amount);
+ return;
+ }
+
+ // BUG(fusion): No bounds check as in `TSkillLevel::Decrease`?
+
+ int Range = 0;
+ this->Exp += Amount;
+ while(this->Exp >= this->NextLevel){
+ this->Act += 1;
+ this->LastLevel = this->GetExpForLevel(this->Act);
+ this->NextLevel = this->GetExpForLevel(this->Act + 1);
+ if(this->NextLevel < 0){
+ // BUG(fusion): We don't check if `Master` is valid here?
+ error("TSkillLevel::Increase: Skill vor Überlauf (%s, Skill %d).\n", this->Master->Name, this->SkNr);
+ this->NextLevel = this->Exp;
+ this->Exp -= 1000000;
+ break;
+ }
+ Range += 1;
+ }
+
+ if(Range != 0){
+ this->Jump(Range);
+ }
+
+ if(this->Master == NULL){
+ error("TSkillLevel::Increase: GetMaster liefert NULL zurueck.\n");
+ return;
+ }
+
+ if(this->Master->Type == PLAYER){
+ SendPlayerData(this->Master->Connection);
+ }
+}
+
+void TSkillLevel::Decrease(int Amount){
+ if(Amount < 0){
+ error("TSkillLevel::Decrease: Amount negativ (%d).\n", Amount);
+ return;
+ }
+
+ // TODO(fusion): This is some weird ass comparison.
+ if(Amount > this->Exp && this->Exp > 100000){
+ error("TSkillLevel::Decrease: Amount zu gross(%d).\n", Amount);
+ return;
+ }
+
+ int Range = 0;
+ if(Amount < this->Exp){
+ this->Exp -= Amount;
+ }else{
+ this->Exp = 0;
+ }
+
+ // TODO(fusion): This could probably be an oversight but the decompiled
+ // function was calling `GetExpForLevel` instead of using `LastLevel`
+ // which makes me wonder whether `LastLevel` is properly initialized.
+ while(this->Exp < this->LastLevel){
+ this->Act -= 1;
+ this->NextLevel = this->LastLevel;
+ this->LastLevel = this->GetExpForLevel(this->Act);
+ }
+
+ if(Range != 0){
+ this->Jump(Range);
+ }
+
+ if(this->Master == NULL){
+ error("TSkillLevel::Decrease: GetMaster liefert NULL zurueck.\n");
+ return;
+ }
+
+ if(this->Master->Type == PLAYER){
+ SendPlayerData(this->Master->Connection);
+ }
+}
+
+int TSkillLevel::GetExpForLevel(int Level){
+ if(Level < 1){
+ error("TSkillLevel::GetExpForLevel: Ungültiger Level %d.\n", Level);
+ return 0;
+ }
+
+ if(Level > 500){
+ error("TSkillLevel::GetExpForLevel: Level=%d; Formel gegen Überlauf sichern.\n", Level);
+ return -1;
+ }
+
+ if(this->Delta <= 0){
+ error("TSkillLevel::GetExpForLevel: Ungültiger Delta-Wert %d.\n", this->Delta);
+ return 0;
+ }
+
+ return ((((Level - 6) * Level + 17) * Level - 12) / 6) * this->Delta;
+}
+
+bool TSkillLevel::Jump(int Range){
+ if(this->Master == NULL){
+ error("TSkillLevel::Jump: GetMaster liefert NULL zurueck!\n");
+ return false;
+ }
+
+ this->Master->Skills[SKILL_HITPOINTS ]->Advance(Range);
+ this->Master->Skills[SKILL_MANA ]->Advance(Range);
+ this->Master->Skills[SKILL_GO_STRENGTH ]->Advance(Range);
+ this->Master->Skills[SKILL_CARRY_WEIGHT]->Advance(Range);
+
+ AnnounceChangedCreature(this->Master->ID, 4);
+ this->Master->Combat.CheckCombatValues();
+
+ if(this->Master->Type == PLAYER){
+ // TODO(fusion): Probably `TSkill::Get` inlined?
+ int Level = this->Act;
+ if(Level < this->Min)
+ Level = this->Min;
+ Level += this->MDAct + this->DAct;
+ // ==
+
+ int FromLevel = Level - Range;
+ int ToLevel = Level;
+ if(Range > 0){
+ SendMessage(this->Master->Connection, TALK_ANONYMOUS_BROADCAST,
+ "You advanced from Level %d to Level %d.", FromLevel, ToLevel);
+ }else if(Range < 0){
+ SendMessage(this->Master->Connection, TALK_ANONYMOUS_BROADCAST,
+ "You were downgraded from Level %d to Level %d.", FromLevel, ToLevel);
+ }
+ }
+
+ return true;
+}
+
+// Creature Functions
+//==============================================================================
+// TODO(fusion): This was the first function I attempted to cleanup but soon
+// realized we should start with the building blocks of the codebase, namely
+// TSkill, TSkillBase, etc...
+// We should probably come back to this once we start doing creature functions
+// again.
+void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay){
+ if(Creature == NULL){
+ error("CheckMana: Übergebene Kreatur existiert nicht.\n");
+ throw ERROR;
+ }
+
+ if(Creature->Type != PLAYER || ManaPoints < 0)
+ return;
+
+ TSkill *Mana = Creature->Skills[SKILL_MANA];
+ if(Mana == NULL){
+ error("CheckMana: Kein Skill MANA!\n");
+ throw ERROR;
+ }
+
+ TSkill *Soul = Creature->Skills[SKILL_SOUL];
+ if(Soul == NULL){
+ error("CheckMana: Kein Skill SOULPOINTS!\n");
+ throw ERROR;
+ }
+
+ if(!CheckRight(Creature->ID, UNLIMITED_MANA)){
+ if(Mana->Get() < ManaPoints)
+ throw NOTENOUGHMANA;
+
+ if(Soul->Get() < SoulPoints)
+ throw NOTENOUGHSOULPOINTS;
+
+ Mana->Change(-ManaPoints);
+ Soul->Change(-SoulPoints);
+ }
+
+ if(ManaPoints > 0){
+ Creature->Skills[SKILL_MAGIC_LEVEL]->Increase(ManaPoints);
+ }
+
+ // NOTE(fusion): Maintain largest exhaust?
+ int EarliestSpellTime = Delay + ServerMilliseconds;
+ if(Creature->EarliestSpellTime < EarliestSpellTime)
+ Creature->EarliestSpellTime = EarliestSpellTime;
+}
diff --git a/src/creature.hh b/src/creature.hh
new file mode 100644
index 0000000..b471662
--- /dev/null
+++ b/src/creature.hh
@@ -0,0 +1,76 @@
+#ifndef TIBIA_CREATURE_HH_
+#define TIBIA_CREATURE_HH_ 1
+
+#include "main.hh"
+
+struct TSkillBase;
+struct TCreature;
+struct TPlayer;
+
+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]
+ //virtual ~TSkill(void); // 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(void) override;
+ void Increase(int Amount) override;
+ void Decrease(int Amount) override;
+ int GetExpForLevel(int Level) override;
+ bool Jump(int Range) override;
+};
+
+#endif //TIBIA_CREATURE_HH_
diff --git a/src/enums.hh b/src/enums.hh
new file mode 100644
index 0000000..8212db1
--- /dev/null
+++ b/src/enums.hh
@@ -0,0 +1,219 @@
+#ifndef TIBIA_ENUMS_HH_
+#define TIBIA_ENUMS_HH_ 1
+
+#include "main.hh"
+
+// TODO(fusion): Probably cleanup these names? Prefix values? Its crazy that
+// there are no collision problems (possibly yet).
+
+enum CreatureType: int {
+ PLAYER = 0,
+ MONSTER = 1,
+ NPC = 2,
+};
+
+enum RESULT: int {
+ ERROR = -1,
+ NOERROR = 0,
+ NOTACCESSIBLE = 1,
+ NOTMOVABLE = 2,
+ NOTTAKABLE = 3,
+ NOROOM = 4,
+ OUTOFRANGE = 5,
+ OUTOFHOME = 6,
+ CANNOTTHROW = 7,
+ TOOHEAVY = 8,
+ CROSSREFERENCE = 9,
+ CONTAINERFULL = 10,
+ WRONGPOSITION = 11,
+ WRONGPOSITION2 = 12,
+ WRONGCLOTHES = 13,
+ HANDSNOTFREE = 14,
+ HANDBLOCKED = 15,
+ ONEWEAPONONLY = 16,
+ NOMATCH = 17,
+ NOTCUMULABLE = 18,
+ TOOMANYPARTS = 19,
+ EMPTYCONTAINER = 20,
+ SPLITOBJECT = 21,
+ NOKEYMATCH = 22,
+ UPSTAIRS = 23,
+ DOWNSTAIRS = 24,
+ CREATURENOTEXISTING = 25,
+ PLAYERNOTEXISTING = 26,
+ PLAYERNOTONLINE = 27,
+ NAMEAMBIGUOUS = 28,
+ NOTUSABLE = 29,
+ FEDUP = 30,
+ DESTROYED = 31,
+ SPELLUNKNOWN = 32,
+ LOWMAGICLEVEL = 33,
+ MAGICITEM = 34,
+ NOTENOUGHMANA = 35,
+ NOSKILL = 36,
+ TARGETLOST = 37,
+ OUTOFAMMO = 38,
+ NOCREATURE = 39,
+ TOOLONG = 40,
+ TARGETOUTOFRANGE = 41,
+ TARGETHIDDEN = 42,
+ ATTACKNOTALLOWED = 43,
+ NOWAY = 44,
+ LOGINERROR = 45,
+ LOGINABORT = 46,
+ PROTECTIONZONE = 47,
+ ENTERPROTECTIONZONE = 48,
+ EXHAUSTED = 49,
+ NOTINVITED = 50,
+ NOPREMIUMACCOUNT = 51,
+ MOVENOTPOSSIBLE = 52,
+ ALREADYTRADING = 53,
+ PARTNERTRADING = 54,
+ TOOMANYOBJECTS = 55,
+ TOOMANYSLAVES = 56,
+ NOTTURNABLE = 57,
+ SECUREMODE = 58,
+ NOTENOUGHSOULPOINTS = 59,
+ LOWLEVEL = 60,
+};
+
+enum RIGHT: int {
+ PREMIUM_ACCOUNT = 0,
+ NOTATION = 1,
+ NAMELOCK = 2,
+ STATEMENT_REPORT = 3,
+ BANISHMENT = 4,
+ FINAL_WARNING = 5,
+ IP_BANISHMENT = 6,
+ KICK = 7,
+ HOME_TELEPORT = 8,
+ GAMEMASTER_BROADCAST = 9,
+ ANONYMOUS_BROADCAST = 10,
+ NO_BANISHMENT = 11,
+ ALLOW_MULTICLIENT = 12,
+ LOG_COMMUNICATION = 13,
+ READ_GAMEMASTER_CHANNEL = 14,
+ READ_TUTOR_CHANNEL = 15,
+ HIGHLIGHT_HELP_CHANNEL = 16,
+ SEND_BUGREPORTS = 17,
+ NAME_INSULTING = 18,
+ NAME_SENTENCE = 19,
+ NAME_NONSENSICAL_LETTERS = 20,
+ NAME_BADLY_FORMATTED = 21,
+ NAME_NO_PERSON = 22,
+ NAME_CELEBRITY = 23,
+ NAME_COUNTRY = 24,
+ NAME_FAKE_IDENTITY = 25,
+ NAME_FAKE_POSITION = 26,
+ STATEMENT_INSULTING = 27,
+ STATEMENT_SPAMMING = 28,
+ STATEMENT_ADVERT_OFFTOPIC = 29,
+ STATEMENT_ADVERT_MONEY = 30,
+ STATEMENT_NON_ENGLISH = 31,
+ STATEMENT_CHANNEL_OFFTOPIC = 32,
+ STATEMENT_VIOLATION_INCITING = 33,
+ CHEATING_BUG_ABUSE = 34,
+ CHEATING_GAME_WEAKNESS = 35,
+ CHEATING_MACRO_USE = 36,
+ CHEATING_MODIFIED_CLIENT = 37,
+ CHEATING_HACKING = 38,
+ CHEATING_MULTI_CLIENT = 39,
+ CHEATING_ACCOUNT_TRADING = 40,
+ CHEATING_ACCOUNT_SHARING = 41,
+ GAMEMASTER_THREATENING = 42,
+ GAMEMASTER_PRETENDING = 43,
+ GAMEMASTER_INFLUENCE = 44,
+ GAMEMASTER_FALSE_REPORTS = 45,
+ KILLING_EXCESSIVE_UNJUSTIFIED = 46,
+ DESTRUCTIVE_BEHAVIOUR = 47,
+ SPOILING_AUCTION = 48,
+ INVALID_PAYMENT = 49,
+ TELEPORT_TO_CHARACTER = 50,
+ TELEPORT_TO_MARK = 51,
+ TELEPORT_VERTICAL = 52,
+ TELEPORT_TO_COORDINATE = 53,
+ LEVITATE = 54,
+ SPECIAL_MOVEUSE = 55,
+ MODIFY_GOSTRENGTH = 56,
+ SHOW_COORDINATE = 57,
+ RETRIEVE = 58,
+ ENTER_HOUSES = 59,
+ OPEN_NAMEDOORS = 60,
+ INVULNERABLE = 61,
+ UNLIMITED_MANA = 62,
+ KEEP_INVENTORY = 63,
+ ALL_SPELLS = 64,
+ UNLIMITED_CAPACITY = 65,
+ ZERO_CAPACITY = 66,
+ ATTACK_EVERYWHERE = 67,
+ NO_ATTACK = 68,
+ NO_RUNES = 69,
+ NO_LOGOUT_BLOCK = 70,
+ GAMEMASTER_OUTFIT = 71,
+ ILLUMINATE = 72,
+ CHANGE_PROFESSION = 73,
+ IGNORED_BY_MONSTERS = 74,
+ SHOW_KEYHOLE_NUMBERS = 75,
+ CREATE_OBJECTS = 76,
+ CREATE_MONEY = 77,
+ CREATE_MONSTERS = 78,
+ CHANGE_SKILLS = 79,
+ CLEANUP_FIELDS = 80,
+ NO_STATISTICS = 81,
+};
+
+// NOTE(fusion): This didn't exist int the decompiled version but is handy when
+// accessing a creature's `Skills` table.
+enum Skill: int {
+ SKILL_LEVEL = 0,
+ SKILL_MAGIC_LEVEL = 1,
+ SKILL_HITPOINTS = 2,
+ SKILL_MANA = 3,
+ SKILL_GO_STRENGTH = 4, // speed?
+ SKILL_CARRY_WEIGHT = 5, // capacity?
+ SKILL_FIST = 6,
+ SKILL_CLUB = 7,
+ SKILL_SWORD = 8,
+ SKILL_AXE = 9,
+ SKILL_DISTANCE = 10,
+ SKILL_SHIELDING = 11,
+ // 12?
+ SKILL_FISHING = 13,
+ SKILL_FED = 14,
+ SKILL_LIGHT = 15,
+ SKILL_ILLUSION = 16,
+ SKILL_POISON = 17,
+ SKILL_BURNING = 18,
+ SKILL_ENERGY = 19,
+ // 20?
+ // 21?
+ SKILL_SOUL = 22,
+};
+
+enum TalkMode: int {
+ TALK_SAY = 1,
+ TALK_WHISPER = 2,
+ TALK_YELL = 3,
+ TALK_PRIVATE_MESSAGE = 4,
+ TALK_CHANNEL_CALL = 5,
+ TALK_GAMEMASTER_REQUEST = 6,
+ TALK_GAMEMASTER_ANSWER = 7,
+ TALK_PLAYER_ANSWER = 8,
+ TALK_GAMEMASTER_BROADCAST = 9,
+ TALK_GAMEMASTER_CHANNELCALL = 10,
+ TALK_GAMEMASTER_MESSAGE = 11,
+ TALK_HIGHLIGHT_CHANNELCALL = 12,
+ TALK_ANONYMOUS_BROADCAST = 13,
+ TALK_ANONYMOUS_CHANNELCALL = 14,
+ TALK_ANONYMOUS_MESSAGE = 15,
+ TALK_ANIMAL_LOW = 16,
+ TALK_ANIMAL_LOUD = 17,
+ TALK_ADMIN_MESSAGE = 18,
+ TALK_EVENT_MESSAGE = 19,
+ TALK_LOGIN_MESSAGE = 20,
+ TALK_STATUS_MESSAGE = 21,
+ TALK_INFO_MESSAGE = 22,
+ TALK_FAILURE_MESSAGE = 23,
+};
+
+#endif //TIBIA_ENUMS_HH_
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..e616ce3
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,38 @@
+#include "main.hh"
+
+#if 0
+// TODO(fusion): All globals should probably be packed into a `GlobalState`
+// structure to minimize collisions and bugs.
+struct GlobalState{
+ // world state
+ // creatures
+ // items
+ // etc...
+};
+
+GlobalState G = {};
+#endif
+
+// TODO(fusion): Probably current server time. Used for delays, timers, etc.
+int ServerMilliseconds = 0;
+
+void (*ErrorFunction)(const char*) = NULL;
+
+void error(char *Text, ...){
+ char s[1024];
+
+ va_list ap;
+ va_start(ap, Text);
+ vsnprintf(s, sizeof(s) - 1, Text, ap);
+ va_end(ap);
+
+ if(ErrorFunction){
+ ErrorFunction(s);
+ }else{
+ printf("%s", s);
+ }
+}
+
+int main(int argc, char **argv){
+ return 0;
+}
diff --git a/src/main.hh b/src/main.hh
new file mode 100644
index 0000000..ad4f3ca
--- /dev/null
+++ b/src/main.hh
@@ -0,0 +1,78 @@
+#ifndef TIBIA_MAIN_HH_
+#define TIBIA_MAIN_HH_ 1
+
+#include <float.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uintptr_t uintptr;
+typedef size_t usize;
+
+#define STATIC_ASSERT(expr) static_assert((expr), "static assertion failed: " #expr)
+#define NARRAY(arr) (sizeof(arr) / sizeof(arr[0]))
+#define ISPOW2(x) ((x) != 0 && ((x) & ((x) - 1)) == 0)
+#define KB(x) ((usize)(x) << 10)
+#define MB(x) ((usize)(x) << 20)
+#define GB(x) ((usize)(x) << 30)
+
+// TODO(fusion): We might not actually compile the output for the decompilation
+// step but if we did, we'd need to make sure we're compiling in 32 bits mode.
+STATIC_ASSERT(sizeof(bool) == 1);
+STATIC_ASSERT(sizeof(int) == 4);
+//STATIC_ASSERT(sizeof(void*) == 4);
+
+#if !OS_WINDOWS && !OS_LINUX
+# if defined(_WIN32)
+# define OS_WINDOWS 1
+# elif defined(__linux__) || defined(__gnu_linux__)
+# define OS_LINUX 1
+# else
+# error "Operating system not supported."
+# endif
+#endif
+
+#if !COMPILER_MSVC && !COMPILER_GCC && !COMPILER_CLANG
+# if defined(_MSC_VER)
+# define COMPILER_MSVC 1
+# elif defined(__GNUC__)
+# define COMPILER_GCC 1
+# elif defined(__clang__)
+# define COMPILER_CLANG 1
+# endif
+#endif
+
+// NOTE(fusion): Compiler attributes.
+#if COMPILER_GCC || COMPILER_CLANG
+# define ATTR_FALLTHROUGH __attribute__((fallthrough))
+# define ATTR_PRINTF(x, y) __attribute__((format(printf, x, y)))
+#else
+# define ATTR_FALLTHROUGH
+# define ATTR_PRINTF(x, y)
+#endif
+
+#if COMPILER_MSVC
+# define TRAP() __debugbreak();
+#elif COMPILER_GCC || COMPILER_CLANG
+# define TRAP() __builtin_trap()
+#else
+# define TRAP() abort()
+#endif
+
+#define ASSERT_ALWAYS(expr) if(!(expr)) { TRAP(); }
+#if BUILD_DEBUG
+# define ASSERT(expr) ASSERT_ALWAYS(expr)
+#else
+# define ASSERT(expr) ((void)(expr))
+#endif
+
+void error(char *Text, ...) ATTR_PRINTF(1, 2);
+
+#endif //TIBIA_MAIN_HH_