aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md3
-rw-r--r--reference/game.c628
-rw-r--r--src/crcombat.cc30
-rw-r--r--src/creature.cc416
-rw-r--r--src/enums.hh33
-rw-r--r--src/monster.hh3
-rw-r--r--src/player.hh1
-rw-r--r--src/stubs.hh5
8 files changed, 475 insertions, 644 deletions
diff --git a/TODO.md b/TODO.md
index 11e3497..6102538 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,4 +1,7 @@
## TODO NEXT
+- TCreature::Damage
+- MAP.CC FileNumber: uint32 -> uintptr
+- MAGIC.CC
- TCreature
- TPlayer
- TNonPlayer
diff --git a/reference/game.c b/reference/game.c
index b5b2520..c003e99 100644
--- a/reference/game.c
+++ b/reference/game.c
@@ -38167,16 +38167,6 @@ TCreature * GetCreature(Object *Obj)
return pTVar1;
}
-
-
-bool IsCreaturePlayer(ulong CreatureID)
-
-{
- return CreatureID < 0x40000000;
-}
-
-
-
// WARNING: Unknown calling convention -- yet parameter storage is locked
void ProcessSkills(void)
@@ -38969,49 +38959,6 @@ void WriteKillStatistics(void)
return;
}
-
-
-void AddKillStatistics(int AttackerRace,int DefenderRace)
-
-{
- int iVar1;
- TRaceData *pTVar2;
- char *pcVar3;
- bool bVar4;
-
- bVar4 = AttackerRace * 0x150 == 0;
- iVar1 = 6;
- pTVar2 = RaceData + AttackerRace;
- pcVar3 = "human";
- do {
- if (iVar1 == 0) break;
- iVar1 = iVar1 + -1;
- bVar4 = pTVar2->Name[0] == *pcVar3;
- pTVar2 = (TRaceData *)(pTVar2->Name + 1);
- pcVar3 = pcVar3 + 1;
- } while (bVar4);
- if (bVar4) {
- KilledCreatures[DefenderRace] = KilledCreatures[DefenderRace] + 1;
- }
- iVar1 = 6;
- bVar4 = DefenderRace * 0x150 == 0;
- pTVar2 = RaceData + DefenderRace;
- pcVar3 = "human";
- do {
- if (iVar1 == 0) break;
- iVar1 = iVar1 + -1;
- bVar4 = pTVar2->Name[0] == *pcVar3;
- pTVar2 = (TRaceData *)(pTVar2->Name + 1);
- pcVar3 = pcVar3 + 1;
- } while (bVar4);
- if (bVar4) {
- KilledPlayers[AttackerRace] = KilledPlayers[AttackerRace] + 1;
- }
- return;
-}
-
-
-
// WARNING: Unknown calling convention -- yet parameter storage is locked
void ExitKillStatistics(void)
@@ -39950,581 +39897,6 @@ void __thiscall TCreature::Death(TCreature *this)
return;
}
-
-
-// DWARF original prototype: int Damage(TCreature * this, TCreature * atk, int Damage, int
-// DamageType)
-
-int __thiscall TCreature::Damage(TCreature *this,TCreature *atk,int Damage,int DamageType)
-
-{
- TConnection *pTVar1;
- bool bVar2;
- bool bVar3;
- ushort uVar4;
- int iVar5;
- int iVar6;
- int iVar7;
- ulong uVar8;
- TPlayer *this_00;
- int iVar9;
- _func_int_varargs **pp_Var10;
- TSkill *pTVar11;
- CreatureType CVar12;
- TSkill *sk_1;
- char *pcVar13;
- Object *pOVar14;
- char *pcVar15;
- undefined4 uVar16;
- TPlayer *pl;
- int Unjustified;
- int i;
- uint local_88;
- ulong MurdererID;
- int OldLevel;
- int b;
- TSkill *sk;
- int b_1;
- ulong atkID;
- TCreature *Responsible;
- char Remark [30];
- Object Obj;
- Object local_3c [4];
- Object local_2c;
- Object Obj_1;
-
- if (this->IsDead != false) {
- return 0;
- }
- if (this->Type == NPC) {
- return 0;
- }
- if (this->Type == PLAYER) {
- CVar12 = PLAYER;
- if ((this->Connection != (TConnection *)0x0) && (atk != (TCreature *)0x0)) {
- SendMarkCreature((TConnection *)atk->ID,0,(int)this->Connection);
- CVar12 = this->Type;
- }
- if ((((CVar12 == PLAYER) && (atk != (TCreature *)0x0)) && (atk->Type == PLAYER)) &&
- (((DamageType != 0x20 && (DamageType != 0x40)) && (DamageType != 0x80)))) {
- Damage = (Damage + 1) / 2;
- }
- }
- atkID = (ulong)atk;
- if (atk == (TCreature *)0x0) {
- b_1 = 0;
- }
- else {
- iVar5 = (*atk->_vptr_TCreature[5])(atk);
- if (iVar5 != 0) {
- uVar8 = (*atk->_vptr_TCreature[5])(atk);
- atkID = (ulong)GetCreature(uVar8);
- }
- b_1 = atk->ID;
- }
- if (atk != (TCreature *)0x0) {
- BlockLogout(atk,0x3c,this->Type == PLAYER);
- }
- if (((atkID != 0) &&
- (BlockLogout((TCreature *)atkID,0x3c,this->Type == PLAYER), *(int *)(atkID + 0x28c) == 0))
- && (this->Type == PLAYER)) {
- TPlayer::RecordAttack((TPlayer *)atkID,this->ID);
- }
- if (Damage < 1) {
-LAB_080a1ca4:
- local_2c.ObjectID = (this->CrObject).ObjectID;
- pOVar14 = &local_2c;
- }
- else {
- if (this->Type == PLAYER) {
- bVar2 = CheckRight(this->ID,INVULNERABLE);
- if (bVar2) goto LAB_080a1ca4;
- if (this->Type == PLAYER) {
- sk = (TSkill *)0x1;
- do {
- if (Damage < 1) goto LAB_080a1c71;
- bVar2 = false;
- GetBodyObject((ulong)&local_2c,this->ID);
- local_3c[0].ObjectID = ::NONE.ObjectID;
- if (local_2c.ObjectID == ::NONE.ObjectID) {
-LAB_080a1d02:
- bVar2 = true;
- }
- else {
- Object::getObjectType((Object *)(Remark + 0x1c));
- bVar3 = ObjectType::getFlag((ObjectType *)(Remark + 0x1c),PROTECTION);
- if (!bVar3) goto LAB_080a1d02;
- }
- if (!bVar2) {
- Object::getObjectType((Object *)(Remark + 0x1c));
- bVar2 = ObjectType::getFlag((ObjectType *)(Remark + 0x1c),CLOTHES);
- if (bVar2) {
- Object::getObjectType((Object *)(Remark + 0x1c));
- pTVar11 = (TSkill *)
- ObjectType::getAttribute
- ((ObjectType *)(Remark + 0x1c),BODYPOSITION);
- if (pTVar11 == sk) {
- Object::getObjectType((Object *)(Remark + 0x1c));
- uVar8 = ObjectType::getAttribute
- ((ObjectType *)(Remark + 0x1c),
- PROTECTIONDAMAGETYPES);
- if ((DamageType & uVar8) != 0) {
- Object::getObjectType((Object *)(Remark + 0x1c));
- uVar8 = ObjectType::getAttribute
- ((ObjectType *)(Remark + 0x1c),DAMAGEREDUCTION
- );
- Damage = (Damage * (100 - uVar8)) / 100;
- Object::getObjectType((Object *)(Remark + 0x1c));
- bVar2 = ObjectType::getFlag((ObjectType *)(Remark + 0x1c),
- WEAROUT);
- if (bVar2) {
- uVar8 = Object::getAttribute(&local_2c,REMAININGUSES);
- if (uVar8 < 2) {
- // try { // try from 080a1e9f to 080a1ee8 has its CatchHandler @ 080a1f63
- Object::getObjectType(local_3c);
- uVar8 = ObjectType::getAttribute
- ((ObjectType *)local_3c,WEAROUTTARGET)
- ;
- ObjectType::setTypeID
- ((ObjectType *)(Remark + 0x1c),uVar8);
- local_3c[0].ObjectID = local_2c.ObjectID;
- Change(local_3c,(ObjectType *)(Remark + 0x1c),0);
- }
- else {
- stack0xffffffb4 = local_2c.ObjectID;
- uVar8 = Object::getAttribute(&local_2c,REMAININGUSES);
- Change((Object *)(Remark + 0x1c),REMAININGUSES,uVar8 - 1
- );
- }
- }
- }
- }
- }
- else {
- Object::getObjectType((Object *)(Remark + 0x1c));
- error("TCreature::Damage: Objekt %d hat PROTECTION, aber nicht CLOTHES.\n"
- ,stack0xffffffb4);
- }
- }
- sk = (TSkill *)((int)&sk->_vptr_TSkill + 1);
- } while ((int)sk < 0xb);
- }
- }
- if (0 < Damage) {
- if (DamageType == 0x20) {
- if (RaceData[this->Race].NoPoison != false) {
- return 0;
- }
- pTVar11 = (this->super_TSkillBase).Skills[0x11];
- iVar5 = (*pTVar11->_vptr_TSkill[0xd])(pTVar11);
- if (iVar5 < Damage) {
- this->PoisonDamageOrigin = b_1;
- TSkillBase::SetTimer(&this->super_TSkillBase);
- }
- pp_Var10 = this->_vptr_TCreature;
- uVar16 = 0x20;
-LAB_080a1b71:
- (*pp_Var10[7])(this,b_1,Damage,uVar16);
- return (int)(Object *)Damage;
- }
- if (DamageType == 0x40) {
- if (RaceData[this->Race].NoBurning != false) {
- return 0;
- }
- this->FireDamageOrigin = b_1;
- TSkillBase::SetTimer(&this->super_TSkillBase);
- pp_Var10 = this->_vptr_TCreature;
- uVar16 = 0x40;
- goto LAB_080a1b71;
- }
- if (DamageType == 0x80) {
- if (RaceData[this->Race].NoEnergy != false) {
- return 0;
- }
- this->EnergyDamageOrigin = b_1;
- TSkillBase::SetTimer(&this->super_TSkillBase);
- pp_Var10 = this->_vptr_TCreature;
- uVar16 = 0x80;
- goto LAB_080a1b71;
- }
- if (DamageType == 1) {
- uVar4 = TCombat::GetArmorStrength(&this->Combat);
- Damage = Damage - (uint)uVar4;
- if (0 < Damage) goto LAB_080a0dd2;
- }
- else {
-LAB_080a0dd2:
- if ((((DamageType != 2) || (RaceData[this->Race].NoPoison == false)) &&
- (((DamageType != 4 || (RaceData[this->Race].NoBurning == false)) &&
- ((DamageType != 8 || (RaceData[this->Race].NoEnergy == false)))))) &&
- (((DamageType != 1 || (RaceData[this->Race].NoHit == false)) &&
- ((DamageType != 0x100 || (RaceData[this->Race].NoLifeDrain == false)))))) {
- pOVar14 = (Object *)Damage;
- iVar5 = DamageType;
- (*this->_vptr_TCreature[7])(this,b_1,Damage,DamageType);
- bVar2 = false;
- if (((this->Outfit).OutfitID == 0) && ((this->Outfit).field_1.ObjectType == 0))
- {
- bVar2 = true;
- }
- if ((bVar2) && (this->Type != PLAYER)) {
- iVar5 = 0;
- TSkillBase::SetTimer(&this->super_TSkillBase);
- iVar9 = (this->OrgOutfit).OutfitID;
- (this->Outfit).field_1 = (this->OrgOutfit).field_1;
- (this->Outfit).OutfitID = iVar9;
- AnnounceChangedCreature(this->ID,3);
- pOVar14 = (Object *)(Remark + 0x1c);
- stack0xffffffb4 = ::NONE.ObjectID;
- local_3c[0].ObjectID = (this->CrObject).ObjectID;
- b_1 = 2;
- NotifyAllCreatures(local_3c,2,pOVar14);
- }
- if (DamageType == 0x200) {
- pTVar11 = (this->super_TSkillBase).Skills[3];
- iVar5 = TSkill::Get(pTVar11);
- if (iVar5 < Damage) {
- Damage = TSkill::Get(pTVar11);
- }
- if ((Object *)Damage != (Object *)0x0) {
- TSkill::Change(pTVar11,-Damage);
- if ((this->Type == PLAYER) && (this->Connection != (TConnection *)0x0))
- {
- SendMessage(this->Connection,0x15,"You lose %d mana.",Damage);
- }
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),0xe);
- unique0x00009680 = (this->CrObject).ObjectID;
- TextualEffect();
- return (int)(Object *)Damage;
- }
- return 0;
- }
- pTVar11 = (this->super_TSkillBase).Skills[0x15];
- iVar5 = (*pTVar11->_vptr_TSkill[0xd])(pTVar11,b_1,pOVar14,iVar5);
- if ((0 < iVar5) ||
- (iVar5 = TSkill::Get((this->super_TSkillBase).Skills[0x15]), 0 < iVar5)) {
- pTVar11 = (this->super_TSkillBase).Skills[3];
- iVar5 = TSkill::Get(pTVar11);
- if (Damage <= iVar5) {
- TSkill::Change(pTVar11,-Damage);
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),2);
- unique0x00009680 = (this->CrObject).ObjectID;
- TextualEffect();
- if (this->Type != PLAYER) {
- return (int)(Object *)Damage;
- }
- pTVar1 = this->Connection;
- if (pTVar1 != (TConnection *)0x0) {
- if (atk == (TCreature *)0x0) {
- SendMessage(pTVar1,0x15,"You lose %d mana.",Damage);
- }
- else {
- SendMessage(pTVar1,0x15,
- "You lose %d mana blocking an attack by %s.",Damage,
- atk->Name);
- }
- SendPlayerData(this->Connection);
- return (int)(Object *)Damage;
- }
- return (int)(Object *)Damage;
- }
- iVar5 = TSkill::Get(pTVar11);
- Damage = Damage - iVar5;
- (*pTVar11->_vptr_TSkill[2])(pTVar11,0);
- }
- pTVar11 = (this->super_TSkillBase).Skills[2];
- iVar5 = TSkill::Get(pTVar11);
- if (iVar5 < Damage) {
- Damage = TSkill::Get(pTVar11);
- }
- TSkill::Change(pTVar11,-Damage);
- if ((TCreature *)atkID == atk) {
- pOVar14 = (Object *)Damage;
- if (atk != (TCreature *)0x0) goto LAB_080a0fec;
- }
- else {
- if (atkID != 0) {
- TCombat::AddDamageToCombatList
- (&this->Combat,*(ulong *)(atkID + 0x208),(ushort)(Damage / 2))
- ;
- }
- if (atk != (TCreature *)0x0) {
- pOVar14 = (Object *)(Damage / 2);
-LAB_080a0fec:
- TCombat::AddDamageToCombatList(&this->Combat,atk->ID,(ushort)pOVar14);
- }
- }
- if (DamageType == 4) {
- unique0x00009680 = (this->CrObject).ObjectID;
- iVar5 = 0x10;
-LAB_080a17a3:
- GraphicalEffect((Object *)(Remark + 0x1c),iVar5);
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)0xc6;
-LAB_080a1080:
- pcVar15 = "%d";
- TextualEffect();
- }
- else {
- if (4 < DamageType) {
- if (DamageType == 8) {
-switchD_080a1623_caseD_4:
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),0xc);
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)0x23;
- }
- else {
- if (DamageType != 0x100) goto LAB_080a102f;
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),0xe);
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)0xb4;
- }
- goto LAB_080a1080;
- }
- if (DamageType != 1) {
- if (DamageType != 2) {
-LAB_080a102f:
- error(&DAT_08101f00,DamageType);
- return (int)(Object *)Damage;
- }
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),9);
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)0x1e;
- goto LAB_080a1080;
- }
- pcVar15 = (char *)this->Race;
- switch(RaceData[(int)pcVar15].Blood) {
- case BT_BLOOD:
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),1);
- unique0x00009680 = (this->CrObject).ObjectID;
- TextualEffect();
- local_3c[0].ObjectID = (this->CrObject).ObjectID;
- // try { // try from 080a1684 to 080a16b8 has its CatchHandler @ 080a1fb1
- GetMapContainer((Object *)(Remark + 0x1c));
- pOVar14 = &local_2c;
- GetSpecialObject((SPECIALMEANING)pOVar14);
- pcVar15 = (char *)0x5;
- CreatePool();
- break;
- case BT_SLIME:
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),0x11);
- unique0x00009680 = (this->CrObject).ObjectID;
- TextualEffect();
- local_3c[0].ObjectID = (this->CrObject).ObjectID;
- // try { // try from 080a171a to 080a174e has its CatchHandler @ 080a1fb1
- GetMapContainer((Object *)(Remark + 0x1c));
- pOVar14 = &local_2c;
- GetSpecialObject((SPECIALMEANING)pOVar14);
- pcVar15 = (char *)0x6;
- CreatePool();
- break;
- case BT_BONES:
- unique0x00009680 = (this->CrObject).ObjectID;
- GraphicalEffect((Object *)(Remark + 0x1c),10);
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)0x81;
- goto LAB_080a1080;
- case BT_FIRE:
- unique0x00009680 = (this->CrObject).ObjectID;
- iVar5 = 1;
- goto LAB_080a17a3;
- case BT_ENERGY:
- goto switchD_080a1623_caseD_4;
- default:
- pOVar14 = (Object *)RaceData[(int)pcVar15].Blood;
- error(&DAT_08101fe0,pOVar14,pcVar15);
- }
- }
- if ((this->Type == PLAYER) &&
- (pTVar1 = this->Connection, pTVar1 != (TConnection *)0x0)) {
- if (atk == (TCreature *)0x0) {
- pcVar15 = "You lose %d hitpoint%s.";
- pcVar13 = "";
- if ((Object *)Damage != (Object *)0x1) {
- pcVar13 = "s";
- }
- pOVar14 = (Object *)0x15;
- SendMessage(pTVar1,0x15,"You lose %d hitpoint%s.",Damage,pcVar13);
- }
- else {
- pcVar13 = "";
- pcVar15 = "You lose %d hitpoint%s due to an attack by %s.";
- pOVar14 = (Object *)0x15;
- if ((Object *)Damage != (Object *)0x1) {
- pcVar13 = "s";
- }
- SendMessage(pTVar1,0x15,"You lose %d hitpoint%s due to an attack by %s."
- ,Damage,pcVar13,atk->Name);
- }
- SendPlayerData(this->Connection);
- }
- iVar5 = TSkill::Get(pTVar11);
- if (0 < iVar5) goto LAB_080a1103;
- if (this->Type == PLAYER) {
- OldLevel = 1;
- do {
- pOVar14 = (Object *)this->ID;
- pcVar15 = (char *)OldLevel;
- GetBodyObject((ulong)(Remark + 0x1c),(int)pOVar14);
- local_3c[0].ObjectID = ::NONE.ObjectID;
- if (stack0xffffffb4 != ::NONE.ObjectID) {
- Object::getObjectType(local_3c);
- pOVar14 = (Object *)0x2e;
- bVar2 = ObjectType::getFlag((ObjectType *)local_3c,CLOTHES);
- if (bVar2) {
- Object::getObjectType(local_3c);
- pOVar14 = (Object *)0x16;
- uVar8 = ObjectType::getAttribute
- ((ObjectType *)local_3c,BODYPOSITION);
- if (uVar8 == OldLevel) {
- Object::getObjectType(local_3c);
- pcVar15 = (char *)0xc;
- pOVar14 = (Object *)0x4d;
- GetNewObjectType((uchar)&local_2c,'M');
- if (local_3c[0].ObjectID == local_2c.ObjectID) {
- pcVar15 = this->Name;
- Log("game","%s stirbt mit Amulett of Loss.\n",pcVar15);
- this->LoseInventory = 0;
- pOVar14 = (Object *)0xffffffff;
- local_3c[0].ObjectID = stack0xffffffb4;
- // try { // try from 080a159b to 080a159f has its CatchHandler @ 080a1fc8
- Delete(local_3c,-1);
- }
- }
- }
- }
- OldLevel = OldLevel + 1;
- } while (OldLevel < 0xb);
- }
- iVar5 = TSkill::Get((this->super_TSkillBase).Skills[0]);
- (*this->_vptr_TCreature[2])(this,pOVar14,pcVar15);
- if ((atk != (TCreature *)0x0) && (this->Type == PLAYER)) {
- BlockLogout(atk,900,true);
- }
- if ((atkID != 0) && (this->Type == PLAYER)) {
- BlockLogout((TCreature *)atkID,900,true);
- }
- Responsible = (TCreature *)((uint)Responsible & 0xffffff00);
- local_88 = 0;
- if (atk == (TCreature *)0x0) {
- iVar9 = this->Race;
- iVar7 = strcmp(RaceData[0].Name,"human");
- if (iVar7 == 0) {
- KilledCreatures[iVar9] = KilledCreatures[iVar9] + 1;
- }
- uVar16 = 6;
- iVar9 = strcmp(RaceData[iVar9].Name,"human");
- if (iVar9 == 0) {
- KilledPlayers[0] = KilledPlayers[0] + 1;
- }
- if (DamageType == 2) {
- Responsible = (TCreature *)0x73696f70;
- Remark[0] = 'o';
- Remark[1] = 'n';
-LAB_080a1479:
- Remark[2] = '\0';
- }
- else if (DamageType < 3) {
- if (DamageType == 1) {
- Responsible = (TCreature *)0x69682061;
- Remark[0] = 't';
- Remark[1] = '\0';
- }
- else {
-LAB_080a1436:
- error(&DAT_08101f80,DamageType,uVar16);
- }
- }
- else {
- if (DamageType != 4) {
- if (DamageType == 8) {
- Responsible = (TCreature *)0x72656e65;
- Remark[0] = 'g';
- Remark[1] = 'y';
- goto LAB_080a1479;
- }
- goto LAB_080a1436;
- }
- Responsible = (TCreature *)0x65726966;
- Remark._0_2_ = Remark._0_2_ & 0xff00;
- }
- }
- else {
- iVar9 = atk->Race;
- iVar7 = this->Race;
- iVar6 = strcmp(RaceData[iVar9].Name,"human");
- if (iVar6 == 0) {
- KilledCreatures[iVar7] = KilledCreatures[iVar7] + 1;
- }
- iVar7 = strcmp(RaceData[iVar7].Name,"human");
- if (iVar7 == 0) {
- KilledPlayers[iVar9] = KilledPlayers[iVar9] + 1;
- }
- strcpy(this->Murderer,atk->Name);
- if ((atkID != 0) && (*(int *)(atkID + 0x28c) == 0)) {
- local_88 = *(uint *)(atkID + 0x208);
- }
- if (atk->Type != PLAYER) {
- strcpy((char *)&Responsible,atk->Name);
- }
- }
- if (this->Type == PLAYER) {
- Unjustified = 1;
- do {
- uVar8 = local_88;
- if (Unjustified != 1) {
- uVar8 = TCombat::GetMostDangerousAttacker(&this->Combat);
- }
- if ((Unjustified != 2) ||
- (((uVar8 != 0 && (uVar8 != local_88)) && (uVar8 < 0x40000000)))) {
- bVar2 = false;
- if (uVar8 - 1 < 0x3fffffff) {
- this_00 = GetPlayer(uVar8);
- if (this_00 == (TPlayer *)0x0) goto LAB_080a1376;
- bVar2 = TPlayer::IsAttackJustified(this_00,this->ID);
- bVar2 = !bVar2;
- TPlayer::RecordMurder(this_00,this->ID);
- }
- bVar3 = false;
- if (this->ID != uVar8) {
- bVar3 = bVar2;
- }
- CharacterDeathOrder(this,iVar5,uVar8,(char *)&Responsible,bVar3);
- Responsible = (TCreature *)((uint)Responsible & 0xffffff00);
- }
-LAB_080a1376:
- Unjustified = Unjustified + 1;
- } while (Unjustified < 3);
- }
-LAB_080a1103:
- AnnounceChangedCreature(this->ID,1);
- return (int)(Object *)Damage;
- }
- }
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)(Remark + 0x1c);
- iVar5 = 4;
- goto LAB_080a1a50;
- }
-LAB_080a1c71:
- unique0x00009680 = (this->CrObject).ObjectID;
- pOVar14 = (Object *)(Remark + 0x1c);
- }
- iVar5 = 3;
-LAB_080a1a50:
- GraphicalEffect(pOVar14,iVar5);
- return 0;
-}
-
-
-
// WARNING: Variable defined which should be unmapped: Execute_local
// DWARF original prototype: bool MovePossible(TCreature * this, int x, int y, int z, bool Execute,
// bool Jump)
diff --git a/src/crcombat.cc b/src/crcombat.cc
index 7ad7534..2843cd3 100644
--- a/src/crcombat.cc
+++ b/src/crcombat.cc
@@ -839,10 +839,17 @@ void TCombat::DistanceAttack(TCreature *Target){
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);
+ try{
+ if(random(0, 99) < Fragility){
+ Delete(this->Ammo, 1);
+ }else{
+ Move(0, this->Ammo, DropCon, 1, false, NONE);
+ }
+ }catch(RESULT err){
+ if(err != DESTROYED){
+ error("TCombat::RangeAttack: Konnte Ammo nicht verschieben/löschen"
+ " (Exception %d, [%d,%d,%d].\n", err, DropX, DropY, DropZ);
+ }
}
if(!Hit){
@@ -918,11 +925,12 @@ void TCombat::DistributeExperiencePoints(uint32 Exp){
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(AttackerLevel <= MaxLevel){
+ continue;
+ }
+
+ Amount = ((MaxLevel - AttackerLevel) * Amount) / MasterLevel;
+ print(3, "%s erhält %d EXP.\n", Attacker->Name, Amount);
}
if(Amount > 0){
@@ -944,8 +952,8 @@ void TCombat::DistributeExperiencePoints(uint32 Exp){
}
}
- Attacker->Skills[0]->Increase(Amount);
- TextualEffect(Attacker->CrObject, 0xD7, "%d", Amount);
+ Attacker->Skills[SKILL_LEVEL]->Increase(Amount);
+ TextualEffect(Attacker->CrObject, COLOR_WHITE, "%d", Amount);
}
}
}
diff --git a/src/creature.cc b/src/creature.cc
index a02cb82..4fee0b3 100644
--- a/src/creature.cc
+++ b/src/creature.cc
@@ -1,4 +1,5 @@
#include "creature.hh"
+#include "monster.hh"
#include "enums.hh"
#include "stubs.hh"
@@ -54,3 +55,418 @@ TCreature::TCreature(void) :
void TCreature::Attack(void){
this->Combat.Attack();
}
+
+// TODO(fusion): This probably belongs elsewhere but we should come back to
+// this when we're wrapping up creature files.
+static bool IsCreaturePlayer(uint32 CreatureID){
+ return CreatureID < 0x40000000;
+}
+
+// TODO(fusion): This probably belongs elsewhere but we should come back to
+// this when we're wrapping up creature files.
+static void AddKillStatistics(int AttackerRace, int DefenderRace){
+ // NOTE(fusion): I think the race name can be "human" only for players,
+ // which means we're probably tracking how many creatures are killed by
+ // players with `KilledCreatures`, and how many players are killed by
+ // creatures with `KilledPlayers`.
+
+ if(strcmp(RaceData[AttackerRace].Name, "human") == 0){
+ KilledCreatures[DefenderRace] += 1;
+ }
+
+ // NOTE(fusion): This seems to track how many players are killed by
+ if(strcmp(RaceData[DefenderRace].Name, "human") == 0){
+ KilledPlayers[AttackerRace] += 1;
+ }
+}
+
+int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){
+ if(this->IsDead || this->Type == NPC){
+ return 0;
+ }
+
+ if(this->Type == PLAYER){
+ if(this->Connection != NULL && Attacker != NULL){
+ SendMarkCreature(this->Connection, Attacker->ID, COLOR_BLACK);
+ }
+
+ if(Attacker != NULL && Attacker->Type == PLAYER
+ && DamageType != DAMAGE_POISON_PERIODIC
+ && DamageType != DAMAGE_FIRE_PERIODIC
+ && DamageType != DAMAGE_ENERGY_PERIODIC){
+ Damage = (Damage + 1) / 2;
+ }
+ }
+
+ // NOTE(fusion): `Responsible` is either the attacker or its master. It is
+ // always valid if `Attacker` is not NULL.
+ TCreature *Responsible = Attacker;
+ if(Attacker != NULL){
+ uint32 MasterID = Attacker->GetMaster();
+ if(MasterID != 0){
+ Responsible = GetCreature(MasterID);
+ }
+
+ Attacker->BlockLogout(60, this->Type == PLAYER);
+ if(Responsible != Attacker){
+ Responsible->BlockLogout(60, this->Type == PLAYER);
+ }
+
+ if(this->Type == PLAYER && Responsible->Type == PLAYER){
+ ((TPlayer*)Responsible)->RecordAttack(this->ID);
+ }
+ }
+
+ if(this->Type == PLAYER){
+ if(CheckRight(this->ID, INVULNERABLE)){
+ Damage = 0;
+ }
+
+ // NOTE(fusion): We're iterating over the victim's inventory to apply
+ // damage reductions, while the damage is greater than zero.
+ for(int Position = 1;
+ Position <= 10 && Damage > 0;
+ Position += 1){
+ Object Obj = GetBodyObject(this->ID, Position);
+ if(Obj == NONE){
+ continue;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(PROTECTION) && ObjType.getFlag(CLOTHES)
+ && (int)ObjType.getAttribute(BODYPOSITION) == Position
+ && (ObjType.getAttribute(PROTECTIONDAMAGETYPES) & DamageType) != 0){
+ int DamageReduction = ObjType.getAttribute(DAMAGEREDUCTION);
+ Damage = (Damage * (100 - DamageReduction)) / 100;
+ if(ObjType.getFlag(WEAROUT)){
+ // TODO(fusion): Ugh... The try..catch block might be used only
+ // when changing the object's type.
+ try{
+ uint32 RemainingUses = Obj.getAttribute(REMAININGUSES);
+ if(RemainingUses > 1){
+ Change(Obj, REMAININGUSES, RemainingUses - 1);
+ }else{
+ ObjectType WearOutType = ObjType.getAttribute(WEAROUTTARGET);
+ Change(Obj, WearOutType, 0);
+ }
+ }catch(RESULT err){
+ error("TCreature::Damage: Exception %d beim Abnutzen von Objekt %d.\n",
+ err, ObjType.TypeID);
+ }
+ }
+ }else if(ObjType.getFlag(PROTECTION) && !ObjType.getFlag(CLOTHES)){
+ error("TCreature::Damage: Objekt %d hat PROTECTION, aber nicht CLOTHES.\n",
+ ObjType.TypeID);
+ }
+ }
+ }
+
+ if(Damage <= 0){
+ GraphicalEffect(this->CrObject, EFFECT_POFF);
+ return 0;
+ }
+
+ if(DamageType == DAMAGE_POISON_PERIODIC){
+ if(RaceData[this->Race].NoPoison){
+ return 0;
+ }
+
+ if(Damage > this->Skills[SKILL_POISON]->TimerValue()){
+ this->PoisonDamageOrigin = Attacker->ID;
+ this->SetTimer(SKILL_POISON, Damage, 3, 3, -1);
+ }
+
+ this->DamageStimulus(Attacker->ID, Damage, DamageType);
+ return Damage;
+ }else if(DamageType == DAMAGE_FIRE_PERIODIC){
+ if(RaceData[this->Race].NoBurning){
+ return 0;
+ }
+
+ this->FireDamageOrigin = Attacker->ID;
+ this->SetTimer(SKILL_BURNING, Damage / 10, 8, 8, -1);
+ this->DamageStimulus(Attacker->ID, Damage, DamageType);
+ return Damage;
+ }else if(DamageType == DAMAGE_ENERGY_PERIODIC){
+ if(RaceData[this->Race].NoEnergy){
+ return 0;
+ }
+
+ // TODO(fusion): Shouldn't we use `Damage / 25` here?
+ this->EnergyDamageOrigin = Attacker->ID;
+ this->SetTimer(SKILL_ENERGY, Damage / 20, 10, 10, -1);
+ this->DamageStimulus(Attacker->ID, Damage, DamageType);
+ return Damage;
+ }
+
+ if((DamageType == DAMAGE_PHYSICAL && RaceData[this->Race].NoHit)
+ || (DamageType == DAMAGE_POISON && RaceData[this->Race].NoPoison)
+ || (DamageType == DAMAGE_FIRE && RaceData[this->Race].NoBurning)
+ || (DamageType == DAMAGE_ENERGY && RaceData[this->Race].NoEnergy)
+ || (DamageType == DAMAGE_LIFEDRAIN && RaceData[this->Race].NoLifeDrain)){
+ GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT);
+ return 0;
+ }
+
+ if(DamageType == DAMAGE_PHYSICAL){
+ Damage -= this->Combat.GetArmorStrength();
+ if(Damage <= 0){
+ GraphicalEffect(this->CrObject, EFFECT_BLOCK_HIT);
+ return 0;
+ }
+ }
+
+ this->DamageStimulus(Attacker->ID, Damage, DamageType);
+
+ // NOTE(fusion): Remove non-player illusion. Might as well be an inlined
+ // function.
+ if(this->Type != PLAYER
+ && this->Outfit.OutfitID == 0
+ && this->Outfit.ObjectType == 0){
+ this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1);
+ this->Outfit = this->OrgOutfit;
+ AnnounceChangedCreature(this->ID, 3); // CREATURE_OUTFIT_CHANGED ?
+ NotifyAllCreatures(this->CrObject, 2, NONE); // CREATURE_APPEAR ?
+ }
+
+ if(DamageType == DAMAGE_MANADRAIN){
+ int ManaPoints = this->Skills[SKILL_MANA]->Get();
+ if(Damage > ManaPoints){
+ Damage = ManaPoints;
+ }
+
+ if(Damage > 0){
+ this->Skills[SKILL_MANA]->Change(-Damage);
+ if(this->Type == PLAYER && this->Connection != NULL){
+ SendMessage(this->Connection, TALK_STATUS_MESSAGE,
+ "You lose %d mana.", Damage);
+ }
+ GraphicalEffect(this->CrObject, EFFECT_MAGIC_RED);
+ TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage);
+ }
+
+ return Damage;
+ }
+
+ if(this->Skills[SKILL_MANASHIELD]->TimerValue() > 0
+ || this->Skills[SKILL_MANASHIELD]->Get() > 0){
+ // NOTE(fusion): We only send these if the attack was fully absorbed,
+ // else it'd be overwritten by whatever effect and messages we send
+ // next, when the victim's hitpoints are actually touched.
+ int ManaPoints = this->Skills[SKILL_MANA]->Get();
+ if(Damage <= ManaPoints){
+ this->Skills[SKILL_MANA]->Change(-Damage);
+ GraphicalEffect(this->CrObject, EFFECT_MANA_HIT);
+ TextualEffect(this->CrObject, COLOR_BLUE, "%d", Damage);
+ if(this->Type == PLAYER && this->Connection != NULL){
+ if(Attacker != NULL){
+ SendMessage(this->Connection, TALK_STATUS_MESSAGE,
+ "You lose %d mana blocking an attack by %s.",
+ Damage, Attacker->Name);
+ }else{
+ SendMessage(this->Connection, TALK_STATUS_MESSAGE,
+ "You lose %d mana.", Damage);
+ }
+ SendPlayerData(this->Connection);
+ }
+ return Damage;
+ }
+
+ this->Skills[SKILL_MANA]->Set(0);
+ Damage -= ManaPoints;
+ }
+
+ int HitPoints = this->Skills[SKILL_HITPOINTS]->Get();
+ if(Damage > HitPoints){
+ Damage = HitPoints;
+ }
+
+ this->Skills[SKILL_HITPOINTS]->Change(-Damage);
+ if(Responsible != NULL){
+ ASSERT(Attacker != NULL);
+ if(Responsible == Attacker){
+ this->Combat.AddDamageToCombatList(Attacker->ID, Damage);
+ }else{
+ this->Combat.AddDamageToCombatList(Attacker->ID, Damage / 2);
+ this->Combat.AddDamageToCombatList(Responsible->ID, Damage / 2);
+ }
+ }
+
+ int HitEffect = EFFECT_NONE;
+ int TextColor = COLOR_BLACK;
+ int SplashLiquid = LIQUID_NONE;
+ if(DamageType == DAMAGE_PHYSICAL){
+ switch(RaceData[this->Race].Blood){
+ case BT_BLOOD:{
+ HitEffect = EFFECT_BLOOD_HIT;
+ TextColor = COLOR_RED;
+ SplashLiquid = LIQUID_BLOOD;
+ break;
+ }
+ case BT_SLIME:{
+ HitEffect = EFFECT_POISON_HIT;
+ TextColor = COLOR_LIGHTGREEN;
+ SplashLiquid = LIQUID_SLIME;
+ break;
+ }
+ case BT_BONES:{
+ HitEffect = EFFECT_BONE_HIT;
+ TextColor = COLOR_LIGHTGRAY;
+ break;
+ }
+ case BT_FIRE:{
+ HitEffect = EFFECT_FIRE_HIT;
+ TextColor = COLOR_ORANGE;
+ break;
+ }
+ case BT_ENERGY:{
+ HitEffect = EFFECT_ENERGY_HIT;
+ TextColor = COLOR_LIGHTBLUE;
+ break;
+ }
+ default:{
+ error("TCreature::Damage: Ungültiger Bluttyp %d für Rasse %d.\n",
+ RaceData[this->Race].Blood, this->Race);
+ break;
+ }
+ }
+ }else if(Damage == DAMAGE_POISON){
+ HitEffect = EFFECT_POISON;
+ TextColor = COLOR_LIGHTGREEN;
+ }else if(DamageType == DAMAGE_FIRE){
+ HitEffect = EFFECT_FIRE_HIT;
+ TextColor = COLOR_ORANGE;
+ }else if(DamageType == DAMAGE_ENERGY){
+ HitEffect = EFFECT_ENERGY_HIT;
+ TextColor = COLOR_LIGHTBLUE;
+ }else if(DamageType == DAMAGE_LIFEDRAIN){
+ HitEffect = EFFECT_MAGIC_RED;
+ TextColor = COLOR_RED;
+ }else{
+ // TODO(fusion): The original decompiled function would return here but
+ // I don't think it's a good idea because it would skip death handling.
+ error("TCreature::Damage: Ungültiger Schadenstyp %d.\n", DamageType);
+ //return Damage;
+ }
+
+ if(HitEffect != EFFECT_NONE){
+ GraphicalEffect(this->CrObject, HitEffect);
+ TextualEffect(this->CrObject, TextColor, "%d", Damage);
+ if(SplashLiquid != LIQUID_NONE){
+ CreatePool(GetMapContainer(this->CrObject),
+ GetSpecialObject(BLOOD_SPLASH),
+ SplashLiquid);
+ }
+ }
+
+ if(this->Type == PLAYER && this->Connection != NULL){
+ if(Attacker != NULL){
+ SendMessage(this->Connection, TALK_STATUS_MESSAGE,
+ "You lose %d hitpoint%s due to an attack by %s.",
+ Damage, (Damage == 1 ? "" : "s"), Attacker->Name);
+ }else{
+ SendMessage(this->Connection, TALK_STATUS_MESSAGE,
+ "You lose %d hitpoint%s.",
+ Damage, (Damage == 1 ? "" : "s"));
+ }
+ SendPlayerData(this->Connection);
+ }
+
+ if(Damage == HitPoints){
+ if(this->Type == PLAYER){
+ for(int Position = 1; Position <= 10; Position += 1){
+ Object Obj = GetBodyObject(this->ID, Position);
+ if(Obj == NONE){
+ continue;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(CLOTHES) && (int)ObjType.getAttribute(BODYPOSITION) == Position){
+ ObjectType AmuletOfLossType = GetNewObjectType(77, 12);
+ if(ObjType == AmuletOfLossType){
+ Log("game", "%s stirbt mit Amulett of Loss.\n", this->Name);
+ this->LoseInventory = 0;
+ Delete(Obj, -1);
+ // TODO(fusion): Shouldn't we break here? We could also
+ // just check if there is an amulet of loss in the necklace
+ // container instead of iterating over all of them.
+ }
+ }
+ }
+ }
+
+ int OldLevel = this->Skills[SKILL_LEVEL]->Get();
+ this->Death();
+ if(Attacker != NULL && this->Type == PLAYER){
+ Attacker->BlockLogout(900, true);
+ if(Responsible != Attacker){
+ Responsible->BlockLogout(900, true);
+ }
+ }
+
+ uint32 MurdererID = 0;
+ char Remark[30] = {};
+ if(Attacker == NULL){
+ AddKillStatistics(0, this->Race);
+ if(DamageType == DAMAGE_PHYSICAL){
+ strcpy(Remark, "a hit");
+ }else if(DamageType == DAMAGE_POISON){
+ strcpy(Remark, "poison");
+ }else if(DamageType == DAMAGE_FIRE){
+ strcpy(Remark, "fire");
+ }else if(DamageType == DAMAGE_ENERGY){
+ strcpy(Remark, "energy");
+ }else{
+ // NOTE(fusion): We probably don't expect any other damage type
+ // as the cause of death when there is no attacker.
+ error("TCreature::Damage: Ungültiger Schadenstyp %d als Todesursache.\n", DamageType);
+ }
+ }else{
+ AddKillStatistics(Attacker->Race, this->Race);
+ strcpy(this->Murderer, Attacker->Name);
+
+ if(Responsible->Type == PLAYER){
+ MurdererID = Responsible->ID;
+ }
+
+ if(Attacker->Type != PLAYER){
+ strcpy(Remark, Attacker->Name);
+ }
+ }
+
+ if(this->Type == PLAYER){
+ if(MurdererID != 0 && MurdererID != this->ID){
+ bool Justified = true;
+ if(IsCreaturePlayer(MurdererID)){
+ TPlayer *Murderer = GetPlayer(MurdererID);
+ if(Murderer != NULL){
+ Justified = Murderer->IsAttackJustified(this->ID);
+ Murderer->RecordMurder(this->ID);
+ }
+ }
+ CharacterDeathOrder(this, OldLevel, MurdererID, Remark, !Justified);
+ }
+
+ uint32 MostDangerousID = this->Combat.GetMostDangerousAttacker();
+ if(MostDangerousID != 0
+ && MostDangerousID != MurdererID
+ && MostDangerousID != this->ID
+ && IsCreaturePlayer(MostDangerousID)){
+ bool Justified = true;
+ TPlayer *MostDangerous = GetPlayer(MostDangerousID);
+ if(MostDangerous != NULL){
+ Justified = MostDangerous->IsAttackJustified(this->ID);
+ MostDangerous->RecordMurder(this->ID);
+ }
+
+ // TODO(fusion): The original function is confusing at this point
+ // but it seems correct that the remark is included only with the
+ // murderer.
+ CharacterDeathOrder(this, OldLevel, MostDangerousID, "", !Justified);
+ }
+ }
+ }
+
+ AnnounceChangedCreature(this->ID, 1); // CREATURE_HITPOINTS_CHANGED ?
+ return Damage;
+}
diff --git a/src/enums.hh b/src/enums.hh
index 4081dce..6d02208 100644
--- a/src/enums.hh
+++ b/src/enums.hh
@@ -45,10 +45,16 @@ enum ChaseMode: uint8 {
CHASE_MODE_RANGE = 2,
};
-enum CreatureType: int {
- PLAYER = 0,
- MONSTER = 1,
- NPC = 2,
+// NOTE(fusion): Not in debug symbols.
+enum Color: int {
+ COLOR_BLACK = 0,
+ COLOR_BLUE = 5,
+ COLOR_LIGHTGREEN = 30,
+ COLOR_LIGHTBLUE = 35,
+ COLOR_LIGHTGRAY = 129,
+ COLOR_RED = 180,
+ COLOR_ORANGE = 198,
+ COLOR_WHITE = 215,
};
enum CONNECTIONSTATE: int {
@@ -62,6 +68,12 @@ enum CONNECTIONSTATE: int {
CONNECTION_DISCONNECTED = 7,
};
+enum CreatureType: int {
+ PLAYER = 0,
+ MONSTER = 1,
+ NPC = 2,
+};
+
// NOTE(fusion): Not in debug symbols.
enum DamageType: int {
DAMAGE_NONE = 0x0000,
@@ -77,9 +89,20 @@ enum DamageType: int {
DAMAGE_MANADRAIN = 0x0200,
};
+// TODO(fusion): Review these names.
enum EffectType: int {
+ EFFECT_NONE = 0,
+ EFFECT_BLOOD_HIT = 1,
+ EFFECT_MANA_HIT = 2,
EFFECT_POFF = 3,
- EFFECT_BURST_ARROW = 7, // Review
+ EFFECT_BLOCK_HIT = 4,
+ EFFECT_BURST_ARROW = 7,
+ EFFECT_POISON = 9,
+ EFFECT_BONE_HIT = 10,
+ EFFECT_ENERGY_HIT = 12,
+ EFFECT_MAGIC_RED = 14,
+ EFFECT_FIRE_HIT = 16,
+ EFFECT_POISON_HIT = 17,
};
enum FLAG: int {
diff --git a/src/monster.hh b/src/monster.hh
index dd246a1..f7ce3f5 100644
--- a/src/monster.hh
+++ b/src/monster.hh
@@ -85,6 +85,9 @@ struct TMonster: TNonplayer {
};
#endif
+//#define MAX_RACES 512
extern TRaceData RaceData[512];
+extern int KilledCreatures[512];
+extern int KilledPlayers[512];
#endif //TIBIA_MONSTER_HH_
diff --git a/src/player.hh b/src/player.hh
index 33ca7c3..b98882e 100644
--- a/src/player.hh
+++ b/src/player.hh
@@ -77,6 +77,7 @@ struct TPlayer: TCreature {
bool IsAttackJustified(uint32 Victim);
void RecordAttack(uint32 Victim);
+ void RecordMurder(uint32 Victim);
void CheckState(void);
diff --git a/src/stubs.hh b/src/stubs.hh
index d3cfcd0..8553177 100644
--- a/src/stubs.hh
+++ b/src/stubs.hh
@@ -19,17 +19,21 @@ extern void AbortWriter(void);
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 void CharacterDeathOrder(TCreature *Creature, int OldLevel,
+ uint32 Offender, const char *Remark, bool Unjustified);
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 CreatePool(Object Con, ObjectType Type, uint32 Value);
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 TPlayer *GetPlayer(uint32 CreatureID);
extern int GetRacePoison(int Race);
extern void GraphicalEffect(int x, int y, int z, int Type);
extern void GraphicalEffect(Object Obj, int Type);
@@ -61,6 +65,7 @@ extern void SendAll(void);
extern void SendAmbiente(TConnection *Connection);
extern void SendClearTarget(TConnection *Connection);
extern void SendMails(TPlayerData *PlayerData);
+extern void SendMarkCreature(TConnection *Connection, uint32 CreatureID, int Color);
extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ...) ATTR_PRINTF(3, 4);
extern void SendPlayerData(TConnection *Connection);
extern void SendPlayerSkills(TConnection *Connection);