aboutsummaryrefslogtreecommitdiff
path: root/src/creature.cc
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/creature.cc
downloadgame-717f5cd9dacf30fe6bf1561db9297e75fd27fdef.tar.gz
game-717f5cd9dacf30fe6bf1561db9297e75fd27fdef.zip
initial commit - getting at TSkill structs
Diffstat (limited to 'src/creature.cc')
-rw-r--r--src/creature.cc429
1 files changed, 429 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;
+}