diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-05-30 20:12:36 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-05-30 20:12:36 -0300 |
| commit | 08780dad536eb5d0544eb2ea70e5443dc237976f (patch) | |
| tree | 673a57b538ca9016d5b386ff32c3bd67c2190c2f | |
| parent | dff5e62f6ebb1b06c234b9144458788893b6ca86 (diff) | |
| download | game-08780dad536eb5d0544eb2ea70e5443dc237976f.tar.gz game-08780dad536eb5d0544eb2ea70e5443dc237976f.zip | |
more work on `crcombat.cc`
| -rw-r--r-- | reference/types.hh | 61 | ||||
| -rw-r--r-- | src/crcombat.cc | 417 | ||||
| -rw-r--r-- | src/crcombat.hh | 17 | ||||
| -rw-r--r-- | src/creature.cc | 52 | ||||
| -rw-r--r-- | src/creature.hh | 16 | ||||
| -rw-r--r-- | src/crskill.cc | 18 | ||||
| -rw-r--r-- | src/enums.hh | 63 | ||||
| -rw-r--r-- | src/magic.cc | 74 | ||||
| -rw-r--r-- | src/magic.hh | 90 | ||||
| -rw-r--r-- | src/stubs.hh | 12 |
10 files changed, 685 insertions, 135 deletions
diff --git a/reference/types.hh b/reference/types.hh index b87faf2..2147440 100644 --- a/reference/types.hh +++ b/reference/types.hh @@ -1,65 +1,4 @@ - -struct TImpact { - int (**_vptr.TImpact)(...); // VTABLE? -}; - -struct TSummonImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int Race; - int Maximum; -}; - -struct TSpeedImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int Percent; - int Duration; -}; - -struct THealingImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int Power; -}; - -struct TOutfitImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - TOutfit Outfit; - int Duration; -}; - -struct TFieldImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int FieldType; -}; - -struct TDrunkenImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int Power; - int Duration; -}; - -struct TStrengthImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int Skills; - int Percent; - int Duration; -}; - -struct TDamageImpact { - TImpact super_TImpact; // INHERITANCE? - TCreature *Actor; - int DamageType; - int Power; - bool AllowDefense; -}; - struct TCircle { int x[32]; int y[32]; diff --git a/src/crcombat.cc b/src/crcombat.cc index de6385a..256f056 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -1,6 +1,7 @@ #include "creature.hh" #include "player.hh" #include "config.hh" +#include "magic.hh" #include "stubs.hh" @@ -10,8 +11,8 @@ TCombat::TCombat(void){ this->EarliestDefendTime = 0; this->LastDefendTime = 0; this->LatestAttackTime = 0; - this->AttackMode = 2; - this->ChaseMode = 0; + this->AttackMode = ATTACK_MODE_BALANCED; + this->ChaseMode = CHASE_MODE_NONE; this->SecureMode = 1; this->AttackDest = 0; this->Following = false; @@ -146,11 +147,184 @@ void TCombat::CheckCombatValues(void){ } } +int TCombat::GetDistance(void){ + int Distance = 0; + if(this->Close != NONE || this->Fist){ + Distance = 1; + }else if(this->Throw != NONE){ + Distance = 2; + }else if(this->Missile != NONE || this->Wand != NONE){ + Distance = 3; + } + return Distance; +} + +void TCombat::SetAttackDest(uint32 TargetID, bool Follow){ + if(this->AttackDest == TargetID && this->Following == Follow){ + return; + } + + TCreature *Master = this->Master; + if(TargetID == 0 || TargetID == Master->ID){ + this->StopAttack(0); + return; + } + + TCreature *Target = GetCreature(TargetID); + if(Target == NULL){ + this->StopAttack(0); + return; + } + + if(!Follow){ + if(Master->Type == PLAYER && Target->Type == PLAYER){ + if(this->SecureMode == 1 && WorldType == NORMAL + && !((TPlayer*)Master)->IsAttackJustified(TargetID)){ + this->StopAttack(0); + throw SECUREMODE; + } + } + + if(Master->Type == PLAYER && !CheckRight(Master->ID, ATTACK_EVERYWHERE)){ + if(IsProtectionZone(Master->posx, Master->posy, Master->posz) + || IsProtectionZone(Target->posx, Target->posy, Target->posz)){ + this->StopAttack(0); + throw PROTECTIONZONE; + } + } + + if(Master->Type == PLAYER && CheckRight(Master->ID, NO_ATTACK)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + + if(Master->Type == PLAYER && Target->Type == PLAYER){ + if(((TPlayer*)Master)->GetRealProfession() == PROFESSION_NONE + && !CheckRight(Master->ID, ATTACK_EVERYWHERE)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + + if(WorldType == NON_PVP){ + if(!Master->IsPeaceful() || !Target->IsPeaceful()){ + if(Target->Type == NPC){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + + if(Master->Type == PLAYER){ + if(Target->Type == NPC || !CheckRight(Master->ID, ATTACK_EVERYWHERE)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + } + } + + if(Master->Type == PLAYER && Target->Type != PLAYER){ + if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + this->StopAttack(0); + throw TARGETLOST; + } + } + + int Distance = ObjectDistance(Master->CrObject, Target->CrObject); + if(Distance > 8){ + this->StopAttack(0); + throw TARGETLOST; + } + + this->AttackDest = TargetID; + this->Following = Follow; + if(!Follow){ + Target->AttackStimulus(Master->ID); + Master->BlockLogout(60, Target->Type == PLAYER); + if(Master->Type == PLAYER && Target->Type == PLAYER){ + ((TPlayer*)Master)->RecordAttack(TargetID); + } + this->LatestAttackTime = 0; + } +} + +void TCombat::CanToDoAttack(void){ + if(this->AttackDest == 0){ + return; + } + + // TODO(fusion): There is some `CanAttack` function inlined here. + 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; + } + + if(Master->Type == PLAYER && Target->Type != PLAYER){ + if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + this->StopAttack(0); + throw TARGETLOST; + } + } + + if(!this->Following){ + if(Master->Type == PLAYER && Target->Type == PLAYER){ + if(this->SecureMode == 1 && WorldType == NORMAL + && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){ + this->StopAttack(0); + throw SECUREMODE; + } + } + + if(WorldType == NON_PVP){ + if(Master->IsPeaceful() && Target->IsPeaceful()){ + if(Master->Type != PLAYER || !CheckRight(Master->ID, ATTACK_EVERYWHERE)){ + this->StopAttack(0); + throw ATTACKNOTALLOWED; + } + } + } + } + + int Distance = ObjectDistance(Master->CrObject, Target->CrObject); + if(Distance > 8){ + this->StopAttack(0); + throw TARGETLOST; + } + + int ChaseMode = this->ChaseMode; + if(this->Following){ + ChaseMode = CHASE_MODE_CLOSE; + } + + if(ChaseMode == CHASE_MODE_CLOSE){ + if(Distance > 1){ + Master->ToDoGo(Target->posx, Target->posy, Target->posz, false, 3); + } + }else if(ChaseMode == CHASE_MODE_RANGE){ + if(Distance > 4){ + Master->ToDoGo(Target->posx, Target->posy, Target->posz, false, Distance - 4); + }else if(Distance < 4){ + int DestX, DestY, DestZ; + if(SearchFlightField(Master->ID, Target->ID, &DestX, &DestY, &DestZ)){ + Master->ToDoGo(DestX, DestY, DestZ, true, -1); + } + } + } +} + void TCombat::Attack(void){ if(this->AttackDest == 0 || this->Following){ return; } + // TODO(fusion): There is some `CanAttack` function inlined here. TCreature *Master = this->Master; if(Master == NULL){ error("TCombat::CanAttack: Kein Master gesetzt!\n"); @@ -232,7 +406,7 @@ void TCombat::Attack(void){ } this->CloseAttack(Target); }else{ - if(Range < 1 || Range > 3){ + if(Range < 2 || Range > 3){ throw ERROR; } @@ -243,7 +417,16 @@ void TCombat::Attack(void){ throw TARGETOUTOFRANGE; } - this->RangeAttack(Target); + // NOTE(fusion): Originally, this was a single function `RangeAttack` + // and I quickly realized that readability would be massively improved + // by splitting it into `DistanceAttack` and `WandAttack`. + if(this->Missile != NONE || this->Throw != NONE){ + this->DistanceAttack(Target); + }else if(this->Wand != NONE){ + this->WandAttack(Target); + }else{ + throw ERROR; + } } this->DelayAttack(2000); @@ -270,3 +453,229 @@ void TCombat::DelayAttack(int Milliseconds){ this->EarliestAttackTime = EarliestAttackTime; } } + +void TCombat::CloseAttack(TCreature *Target){ + int Attack; + uint16 SkillNr; + this->GetAttackValue(&Attack, &SkillNr); + if(this->AttackMode == ATTACK_MODE_OFFENSIVE){ + Attack += (Attack * 2) / 10; + }else if(this->AttackMode == ATTACK_MODE_DEFENSIVE){ + Attack -= (Attack * 4) / 10; + } + + TCreature *Master = this->Master; + Attack = Master->Skills[SkillNr]->ProbeValue(Attack, this->LearningPoints > 0); + if(this->LearningPoints > 0){ + this->LearningPoints -= 1; + } + + int Defense = Target->Combat.GetDefendDamage(); + int Damage = Attack - Defense; + if(Damage < 0){ + Damage = 0; + } + + int DamageDone = Target->Damage(Master, Damage, 1); // DAMAGE_PHYSICAL ? + if(DamageDone > 0){ + this->LearningPoints = 30; + } + + int Poison = GetRacePoison(Master->Race); + if(Poison != 0){ + if(DamageDone > 0 || (Attack > Defense && (rand() % 5) == 0)){ + int PoisonDamage = random(Poison / 2, Poison); + if(PoisonDamage > 0){ + Target->Damage(Master, PoisonDamage, DAMAGE_POISON_PERIODIC); + if(Target->Type == PLAYER){ + SendMessage(Target->Connection, TALK_STATUS_MESSAGE, "You are poisoned."); + } + } + } + } + + Object Close = this->Close; + if(Close != NONE){ + ObjectType CloseType = Close.getObjectType(); + if(CloseType.getFlag(WEAROUT)){ + uint32 RemainingUses = Close.getAttribute(REMAININGUSES); + if(RemainingUses > 1){ + Change(Close, REMAININGUSES, RemainingUses - 1); + }else{ + ObjectType WearOutType = CloseType.getAttribute(WEAROUTTARGET); + Change(Close, WearOutType, 0); + this->CheckCombatValues(); + } + } + } +} + +void TCombat::WandAttack(TCreature *Target){ + ASSERT(this->Wand != NONE); + + TCreature *Master = this->Master; + int Distance = std::min<int>( + std::abs(Master->posx - Target->posx), + std::abs(Master->posy - Target->posy)); + + ObjectType WandType = this->Wand.getObjectType(); + if(Distance > (int)WandType.getAttribute(WANDRANGE)){ + throw TARGETOUTOFRANGE; + } + + if(!ThrowPossible(Master->posx, Master->posy, Master->posz, + Target->posx, Target->posy, Target->posz, 0)){ + throw TARGETHIDDEN; + } + + int DamageType = WandType.getAttribute(WANDDAMAGETYPE); + int AnimType = WandType.getAttribute(WANDMISSILE); + int ManaConsumption = WandType.getAttribute(WANDMANACONSUMPTION); + int AttackStrength = WandType.getAttribute(WANDATTACKSTRENGTH); + int AttackVariation = (int)WandType.getAttribute(WANDATTACKVARIATION); + + // NOTE(fusion): Oof... + try{ + CheckMana(Master, ManaConsumption, 0, 0); + }catch(RESULT err){ + if(err == NOTENOUGHMANA){ + throw OUTOFAMMO; + }else{ + throw err; + } + } + + int Damage = AttackStrength + random(-AttackVariation, AttackVariation); + if(Target->Damage(Master, Damage, DamageType) > 0){ + this->LearningPoints = 30; + } + + ::Missile(Master->CrObject, Target->CrObject, AnimType); +} + +void TCombat::DistanceAttack(TCreature *Target){ + ASSERT(this->Missile != NONE || this->Throw != NONE); + + int HitChance; + int DamageType; + int AnimType; + int Fragility; + int SpecialEffect; + int EffectStrength; + + TCreature *Master = this->Master; + int DistanceX = std::abs(Master->posx - Target->posx); + int DistanceY = std::abs(Master->posy - Target->posy); + int Distance = std::max<int>(DistanceX, DistanceY); + + if(this->Missile != NONE){ + this->GetAmmo(); + if(this->Ammo == NONE || !this->Ammo.exists()){ + throw OUTOFAMMO; + } + + ObjectType BowType = this->Missile.getObjectType(); + ObjectType AmmoType = this->Ammo.getObjectType(); + if(Distance > (int)BowType.getAttribute(BOWRANGE)){ + throw TARGETOUTOFRANGE; + } + + HitChance = 90; + DamageType = DAMAGE_PHYSICAL; + AnimType = AmmoType.getAttribute(AMMOMISSILE); + Fragility = 100; + SpecialEffect = AmmoType.getAttribute(AMMOSPECIALEFFECT); + EffectStrength = AmmoType.getAttribute(AMMOEFFECTSTRENGTH); + }else{ + ASSERT(this->Throw != NONE); + ObjectType ThrowType = this->Throw.getObjectType(); + if(Distance > (int)ThrowType.getAttribute(THROWRANGE)){ + throw TARGETOUTOFRANGE; + } + + HitChance = 75; + DamageType = DAMAGE_PHYSICAL; + AnimType = ThrowType.getAttribute(THROWMISSILE); + Fragility = ThrowType.getAttribute(THROWFRAGILITY); + SpecialEffect = ThrowType.getAttribute(THROWSPECIALEFFECT); + EffectStrength = ThrowType.getAttribute(THROWEFFECTSTRENGTH); + } + + if(!ThrowPossible(Master->posx, Master->posy, Master->posz, + Target->posx, Target->posy, Target->posz, 0)){ + throw TARGETHIDDEN; + } + + int Difficulty = (Distance >= 2) ? Distance : 5; + bool Hit = Master->Skills[SKILL_DISTANCE]->Probe( + Difficulty * 15, HitChance, this->LearningPoints > 0); + if(this->LearningPoints > 0){ + this->LearningPoints -= 1; + } + + int DropX = Target->posx; + int DropY = Target->posy; + int DropZ = Target->posz; + if(Hit){ + int Attack; + uint16 SkillNr; + this->GetAttackValue(&Attack, &SkillNr); + if(this->AttackMode == ATTACK_MODE_OFFENSIVE){ + Attack += (Attack * 2) / 10; + }else if(this->AttackMode == ATTACK_MODE_DEFENSIVE){ + Attack -= (Attack * 4) / 10; + } + + Attack = Master->Skills[SkillNr]->ProbeValue(Attack, this->LearningPoints > 0); + if(this->LearningPoints > 0){ + this->LearningPoints -= 1; + } + + // TODO(fusion): This doesn't make any sense. The disassembly looks + // correct but I feel we're missing something here. Or maybe defense + // doesn't work with ranged attacks but worn them out? + if(this->Shield != NONE){ + Target->Combat.GetDefendDamage(); + } + + if(Target->Damage(Master, Attack, DamageType) > 0){ + this->LearningPoints = 30; + } + }else{ + if(DistanceX > 1 || DistanceY > 1){ + DropX += (rand() % 3) - 1; + DropY += (rand() % 3) - 1; + } + + if(!CoordinateFlag(DropX, DropY, DropZ, BANK) + || CoordinateFlag(DropX, DropY, DropZ, UNLAY) + || !ThrowPossible(Master->posx, Master->posy, Master->posz, DropX, DropY, DropZ, 0)){ + DropX = Target->posx; + DropY = Target->posy; + } + } + + Object DropCon = GetMapContainer(DropX, DropY, DropZ); + ::Missile(Master->CrObject, DropCon, AnimType); + + if(SpecialEffect == 1){ // POISON ARROW + if(Hit){ + Target->Damage(Master, EffectStrength, DAMAGE_POISON_PERIODIC); + } + }else if(SpecialEffect == 2){ // BURST ARROW + int Damage = ComputeDamage(Master, 0, EffectStrength, EffectStrength); + TDamageImpact Impact(Master, DAMAGE_PHYSICAL, Damage, false); + CircleShapeSpell(Master, DropX, DropY, DropZ, -1, + ANIMATION_NONE, 2, &Impact, EFFECT_BURST_ARROW); + } + + if(random(0, 99) < Fragility){ + Delete(this->Ammo, 1); + }else{ + Move(0, this->Ammo, DropCon, 1, false, NONE); + } + + if(!Hit){ + GraphicalEffect(DropX, DropY, DropZ, EFFECT_POFF); + } +} diff --git a/src/crcombat.hh b/src/crcombat.hh index f015252..bd22584 100644 --- a/src/crcombat.hh +++ b/src/crcombat.hh @@ -16,12 +16,17 @@ struct TCombat{ void GetWeapon(void); void GetAmmo(void); void CheckCombatValues(void); - int GetDistance(void); // TODO + int GetDistance(void); + void GetAttackValue(int *Attack, uint16 *SkillNr); + int GetDefendDamage(void); + void SetAttackDest(uint32 TargetID, bool Follow); + void CanToDoAttack(void); void Attack(void); void StopAttack(int Delay); void DelayAttack(int Milliseconds); - void CloseAttack(TCreature *Target); // TODO - void RangeAttack(TCreature *Target); // TODO + void CloseAttack(TCreature *Target); + void DistanceAttack(TCreature *Target); + void WandAttack(TCreature *Target); // DATA // ========================================================================= @@ -30,9 +35,9 @@ struct TCombat{ uint32 EarliestDefendTime; uint32 LastDefendTime; uint32 LatestAttackTime; - uint32 AttackMode; - uint32 ChaseMode; - uint32 SecureMode; + uint8 AttackMode; + uint8 ChaseMode; + uint8 SecureMode; uint32 AttackDest; bool Following; Object Shield; diff --git a/src/creature.cc b/src/creature.cc index 119d0e7..a02cb82 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -51,54 +51,6 @@ TCreature::TCreature(void) : } } -// TODO(fusion): Probably better to figure out how TCombat and Object work. - -// 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? - uint32 EarliestSpellTime = ServerMilliseconds + Delay; - if(Creature->EarliestSpellTime < EarliestSpellTime){ - Creature->EarliestSpellTime = EarliestSpellTime; - } +void TCreature::Attack(void){ + this->Combat.Attack(); } diff --git a/src/creature.hh b/src/creature.hh index 69f0ef2..d5f4f1b 100644 --- a/src/creature.hh +++ b/src/creature.hh @@ -74,22 +74,24 @@ struct TCreature: TSkillBase { // REGULAR FUNCTIONS // ========================================================================= TCreature(void); + void Attack(void); int Damage(TCreature *Attacker, int Damage, int DamageType); 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 void MovePossible(void); // VTABLE[ 3] - virtual void IsPeaceful(void); // VTABLE[ 4] - virtual void GetMaster(void); // VTABLE[ 5] - virtual void TalkStimulus(void); // VTABLE[ 6] - virtual void DamageStimulus(void); // VTABLE[ 7] + 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(void); // VTABLE[ 9] - virtual void AttackStimulus(void); // VTABLE[10] + virtual void CreatureMoveStimulus(uint32 CreatureID, int Type); // VTABLE[ 9] + virtual void AttackStimulus(uint32 AttackerID); // VTABLE[10] // DATA // ========================================================================= diff --git a/src/crskill.cc b/src/crskill.cc index ce12cad..384e1fc 100644 --- a/src/crskill.cc +++ b/src/crskill.cc @@ -546,7 +546,11 @@ bool TSkillProbe::Probe(int Diff, int Prob, bool Increase){ this->Increase(1); } - // TODO(fusion): Not sure what's going on here. + // NOTE(fusion): `Diff` is a difficulty threshold and determines how challenging + // an action is based on the current skill level `this->Act`. The higher the + // skill level, the less difficult the action. + // I've only seen it used with SKILL_DISTANCE to determine whether a ranged + // attack should hit. bool Result = true; if(Diff != 0){ if(this->Act >= (rand() % Diff)){ @@ -1021,7 +1025,7 @@ void TSkillPoison::Event(int Range){ Range = -Range; } - Master->Damage(GetCreature(Master->PoisonDamageOrigin), Range, 2); + Master->Damage(GetCreature(Master->PoisonDamageOrigin), Range, DAMAGE_POISON); // NOTE(fusion): I think this is checking whether `Master` is still upon some // poison field to determine whether we should extend the poison effect? @@ -1031,7 +1035,7 @@ void TSkillPoison::Event(int Range){ // OpenTibia terms. ObjectType ObjType = Obj.getObjectType(); if(ObjType.getFlag(AVOID)){ - if(ObjType.getAttribute(AVOIDDAMAGETYPES) == 2){ + if(ObjType.getAttribute(AVOIDDAMAGETYPES) == DAMAGE_POISON){ this->Cycle += 1; } } @@ -1054,7 +1058,7 @@ void TSkillBurning::Event(int Range){ return; } - Master->Damage(GetCreature(Master->FireDamageOrigin), 10, 4); + Master->Damage(GetCreature(Master->FireDamageOrigin), 10, DAMAGE_FIRE); // NOTE(Fusion): Something similar to `TSkillPoison::Event` except we're // looking for a different field type. @@ -1062,7 +1066,7 @@ void TSkillBurning::Event(int Range){ while(Obj != NONE){ ObjectType ObjType = Obj.getObjectType(); if(ObjType.getFlag(AVOID)){ - if(ObjType.getAttribute(AVOIDDAMAGETYPES) == 4){ + if(ObjType.getAttribute(AVOIDDAMAGETYPES) == DAMAGE_FIRE){ this->Cycle += 1; } } @@ -1080,7 +1084,7 @@ void TSkillEnergy::Event(int Range){ return; } - Master->Damage(GetCreature(Master->EnergyDamageOrigin), 25, 8); + Master->Damage(GetCreature(Master->EnergyDamageOrigin), 25, DAMAGE_ENERGY); // NOTE(Fusion): Something similar to `TSkillPoison::Event` except we're // looking for a different field type. @@ -1088,7 +1092,7 @@ void TSkillEnergy::Event(int Range){ while(Obj != NONE){ ObjectType ObjType = Obj.getObjectType(); if(ObjType.getFlag(AVOID)){ - if(ObjType.getAttribute(AVOIDDAMAGETYPES) == 8){ + if(ObjType.getAttribute(AVOIDDAMAGETYPES) == DAMAGE_ENERGY){ this->Cycle += 1; } } diff --git a/src/enums.hh b/src/enums.hh index f7aee3a..d8f5816 100644 --- a/src/enums.hh +++ b/src/enums.hh @@ -6,6 +6,31 @@ // TODO(fusion): Probably cleanup these names? Prefix values? Its crazy that // there are no collision problems (possibly yet). +enum AnimationType: int { + ANIMATION_NONE = 0, + ANIMATION_SPEAR = 1, + ANIMATION_BOLT = 2, + ANIMATION_ARROW = 3, + ANIMATION_FIRE = 4, + ANIMATION_ENERGY = 5, + ANIMATION_POISON_ARROW = 6, + ANIMATION_BURST_ARROW = 7, + ANIMATION_THROWING_STAR = 8, + ANIMATION_THROWING_KNIFE = 9, + ANIMATION_SMALL_STONE = 10, + ANIMATION_SNOWBALL = 13, + ANIMATION_POWER_BOLT = 14, + ANIMATION_POISON = 15, +}; + +// NOTE(fusion): Not in debug symbols. +enum AttackMode: uint8 { + ATTACK_MODE_NONE = 0, + ATTACK_MODE_OFFENSIVE = 1, + ATTACK_MODE_BALANCED = 2, + ATTACK_MODE_DEFENSIVE = 3, +}; + enum BloodType: int { BT_BLOOD = 0, BT_SLIME = 1, @@ -14,6 +39,13 @@ enum BloodType: int { BT_ENERGY = 4, }; +// NOTE(fusion): Not in debug symbols. +enum ChaseMode: uint8 { + CHASE_MODE_NONE = 0, + CHASE_MODE_CLOSE = 1, + CHASE_MODE_RANGE = 2, +}; + enum CreatureType: int { PLAYER = 0, MONSTER = 1, @@ -31,6 +63,26 @@ enum CONNECTIONSTATE: int { CONNECTION_DISCONNECTED = 7, }; +// NOTE(fusion): Not in debug symbols. +enum DamageType: int { + DAMAGE_NONE = 0x0000, + DAMAGE_PHYSICAL = 0x0001, + DAMAGE_POISON = 0x0002, + DAMAGE_FIRE = 0x0004, + DAMAGE_ENERGY = 0x0008, + //DAMAGE_DEATH ? = 0x0010 + DAMAGE_POISON_PERIODIC = 0x0020, + DAMAGE_FIRE_PERIODIC = 0x0040, + DAMAGE_ENERGY_PERIODIC = 0x0080, + DAMAGE_LIFEDRAIN = 0x0100, + DAMAGE_MANADRAIN = 0x0200, +}; + +enum EffectType: int { + EFFECT_POFF = 3, + EFFECT_BURST_ARROW = 7, // Review +}; + enum FLAG: int { BANK = 0, CLIP = 1, @@ -134,6 +186,17 @@ enum KNOWNCREATURESTATE: int { KNOWNCREATURE_OUTDATED = 2 }; +enum LiquidType: int { + LIQUID_NONE = 0, + LIQUID_WATER = 1, + LIQUID_WINE = 2, + LIQUID_BEER = 3, + LIQUID_MUD = 4, + LIQUID_BLOOD = 5, + LIQUID_SLIME = 6, + LIQUID_LEMONADE = 12, +}; + // NOTE(fusion): Not in debug symbols. enum PROFESSION: uint8 { PROFESSION_NONE = 0, diff --git a/src/magic.cc b/src/magic.cc new file mode 100644 index 0000000..f8f0505 --- /dev/null +++ b/src/magic.cc @@ -0,0 +1,74 @@ +#include "magic.hh" +#include "creature.hh" + +// TImpact +// ============================================================================= +void TImpact::handleField(int a, int b, int c){ + // no-op +} + +void TImpact::handleCreature(TCreature *Victim){ + // no-op +} + +bool TImpact::isAggressive(void){ + return true; +} + +// TDamageImpact +// ============================================================================= +void TDamageImpact::TDamageImpact(TCreature *Actor, int DamageType, int Power, bool AllowDefense){ + if(Actor == NULL){ + error("TDamageImpact::TDamageImpact: Actor ist NULL.\n"); + } + + this->Actor = Actor; + this->DamageType = DamageType; + this->Power = Power; + this->AllowDefense = AllowDefense; +} + +// Magic Related Functions +// ============================================================================= +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? + uint32 EarliestSpellTime = ServerMilliseconds + Delay; + if(Creature->EarliestSpellTime < EarliestSpellTime){ + Creature->EarliestSpellTime = EarliestSpellTime; + } +} diff --git a/src/magic.hh b/src/magic.hh new file mode 100644 index 0000000..d24edd8 --- /dev/null +++ b/src/magic.hh @@ -0,0 +1,90 @@ +#ifndef TIBIA_MAGIC_HH_ +#define TIBIA_MAGIC_HH_ 1 + +#include "common.hh" + +struct TCreature; + +struct TImpact{ + // VIRTUAL FUNCTIONS + // ========================================================================= + virtual void handleField(int a, int b, int c); // VTABLE[0] + virtual void handleCreature(TCreature *Victim); // VTABLE[1] + virtual bool isAggressive(void); // VTABLE[2] + + // DATA + // ========================================================================= + //void *VTABLE; // IMPLICIT +}; + +#if 0 +struct TSummonImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int Race; + int Maximum; +}; + +struct TSpeedImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int Percent; + int Duration; +}; + +struct THealingImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int Power; +}; + +struct TOutfitImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + TOutfit Outfit; + int Duration; +}; + +struct TFieldImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int FieldType; +}; + +struct TDrunkenImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int Power; + int Duration; +}; + +struct TStrengthImpact { + TImpact super_TImpact; // INHERITANCE? + TCreature *Actor; + int Skills; + int Percent; + int Duration; +}; +#endif + +struct TDamageImpact: TImpact{ + // REGULAR FUNCTIONS + // ========================================================================= + TDamageImpact(TCreature *Actor, int DamageType, int Power, bool AllowDefense); + + // VIRTUAL FUNCTIONS + // ========================================================================= + void handleCreature(TCreature *Victim) override; + + // DATA + // ========================================================================= + // TImpact super_TImpact; // IMPLICIT + TCreature *Actor; + int DamageType; + int Power; + bool AllowDefense; +}; + +void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay); + +#endif //TIBIA_MAGIC_HH_ diff --git a/src/stubs.hh b/src/stubs.hh index 6eb3d3d..622e588 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -5,6 +5,7 @@ #include "enums.hh" #include "connection.hh" #include "creature.hh" +#include "magic.hh" #include "map.hh" #include "player.hh" @@ -19,14 +20,22 @@ extern void AnnounceChangedCreature(uint32 CreatureID, int Type); extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3); extern void Change(Object Obj, ObjectType NewType, uint32 Value); extern bool CheckRight(uint32 CreatureID, RIGHT Right); +extern void CircleShapeSpell(TCreature *Actor, int DestX, int DestY, int DestZ, + int Range, int Animation, int Radius, TImpact *Impact, int Effect); extern void CleanHouseField(int x, int y, int z); +extern int ComputeDamage(TCreature *Creature, int SpellNr, int Damage, int Variation); extern void CreatePlayerList(bool Online); +extern void Delete(Object Obj, int Count); extern Object GetBodyObject(uint32 CreatureID, int Position); extern TCreature *GetCreature(uint32 CreatureID); extern TConnection *GetFirstConnection(void); extern TConnection *GetNextConnection(void); +extern int GetRacePoison(int Race); +extern void GraphicalEffect(int x, int y, int z, int Type); +extern void GraphicalEffect(Object Obj, int Type); extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3); extern void LogoutAllPlayers(void); +extern void Missile(Object Start, Object Dest, int Type); extern void Move(uint32 CreatureID, Object Obj, Object Con, int Count, bool NoMerge, Object Ignore); extern void MoveCreatures(int Delay); extern void NetLoadCheck(void); @@ -47,6 +56,7 @@ extern void RefreshCylinders(void); extern void RefreshMap(void); extern void RefreshSector(int SectorX, int SectorY, int SectorZ, const uint8 *Data, int Size); extern void SavePlayerDataOrder(void); +extern bool SearchFlightField(uint32 FugitiveID, uint32 PursuerID, int *x, int *y, int *z); extern void SendAll(void); extern void SendAmbiente(TConnection *Connection); extern void SendClearTarget(TConnection *Connection); @@ -55,6 +65,8 @@ extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ... extern void SendPlayerData(TConnection *Connection); extern void SendPlayerSkills(TConnection *Connection); extern void SendPlayerState(TConnection *Connection, uint8 State); +extern bool ThrowPossible(int FromX, int FromY, int FromZ, + int ToX, int ToY, int ToZ, int Power); extern void WriteKillStatistics(void); #endif //TIBIA_STUBS_HH_ |
