diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-05-30 10:32:29 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-05-30 10:32:29 -0300 |
| commit | dff5e62f6ebb1b06c234b9144458788893b6ca86 (patch) | |
| tree | 828c51278c906ad9e71dac3b54b7246a0c04706d | |
| parent | f6c8c67094de3fe19f1e7871a7e78a91fb9bf3dd (diff) | |
| download | game-dff5e62f6ebb1b06c234b9144458788893b6ca86.tar.gz game-dff5e62f6ebb1b06c234b9144458788893b6ca86.zip | |
initial work on `crcombat.cc`
| -rw-r--r-- | TODO.md | 4 | ||||
| -rw-r--r-- | src/crcombat.cc | 243 | ||||
| -rw-r--r-- | src/crcombat.hh | 8 | ||||
| -rw-r--r-- | src/creature.cc | 2 | ||||
| -rw-r--r-- | src/creature.hh | 1 | ||||
| -rw-r--r-- | src/player.cc | 27 | ||||
| -rw-r--r-- | src/player.hh | 8 | ||||
| -rw-r--r-- | src/stubs.hh | 3 |
8 files changed, 292 insertions, 4 deletions
@@ -1,9 +1,11 @@ ## TODO NEXT -- STRINGS.CC - TCreature - TPlayer - TNonPlayer +## 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. + ## Estimate 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. diff --git a/src/crcombat.cc b/src/crcombat.cc index 854ffe2..de6385a 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -1,4 +1,8 @@ #include "creature.hh" +#include "player.hh" +#include "config.hh" + +#include "stubs.hh" TCombat::TCombat(void){ this->Master = NULL; @@ -28,4 +32,241 @@ TCombat::TCombat(void){ this->LearningPoints = 0; } -// TODO(fusion): Probably better to figure out how Object work. +void TCombat::GetWeapon(void){ + this->Shield = NONE; + this->Close = NONE; + this->Missile = NONE; + this->Throw = NONE; + this->Wand = NONE; + this->Ammo = NONE; + this->Fist = true; + + // TODO(fusion): Check if `Master` is NULL? + TCreature *Master = this->Master; + if(!Master->CrObject.exists()){ + error("TCombat::GetWeapon: Kreatur-Objekt existiert nicht.\n"); + return; + } + + // TODO(fusion): We're iterating over left and right hand slots. Make + // it clearer perhaps. + for(int Hand = 5; Hand <= 6; Hand += 1){ + Object Obj = GetBodyObject(Master->ID, Hand); + if(Obj == NONE){ + continue; + } + + ObjectType ObjType = Obj.getObjectType(); + + if(ObjType.getFlag(RESTRICTLEVEL)){ + int CurrentLevel = Master->Skills[SKILL_LEVEL]->Get(); + int MinimumLevel = (int)ObjType.getAttribute(MINIMUMLEVEL); + if(CurrentLevel < MinimumLevel){ + continue; + } + } + + if(ObjType.getFlag(RESTRICTPROFESSION) && Master->Type == PLAYER){ + uint32 ProfessionMask = ObjType.getAttribute(PROFESSIONS); + uint8 Profession = ((TPlayer*)Master)->GetEffectiveProfession(); + if((ProfessionMask & (1 << Profession)) == 0){ + continue; + } + } + + if(ObjType.getFlag(SHIELD)){ + this->Shield = Obj; + } + + if(ObjType.getFlag(WEAPON)){ + this->Close = Obj; + this->Fist = false; + } + + if(ObjType.getFlag(BOW)){ + this->Missile = Obj; + this->Fist = false; + } + + if(ObjType.getFlag(THROW)){ + this->Throw = Obj; + this->Fist = false; + } + + if(ObjType.getFlag(WAND)){ + this->Wand = Obj; + this->Fist = false; + } + } +} + +void TCombat::GetAmmo(void){ + if(this->Missile == NONE){ + if(this->Throw != NONE){ + this->Ammo = this->Throw; + }else if(this->Wand != NONE){ + this->Ammo = this->Wand; + } + return; + } + + // TODO(fusion): Check if `Master` is NULL? + Object Ammo = GetBodyObject(this->Master->ID, 10); + this->Ammo = NONE; + if(Ammo != NONE){ + ObjectType AmmoType = Ammo.getObjectType(); + if(AmmoType.getFlag(AMMO)){ + ObjectType BowType = this->Missile.getObjectType(); + if(AmmoType.getAttribute(AMMOTYPE) == BowType.getAttribute(BOWAMMOTYPE)){ + this->Ammo = Ammo; + } + } + } +} + +void TCombat::CheckCombatValues(void){ + Object OldShield = this->Shield; + Object OldClose = this->Close; + Object OldMissile = this->Missile; + Object OldThrow = this->Throw; + Object OldWand = this->Wand; + Object OldAmmo = this->Ammo; + bool OldFist = this->Fist; + + this->GetWeapon(); + this->GetAmmo(); + if(OldShield != this->Shield + || OldClose != this->Close + || OldMissile != this->Missile + || OldThrow != this->Throw + || OldWand != this->Wand + || OldAmmo != this->Ammo + || OldFist != this->Fist){ + this->DelayAttack(2000); + } +} + +void TCombat::Attack(void){ + if(this->AttackDest == 0 || this->Following){ + return; + } + + TCreature *Master = this->Master; + if(Master == NULL){ + error("TCombat::CanAttack: Kein Master gesetzt!\n"); + throw ERROR; + } + + TCreature *Target = GetCreature(this->AttackDest); + if(Target == NULL){ + this->StopAttack(0); + throw TARGETLOST; + } + + // NOTE(fusion): This probably has something to do with calling `StopAttack` + // with a non zero delay. + if(this->LatestAttackTime != 0 && this->LatestAttackTime < RoundNr){ + this->StopAttack(0); + return; + } + + if(Master->Type == PLAYER && Target->Type != PLAYER){ + if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + this->StopAttack(0); + throw TARGETLOST; + } + } + + if(Master->Type == PLAYER && Target->Type == PLAYER){ + if(this->SecureMode == 1 && WorldType == NORMAL + && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){ + this->StopAttack(0); + throw SECUREMODE; + } + } + + // TODO(fusion): It is weird that max attack distance is hardcoded. + // Actually, it is related to the maximum visible distance on a + // creature's viewport. + int Distance = ObjectDistance(Master->CrObject, Target->CrObject); + if(Distance > 8){ + this->StopAttack(0); + throw TARGETLOST; + } + + if(Master->Type == PLAYER && Target->Type == PLAYER){ + if(((TPlayer*)Master)->GetRealProfession() == PROFESSION_NONE + && !CheckRight(Master->ID, ATTACK_EVERYWHERE)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + + if(Master->Type == PLAYER){ + if(CheckRight(Master->ID, NO_ATTACK)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + + if(IsProtectionZone(Master->posx, Master->posy, Master->posz) + || IsProtectionZone(Target->posx, Target->posy, Target->posz)){ + this->StopAttack(0); + throw PROTECTIONZONE; + } + + Master->BlockLogout(60, Target->Type == PLAYER); + Target->BlockLogout(60, false); + + if(Master->Type == PLAYER && Target->Type == PLAYER){ + ((TPlayer*)Master)->RecordAttack(Target->ID); + } + + this->DelayAttack(200); + + // TODO(fusion): This `Range` value doesn't make a lot of sense. + int Range = this->GetDistance(); + if(Range == 1){ + if(Distance > 1){ + throw TARGETOUTOFRANGE; + } + this->CloseAttack(Target); + }else{ + if(Range < 1 || Range > 3){ + throw ERROR; + } + + // TODO(fusion): These are related to the maximum visible distance on + // each coordinate. + if(std::abs(Master->posx - Target->posx) > 7 + || std::abs(Master->posy - Target->posy) > 5){ + throw TARGETOUTOFRANGE; + } + + this->RangeAttack(Target); + } + + this->DelayAttack(2000); + + if(Target->IsDead){ + this->StopAttack(0); + } +} + +void TCombat::StopAttack(int Delay){ + if(Delay == 0){ + this->AttackDest = 0; + if(this->Master->Type == PLAYER){ + SendClearTarget(this->Master->Connection); + } + }else{ + this->LatestAttackTime = RoundNr + Delay; + } +} + +void TCombat::DelayAttack(int Milliseconds){ + uint32 EarliestAttackTime = ServerMilliseconds + Milliseconds; + if(this->EarliestAttackTime < EarliestAttackTime){ + this->EarliestAttackTime = EarliestAttackTime; + } +} diff --git a/src/crcombat.hh b/src/crcombat.hh index ce75002..f015252 100644 --- a/src/crcombat.hh +++ b/src/crcombat.hh @@ -13,7 +13,15 @@ struct TCombat{ // REGULAR FUNCTIONS // ========================================================================= TCombat(void); + void GetWeapon(void); + void GetAmmo(void); void CheckCombatValues(void); + int GetDistance(void); // TODO + void Attack(void); + void StopAttack(int Delay); + void DelayAttack(int Milliseconds); + void CloseAttack(TCreature *Target); // TODO + void RangeAttack(TCreature *Target); // TODO // DATA // ========================================================================= diff --git a/src/creature.cc b/src/creature.cc index 549543e..119d0e7 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -97,7 +97,7 @@ void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay){ } // NOTE(fusion): Maintain largest exhaust? - uint32 EarliestSpellTime = Delay + ServerMilliseconds; + uint32 EarliestSpellTime = ServerMilliseconds + Delay; if(Creature->EarliestSpellTime < EarliestSpellTime){ Creature->EarliestSpellTime = EarliestSpellTime; } diff --git a/src/creature.hh b/src/creature.hh index bafc55e..69f0ef2 100644 --- a/src/creature.hh +++ b/src/creature.hh @@ -75,6 +75,7 @@ struct TCreature: TSkillBase { // ========================================================================= TCreature(void); int Damage(TCreature *Attacker, int Damage, int DamageType); + void BlockLogout(int Delay, bool BlockProtectionZone); // VIRTUAL FUNCTIONS // ========================================================================= diff --git a/src/player.cc b/src/player.cc index 0ee78f3..04298c6 100644 --- a/src/player.cc +++ b/src/player.cc @@ -3,6 +3,33 @@ #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; diff --git a/src/player.hh b/src/player.hh index bb58ac3..815289e 100644 --- a/src/player.hh +++ b/src/player.hh @@ -68,8 +68,14 @@ struct TPlayerData { struct TPlayer: TCreature { // REGULAR FUNCTIONS // ========================================================================= - void CheckState(void); + uint8 GetRealProfession(void); + uint8 GetEffectiveProfession(void); uint8 GetActiveProfession(void); + bool GetActivePromotion(void); + bool IsAttackJustified(uint32 Victim); + void RecordAttack(uint32 Victim); + + void CheckState(void); // VIRTUAL FUNCTIONS // ========================================================================= diff --git a/src/stubs.hh b/src/stubs.hh index 372ca92..6eb3d3d 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -21,6 +21,7 @@ extern void Change(Object Obj, ObjectType NewType, uint32 Value); extern bool CheckRight(uint32 CreatureID, RIGHT Right); extern void CleanHouseField(int x, int y, int z); extern void CreatePlayerList(bool Online); +extern Object GetBodyObject(uint32 CreatureID, int Position); extern TCreature *GetCreature(uint32 CreatureID); extern TConnection *GetFirstConnection(void); extern TConnection *GetNextConnection(void); @@ -31,6 +32,7 @@ extern void MoveCreatures(int Delay); extern void NetLoadCheck(void); extern void NetLoadSummary(void); extern void NotifyAllCreatures(Object Obj, int Type, Object OldCon); +extern int ObjectDistance(Object Obj1, Object Obj2); extern void ProcessCommunicationControl(void); extern void ProcessConnections(void); extern void ProcessCreatures(void); @@ -47,6 +49,7 @@ extern void RefreshSector(int SectorX, int SectorY, int SectorZ, const uint8 *Da extern void SavePlayerDataOrder(void); extern void SendAll(void); extern void SendAmbiente(TConnection *Connection); +extern void SendClearTarget(TConnection *Connection); extern void SendMails(TPlayerData *PlayerData); extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ...) ATTR_PRINTF(3, 4); extern void SendPlayerData(TConnection *Connection); |
