aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/crcombat.cc388
-rw-r--r--src/crcombat.hh20
-rw-r--r--src/enums.hh16
-rw-r--r--src/magic.cc88
-rw-r--r--src/player.hh3
-rw-r--r--src/stubs.hh1
6 files changed, 449 insertions, 67 deletions
diff --git a/src/crcombat.cc b/src/crcombat.cc
index 256f056..7ad7534 100644
--- a/src/crcombat.cc
+++ b/src/crcombat.cc
@@ -1,4 +1,5 @@
#include "creature.hh"
+#include "monster.hh"
#include "player.hh"
#include "config.hh"
#include "magic.hh"
@@ -13,7 +14,7 @@ TCombat::TCombat(void){
this->LatestAttackTime = 0;
this->AttackMode = ATTACK_MODE_BALANCED;
this->ChaseMode = CHASE_MODE_NONE;
- this->SecureMode = 1;
+ this->SecureMode = SECURE_MODE_ENABLED;
this->AttackDest = 0;
this->Following = false;
this->Shield = NONE;
@@ -147,6 +148,164 @@ void TCombat::CheckCombatValues(void){
}
}
+static int WeaponTypeToSkill(int WeaponType){
+ int Result = SKILL_FIST;
+ switch(WeaponType){
+ case WEAPON_NONE: Result = SKILL_FIST; break;
+ case WEAPON_SWORD: Result = SKILL_SWORD; break;
+ case WEAPON_CLUB: Result = SKILL_CLUB; break;
+ case WEAPON_AXE: Result = SKILL_AXE; break;
+ case WEAPON_SHIELD: Result = SKILL_SHIELDING; break;
+ case WEAPON_AMMO: ATTR_FALLTHROUGH;
+ case WEAPON_THROW: Result = SKILL_DISTANCE; break;
+ }
+ return Result;
+}
+
+void TCombat::GetAttackValue(int *Value, int *SkillNr){
+ int AttackValue;
+ int WeaponType;
+ if(this->Close != NONE){
+ ObjectType CloseType = this->Close.getObjectType();
+ AttackValue = (int)CloseType.getAttribute(WEAPONATTACKVALUE);
+ WeaponType = (int)CloseType.getAttribute(WEAPONTYPE);
+ }else if(this->Missile != NONE){
+ ObjectType AmmoType = this->Ammo.getObjectType();
+ AttackValue = (int)AmmoType.getAttribute(AMMOATTACKVALUE);
+ WeaponType = WEAPON_AMMO;
+ }else if(this->Throw != NONE){
+ ObjectType ThrowType = this->Throw.getObjectType();
+ AttackValue = (int)ThrowType.getAttribute(THROWATTACKVALUE);
+ WeaponType = WEAPON_THROW;
+ }else if(this->Wand != NONE){
+ AttackValue = 0;
+ WeaponType = WEAPON_NONE;
+ }else{
+ AttackValue = RaceData[this->Master->Race].Attack;
+ WeaponType = WEAPON_NONE;
+ }
+
+ *Value = AttackValue;
+ *SkillNr = WeaponTypeToSkill(WeaponType);
+}
+
+void TCombat::GetDefendValue(int *Value, int *SkillNr){
+ int DefenseValue;
+ int WeaponType;
+ if(this->Shield != NONE){
+ ObjectType ShieldType = this->Shield.getObjectType();
+ DefenseValue = (int)ShieldType.getAttribute(SHIELDDEFENDVALUE);
+ WeaponType = WEAPON_SHIELD;
+ }else if(this->Close != NONE){
+ ObjectType CloseType = this->Close.getObjectType();
+ DefenseValue = (int)CloseType.getAttribute(WEAPONDEFENDVALUE);
+ WeaponType = (int)CloseType.getAttribute(WEAPONTYPE);
+ }else if(this->Throw != NONE){
+ ObjectType ThrowType = this->Throw.getObjectType();
+ DefenseValue = (int)ThrowType.getAttribute(THROWDEFENDVALUE);
+ WeaponType = WEAPON_THROW;
+ }else if(this->Missile != NONE){
+ // TODO(fusion): This might be correct. Having a bow equipped will reduce
+ // your defense to zero but having a wand equipped will give you base defense.
+ DefenseValue = 0;
+ WeaponType = WEAPON_AMMO;
+ }else{
+ DefenseValue = RaceData[this->Master->Race].Defend;
+ WeaponType = WEAPON_NONE;
+ }
+
+ *Value = DefenseValue;
+ *SkillNr = WeaponTypeToSkill(WeaponType);
+}
+
+int TCombat::GetAttackDamage(void){
+ int MaxValue, SkillNr;
+ this->GetAttackValue(&MaxValue, &SkillNr);
+ if(this->AttackMode == ATTACK_MODE_OFFENSIVE){
+ MaxValue += (MaxValue * 2) / 10;
+ }else if(this->AttackMode == ATTACK_MODE_DEFENSIVE){
+ MaxValue -= (MaxValue * 4) / 10;
+ }
+
+ TSkill *Skill = this->Master->Skills[SkillNr];
+ int Result = Skill->ProbeValue(MaxValue, this->LearningPoints > 0);
+ if(this->LearningPoints > 0){
+ this->LearningPoints -= 1;
+ }
+ return Result;
+}
+
+int TCombat::GetDefendDamage(void){
+ if(this->EarliestDefendTime > ServerMilliseconds){
+ return 0;
+ }
+
+ this->EarliestDefendTime = this->LastDefendTime + 2000;
+ this->LastDefendTime = ServerMilliseconds;
+
+ int AttackMode = this->AttackMode;
+ if(this->Following || this->AttackDest == 0){
+ AttackMode = ATTACK_MODE_DEFENSIVE;
+ }
+
+ int MaxValue, SkillNr;
+ this->GetDefendValue(&MaxValue, &SkillNr);
+ if(AttackMode == ATTACK_MODE_OFFENSIVE){
+ MaxValue -= (MaxValue * 4) / 10;
+ }else if(AttackMode == ATTACK_MODE_DEFENSIVE){
+ MaxValue += (MaxValue * 8) / 10;
+ }
+
+ TSkill *Skill = this->Master->Skills[SkillNr];
+ bool Increase = (this->Shield != NONE && this->LearningPoints > 0);
+ int Result = Skill->ProbeValue(MaxValue, Increase);
+ if(Increase){
+ this->LearningPoints -= 1;
+ }
+
+ Object Shield = this->Shield;
+ if(Shield != NONE){
+ ObjectType ShieldType = Shield.getObjectType();
+ if(ShieldType.getFlag(WEAROUT)){
+ uint32 RemainingUses = Shield.getAttribute(REMAININGUSES);
+ if(RemainingUses > 1){
+ Change(Shield, REMAININGUSES, RemainingUses - 1);
+ }else{
+ ObjectType WearOutType = ShieldType.getAttribute(WEAROUTTARGET);
+ Change(Shield, WearOutType, 0);
+ // TODO(fusion): Probably just check anyways, to be sure.
+ if(!Shield.exists() || !WearOutType.getFlag(SHIELD)){
+ this->CheckCombatValues();
+ }
+ }
+ }
+ }
+
+ return Result;
+}
+
+int TCombat::GetArmorStrength(void){
+ // TODO(fusion): We're iterating over inventory slots. Make it clearer?
+ int Armor = 0;
+ TCreature *Master = this->Master;
+ for(int Position = 1; Position <= 10; Position += 1){
+ Object Obj = GetBodyObject(Master->ID, Position);
+ if(Obj.exists()){
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(CLOTHES) && ObjType.getFlag(ARMOR)
+ && (int)ObjType.getAttribute(BODYPOSITION) == Position){
+ Armor += (int)ObjType.getAttribute(ARMORVALUE);
+ }
+ }
+ }
+
+ Armor += RaceData[Master->Race].Armor;
+ if(Armor >= 2){
+ Armor = (Armor / 2) + rand() % (Armor / 2);
+ }
+ return Armor;
+}
+
int TCombat::GetDistance(void){
int Distance = 0;
if(this->Close != NONE || this->Fist){
@@ -159,6 +318,42 @@ int TCombat::GetDistance(void){
return Distance;
}
+void TCombat::ActivateLearning(void){
+ this->LearningPoints = 30;
+}
+
+void TCombat::SetAttackMode(uint8 AttackMode){
+ if(AttackMode != ATTACK_MODE_OFFENSIVE
+ && AttackMode != ATTACK_MODE_BALANCED
+ && AttackMode != ATTACK_MODE_DEFENSIVE){
+ error("TCombat::SetAttackMode: Ungültiger Angriffsmodus %d.\n", AttackMode);
+ return;
+ }
+
+ if(this->AttackMode != AttackMode){
+ this->DelayAttack(2000);
+ this->AttackMode = AttackMode;
+ }
+}
+
+void TCombat::SetChaseMode(uint8 ChaseMode){
+ if(ChaseMode != CHASE_MODE_NONE && ChaseMode != CHASE_MODE_CLOSE){
+ error("TCombat::SetChaseMode: Ungültiger Verfolgungsmodus %d.\n", ChaseMode);
+ return;
+ }
+
+ this->ChaseMode = ChaseMode;
+}
+
+void TCombat::SetSecureMode(uint8 SecureMode){
+ if(SecureMode != SECURE_MODE_DISABLED && SecureMode != SECURE_MODE_ENABLED){
+ error("TCombat::SetSecureMode: Ungültiger Sicherheitsmodus %d.\n", SecureMode);
+ return;
+ }
+
+ this->SecureMode = SecureMode;
+}
+
void TCombat::SetAttackDest(uint32 TargetID, bool Follow){
if(this->AttackDest == TargetID && this->Following == Follow){
return;
@@ -178,8 +373,8 @@ void TCombat::SetAttackDest(uint32 TargetID, bool Follow){
if(!Follow){
if(Master->Type == PLAYER && Target->Type == PLAYER){
- if(this->SecureMode == 1 && WorldType == NORMAL
- && !((TPlayer*)Master)->IsAttackJustified(TargetID)){
+ if(this->SecureMode == SECURE_MODE_ENABLED && WorldType == NORMAL
+ && !((TPlayer*)Master)->IsAttackJustified(TargetID)){
this->StopAttack(0);
throw SECUREMODE;
}
@@ -275,8 +470,8 @@ void TCombat::CanToDoAttack(void){
if(!this->Following){
if(Master->Type == PLAYER && Target->Type == PLAYER){
- if(this->SecureMode == 1 && WorldType == NORMAL
- && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){
+ if(this->SecureMode == SECURE_MODE_ENABLED && WorldType == NORMAL
+ && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){
this->StopAttack(0);
throw SECUREMODE;
}
@@ -319,6 +514,24 @@ void TCombat::CanToDoAttack(void){
}
}
+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;
+ }
+}
+
void TCombat::Attack(void){
if(this->AttackDest == 0 || this->Following){
return;
@@ -352,8 +565,8 @@ void TCombat::Attack(void){
}
if(Master->Type == PLAYER && Target->Type == PLAYER){
- if(this->SecureMode == 1 && WorldType == NORMAL
- && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){
+ if(this->SecureMode == SECURE_MODE_ENABLED && WorldType == NORMAL
+ && !((TPlayer*)Master)->IsAttackJustified(Target->ID)){
this->StopAttack(0);
throw SECUREMODE;
}
@@ -436,49 +649,17 @@ void TCombat::Attack(void){
}
}
-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;
- }
-}
-
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 Attack = this->GetAttackDamage();
int Defense = Target->Combat.GetDefendDamage();
int Damage = Attack - Defense;
if(Damage < 0){
Damage = 0;
}
- int DamageDone = Target->Damage(Master, Damage, 1); // DAMAGE_PHYSICAL ?
+ int DamageDone = Target->Damage(Master, Damage, DAMAGE_PHYSICAL);
if(DamageDone > 0){
- this->LearningPoints = 30;
+ this->ActivateLearning();
}
int Poison = GetRacePoison(Master->Race);
@@ -547,7 +728,7 @@ void TCombat::WandAttack(TCreature *Target){
int Damage = AttackStrength + random(-AttackVariation, AttackVariation);
if(Target->Damage(Master, Damage, DamageType) > 0){
- this->LearningPoints = 30;
+ this->ActivateLearning();
}
::Missile(Master->CrObject, Target->CrObject, AnimType);
@@ -617,29 +798,18 @@ void TCombat::DistanceAttack(TCreature *Target){
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;
- }
+ int Attack = this->GetAttackDamage();
// 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?
+ // doesn't work with ranged attacks but worn them out? But then only
+ // if the attacker is wearing a shield? This is a probably a bug.
if(this->Shield != NONE){
Target->Combat.GetDefendDamage();
}
if(Target->Damage(Master, Attack, DamageType) > 0){
- this->LearningPoints = 30;
+ this->ActivateLearning();
}
}else{
if(DistanceX > 1 || DistanceY > 1){
@@ -679,3 +849,105 @@ void TCombat::DistanceAttack(TCreature *Target){
GraphicalEffect(DropX, DropY, DropZ, EFFECT_POFF);
}
}
+
+void TCombat::AddDamageToCombatList(uint32 Attacker, uint32 Damage){
+ this->CombatDamage += Damage;
+ for(int i = 0; i < NARRAY(this->CombatList); i += 1){
+ if(this->CombatList[i].ID == Attacker){
+ this->CombatList[i].Damage += Damage;
+ this->CombatList[i].TimeStamp = RoundNr;
+ return;
+ }
+ }
+
+ int NextEntryIndex = this->ActCombatEntry;
+ this->CombatList[NextEntryIndex].ID = Attacker;
+ this->CombatList[NextEntryIndex].Damage = Damage;
+ this->CombatList[NextEntryIndex].TimeStamp = RoundNr;
+
+ NextEntryIndex += 1;
+ if(NextEntryIndex >= NARRAY(this->CombatList)){
+ NextEntryIndex = 0;
+ }
+ this->ActCombatEntry = NextEntryIndex;
+}
+
+uint32 TCombat::GetDamageByCreature(uint32 CreatureID){
+ uint32 Damage = 0;
+ for(int i = 0; i < NARRAY(this->CombatList); i += 1){
+ if(this->CombatList[i].ID == CreatureID){
+ Damage = this->CombatList[i].Damage;
+ break;
+ }
+ }
+ return Damage;
+}
+
+uint32 TCombat::GetMostDangerousAttacker(void){
+ uint32 Attacker = 0;
+ uint32 MaxDamage = 0;
+ for(int i = 0; i < NARRAY(this->CombatList); i += 1){
+ if((RoundNr - this->CombatList[i].TimeStamp) < 60
+ && this->CombatList[i].Damage > MaxDamage){
+ Attacker = this->CombatList[i].ID;
+ MaxDamage = this->CombatList[i].Damage;
+ }
+ }
+ return Attacker;
+}
+
+void TCombat::DistributeExperiencePoints(uint32 Exp){
+ TCreature *Master = this->Master;
+ print(3, "%s ist gestorben. Verteile %u EXP...\n", Master->Name, Exp);
+ if(this->CombatDamage == 0){
+ return;
+ }
+
+ for(int i = 0; i < NARRAY(this->CombatList); i += 1){
+ TCreature *Attacker = GetCreature(this->CombatList[i].ID);
+ if(Attacker == NULL || Attacker->IsDead){
+ continue;
+ }
+
+ int Amount = (int)((Exp * this->CombatList[i].Damage) / this->CombatDamage);
+ if(Master->Type == PLAYER && Attacker->Type == PLAYER){
+ if(((TPlayer*)Master)->GetPartyLeader(true) == ((TPlayer*)Attacker)->GetPartyLeader(true)){
+ continue;
+ }
+
+ int MasterLevel = Master->Skills[SKILL_LEVEL]->Get();
+ int AttackerLevel = Attacker->Skills[SKILL_LEVEL]->Get();
+ int MaxLevel = (MasterLevel * 11) / 10;
+ if(AttackerLevel < MaxLevel){
+ Amount = ((MaxLevel - AttackerLevel) * Amount) / MasterLevel;
+ }else{
+ Amount = 0;
+ }
+ }
+
+ if(Amount > 0){
+ if(Attacker->Type == PLAYER){
+ // NOTE(fusion): Enable soul regeneration.
+ int AttackerLevel = Attacker->Skills[SKILL_LEVEL]->Get();
+ if(Amount >= AttackerLevel){
+ int Interval = 120;
+ if(((TPlayer*)Attacker)->GetActivePromotion()){
+ Interval = 15;
+ }
+
+ int Count = Attacker->Skills[SKILL_SOUL]->TimerValue() % Interval;
+ if(Count == 0){
+ Count = Interval;
+ }
+
+ Attacker->SetTimer(SKILL_SOUL, (240 / Interval), Count, Interval, -1);
+ }
+ }
+
+ Attacker->Skills[0]->Increase(Amount);
+ TextualEffect(Attacker->CrObject, 0xD7, "%d", Amount);
+ }
+ }
+}
+
+
diff --git a/src/crcombat.hh b/src/crcombat.hh
index bd22584..b5db3c3 100644
--- a/src/crcombat.hh
+++ b/src/crcombat.hh
@@ -6,7 +6,7 @@ struct TCreature;
struct TCombatEntry{
uint32 ID;
uint32 Damage;
- int TimeStamp;
+ uint32 TimeStamp;
};
struct TCombat{
@@ -16,17 +16,29 @@ struct TCombat{
void GetWeapon(void);
void GetAmmo(void);
void CheckCombatValues(void);
- int GetDistance(void);
- void GetAttackValue(int *Attack, uint16 *SkillNr);
+ void GetAttackValue(int *Value, int *SkillNr);
+ void GetDefendValue(int *Value, int *SkillNr);
+ int GetAttackDamage(void);
int GetDefendDamage(void);
+ int GetArmorStrength(void);
+ int GetDistance(void);
+ void ActivateLearning(void);
+ void SetAttackMode(uint8 AttackMode);
+ void SetChaseMode(uint8 ChaseMode);
+ void SetSecureMode(uint8 SecureMode);
void SetAttackDest(uint32 TargetID, bool Follow);
void CanToDoAttack(void);
- void Attack(void);
void StopAttack(int Delay);
void DelayAttack(int Milliseconds);
+ void Attack(void);
void CloseAttack(TCreature *Target);
void DistanceAttack(TCreature *Target);
void WandAttack(TCreature *Target);
+ void AddDamageToCombatList(uint32 Attacker, uint32 Damage);
+ uint32 GetDamageByCreature(uint32 CreatureID);
+ uint32 GetMostDangerousAttacker(void);
+ void DistributeExperiencePoints(uint32 Exp);
+
// DATA
// =========================================================================
diff --git a/src/enums.hh b/src/enums.hh
index d8f5816..4081dce 100644
--- a/src/enums.hh
+++ b/src/enums.hh
@@ -25,7 +25,6 @@ enum AnimationType: int {
// 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,
@@ -360,6 +359,11 @@ enum RIGHT: int {
NO_STATISTICS = 81,
};
+enum SecureMode: uint8 {
+ SECURE_MODE_DISABLED = 0,
+ SECURE_MODE_ENABLED = 1,
+};
+
// NOTE(fusion): Not in debug symbols.
enum Skill: int {
SKILL_LEVEL = 0,
@@ -556,4 +560,14 @@ enum TWorldType: int {
PVP_ENFORCED = 2,
};
+enum WeaponType: int {
+ WEAPON_NONE = 0,
+ WEAPON_SWORD = 1,
+ WEAPON_CLUB = 2,
+ WEAPON_AXE = 3,
+ WEAPON_SHIELD = 4,
+ WEAPON_AMMO = 5,
+ WEAPON_THROW = 6,
+};
+
#endif //TIBIA_ENUMS_HH_
diff --git a/src/magic.cc b/src/magic.cc
index f8f0505..5d6acad 100644
--- a/src/magic.cc
+++ b/src/magic.cc
@@ -1,6 +1,63 @@
#include "magic.hh"
+#include "config.hh"
#include "creature.hh"
+#include "stubs.hh"
+
+static const char SpellSyllable[51][6] = {
+ "",
+ "al",
+ "ad",
+ "ex",
+ "ut",
+ "om",
+ "para",
+ "ana",
+ "evo",
+ "ori",
+ "mort",
+ "lux",
+ "liber",
+ "vita",
+ "flam",
+ "pox",
+ "hur",
+ "moe",
+ "ani",
+ "ina",
+ "eta",
+ "amo",
+ "hora",
+ "gran",
+ "cogni",
+ "res",
+ "mas",
+ "vis",
+ "som",
+ "aqua",
+ "frigo",
+ "tera",
+ "ura",
+ "sio",
+ "grav",
+ "ito",
+ "pan",
+ "vid",
+ "isa",
+ "iva",
+ "con",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+};
+
// TImpact
// =============================================================================
void TImpact::handleField(int a, int b, int c){
@@ -17,7 +74,7 @@ bool TImpact::isAggressive(void){
// TDamageImpact
// =============================================================================
-void TDamageImpact::TDamageImpact(TCreature *Actor, int DamageType, int Power, bool AllowDefense){
+TDamageImpact::TDamageImpact(TCreature *Actor, int DamageType, int Power, bool AllowDefense){
if(Actor == NULL){
error("TDamageImpact::TDamageImpact: Actor ist NULL.\n");
}
@@ -28,6 +85,26 @@ void TDamageImpact::TDamageImpact(TCreature *Actor, int DamageType, int Power, b
this->AllowDefense = AllowDefense;
}
+void TDamageImpact::handleCreature(TCreature *Victim){
+ if(Victim == NULL){
+ error("TDamageImpact::handleCreature: Opfer existiert nicht.\n");
+ return;
+ }
+
+ TCreature *Actor = this->Actor;
+ if(Actor != NULL && Actor != Victim){
+ if(WorldType != NON_PVP || !Actor->IsPeaceful() || !Victim->IsPeaceful()){
+ int DamageType = this->DamageType;
+ int Damage = this->Power;
+ if(DamageType == DAMAGE_PHYSICAL && this->AllowDefense){
+ // TODO(fusion): Shouldn't we clamp `Damage` to zero?
+ Damage -= Victim->Combat.GetDefendDamage();
+ }
+ Victim->Damage(Actor, Damage, DamageType);
+ }
+ }
+}
+
// Magic Related Functions
// =============================================================================
void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay){
@@ -36,8 +113,9 @@ void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay){
throw ERROR;
}
- if(Creature->Type != PLAYER || ManaPoints < 0)
+ if(Creature->Type != PLAYER || ManaPoints < 0){
return;
+ }
TSkill *Mana = Creature->Skills[SKILL_MANA];
if(Mana == NULL){
@@ -52,11 +130,13 @@ void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay){
}
if(!CheckRight(Creature->ID, UNLIMITED_MANA)){
- if(Mana->Get() < ManaPoints)
+ if(Mana->Get() < ManaPoints){
throw NOTENOUGHMANA;
+ }
- if(Soul->Get() < SoulPoints)
+ if(Soul->Get() < SoulPoints){
throw NOTENOUGHSOULPOINTS;
+ }
Mana->Change(-ManaPoints);
Soul->Change(-SoulPoints);
diff --git a/src/player.hh b/src/player.hh
index 815289e..33ca7c3 100644
--- a/src/player.hh
+++ b/src/player.hh
@@ -72,6 +72,9 @@ struct TPlayer: TCreature {
uint8 GetEffectiveProfession(void);
uint8 GetActiveProfession(void);
bool GetActivePromotion(void);
+
+ uint32 GetPartyLeader(bool CheckFormer);
+
bool IsAttackJustified(uint32 Victim);
void RecordAttack(uint32 Victim);
diff --git a/src/stubs.hh b/src/stubs.hh
index 622e588..d3cfcd0 100644
--- a/src/stubs.hh
+++ b/src/stubs.hh
@@ -65,6 +65,7 @@ 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 void TextualEffect(Object Obj, int Color, const char *Text, ...) ATTR_PRINTF(3, 4);
extern bool ThrowPossible(int FromX, int FromY, int FromZ,
int ToX, int ToY, int ToZ, int Power);
extern void WriteKillStatistics(void);