impl TCreature::Damage + overall improvements

This commit is contained in:
fusion32 2025-05-31 22:18:08 -03:00
parent d7c11b2716
commit c4b06256d9
8 changed files with 475 additions and 644 deletions

View File

@ -1,4 +1,7 @@
## TODO NEXT
- TCreature::Damage
- MAP.CC FileNumber: uint32 -> uintptr
- MAGIC.CC
- TCreature
- TPlayer
- TNonPlayer

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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 {

View File

@ -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_

View File

@ -77,6 +77,7 @@ struct TPlayer: TCreature {
bool IsAttackJustified(uint32 Victim);
void RecordAttack(uint32 Victim);
void RecordMurder(uint32 Victim);
void CheckState(void);

View File

@ -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);