From 717f5cd9dacf30fe6bf1561db9297e75fd27fdef Mon Sep 17 00:00:00 2001 From: fusion32 Date: Fri, 16 May 2025 18:46:18 -0300 Subject: initial commit - getting at TSkill structs --- src/creature.cc | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 src/creature.cc (limited to 'src/creature.cc') 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; +} -- cgit v1.2.3