diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/crcombat.cc | 388 | ||||
| -rw-r--r-- | src/crcombat.hh | 20 | ||||
| -rw-r--r-- | src/enums.hh | 16 | ||||
| -rw-r--r-- | src/magic.cc | 88 | ||||
| -rw-r--r-- | src/player.hh | 3 | ||||
| -rw-r--r-- | src/stubs.hh | 1 |
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); |
