From ea319bc2fbef91e3ea062be554b215756648d1d3 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Wed, 25 Jun 2025 10:09:17 -0300 Subject: finish `crnonpl.cc` --- TODO.md | 1 + reference/game.c | 1725 ---------------------------------------------------- src/connections.cc | 3 +- src/cr.hh | 46 +- src/crcombat.cc | 8 +- src/crmain.cc | 6 +- src/crnonpl.cc | 1078 +++++++++++++++++++++++++++++++- src/magic.cc | 5 +- src/magic.hh | 7 + src/operate.cc | 13 +- src/stubs.hh | 3 - 11 files changed, 1120 insertions(+), 1775 deletions(-) diff --git a/TODO.md b/TODO.md index 251a0a8..fe5db50 100644 --- a/TODO.md +++ b/TODO.md @@ -11,6 +11,7 @@ - The map container uses type id zero but it seems to also be used as a "no type" id which makes me want to add some `isNone()`, `isNull()`, `isVoid()` check to `ObjectType` to be used as an alias when pertinent. +- Make some `TNonplayer` random step function? One version that does a random step and another that does a random step keeping some distance from a certain position. ## Stack allocations Any functions that use `alloca` or some other form of dynamic stack allocations will cause decompiled functions to be an absolute mess. It usually shows up in the decompiled code as both a size computation like `-(VAR + CONST & 0xfffffff0)`, followed by some assignment. It doesn't make total sense without looking at the disassembly. I've encountered ~30 such computations and expect the functions containing them to be amongt the most challenging/annoying to be properly decompiled. diff --git a/reference/game.c b/reference/game.c index c44699e..4b7b5b7 100644 --- a/reference/game.c +++ b/reference/game.c @@ -18537,1721 +18537,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority return; } -// WARNING: Globals starting with '_' overlap smaller symbols at the same address -// DWARF original prototype: void IdleStimulus(TMonster * this) - -void __thiscall TMonster::IdleStimulus(TMonster *this) - -{ - TSkill *id; - TOutfit _Outfit; - bool bVar1; - bool bVar2; - int dir; - TCreature *pTVar3; - uint uVar4; - int iVar5; - int iVar6; - uint uVar7; - uint uVar8; - uint uVar9; - TSpellData *pTVar10; - long lVar11; - ulong uVar12; - TCreature *pTVar13; - ulong *puVar14; - char *pcVar15; - TCreature *master; - STATE SVar16; - int *piVar17; - uint uVar18; - int dy_1; - int destX; - TCombat *this_00; - ulong uVar19; - int destY; - int iVar20; - TDamageImpact *this_01; - int Try; - TMonster *i; - int iVar21; - uint uVar22; - TMonster *pTVar23; - TCreature *target; - TCreature *target_1; - int Strategy; - int TieBreaker; - int Goodness; - int iStack_74; - bool Sleep; - int local_70; - int local_6c; - int x; - int y; - int z; - int x_1; - int y_1; - int z_1; - TOutfit HelpOutfit; - TFindCreatures Search; - - if ((this->super_TNonplayer).super_TCreature.LockToDo != false) { - return; - } - if ((this->super_TNonplayer).super_TCreature.LoggingOut != false) { - return; - } - uVar18 = (this->super_TNonplayer).super_TCreature.LifeEndRound; - if ((uVar18 != 0) && (uVar18 <= RoundNr)) { - print(); - TCreature::StartLogout((TCreature *)this,true,true); - goto LAB_080b97c3; - } - if (this->Master != 0) { - pTVar3 = GetCreature(this->Master); - if (pTVar3 == (TCreature *)0x0) { - if (0x3fffffff < this->Master) { - pcVar15 = (this->super_TNonplayer).super_TCreature.Name; - print(); - (*(this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills[2]->_vptr_TSkill - [2])((this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills[2],0,pcVar15 - ); - (*(this->super_TNonplayer).super_TCreature._vptr_TCreature[2])(this); - goto LAB_080b961e; - } - print(); -LAB_080b9665: - TCreature::StartLogout((TCreature *)this,true,true); - } - else { - uVar18 = this->Master; - if ((uVar18 < 0x40000000) && (pTVar3->SummonedCreatures == 0)) { - print(); - TCreature::StartLogout((TCreature *)this,true,true); - goto LAB_080bac12; - } - if (pTVar3 == (TCreature *)0x0) { - iVar5 = (this->super_TNonplayer).super_TCreature.posz; - iVar20 = _DAT_00000278; -LAB_080b96b4: - if ((iVar5 == iVar20) || (uVar18 < 0x40000000)) { - if ((pTVar3 == (TCreature *)0x0) || ((pTVar3->Combat).Following != false)) { - uVar12 = 0; - } - else { - uVar12 = (pTVar3->Combat).AttackDest; - } - this->Target = uVar12; - if ((uVar12 == 0) || (uVar12 == (this->super_TNonplayer).super_TCreature.ID)) { - this->Target = uVar18; - } - goto LAB_080b96fc; - } - } - else { - iVar5 = (this->super_TNonplayer).super_TCreature.posz; - uVar4 = iVar5 - pTVar3->posz; - if (0x7fffffff < uVar4) { - uVar4 = -uVar4; - } - if ((int)uVar4 < 2) { - uVar4 = (this->super_TNonplayer).super_TCreature.posx - pTVar3->posx; - if (0x7fffffff < uVar4) { - uVar4 = -uVar4; - } - if ((int)uVar4 < 0x1f) { - uVar4 = (this->super_TNonplayer).super_TCreature.posy - pTVar3->posy; - if (0x7fffffff < uVar4) { - uVar4 = -uVar4; - } - iVar20 = pTVar3->posz; - if ((int)uVar4 < 0x1f) goto LAB_080b96b4; - } - } - if (uVar18 < 0x40000000) { - print(); - goto LAB_080b9665; - } - } - (*(this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills[2]->_vptr_TSkill[2]) - ((this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills[2],0); - (*(this->super_TNonplayer).super_TCreature._vptr_TCreature[2])(this); - print(); - } -LAB_080b961e: - (this->super_TNonplayer).State = SLEEPING; - return; - } - bVar1 = MonsterhomeInRange(); - if (!bVar1) { - vector<>::operator()(&Monsterhome,this->Home); - vector<>::operator()(&Monsterhome,this->Home); - vector<>::operator()(&Monsterhome,this->Home); - print(); - TCreature::StartLogout((TCreature *)this,true,true); -LAB_080bac12: - (this->super_TNonplayer).State = SLEEPING; - return; - } -LAB_080b96fc: - if (this->Target == 0) { - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } - else { - pTVar3 = GetCreature(this->Target); - if (pTVar3 == (TCreature *)0x0) { -LAB_080baa94: - this->Target = 0; - } - else { - uVar18 = (this->super_TNonplayer).super_TCreature.posx - pTVar3->posx; - if (0x7fffffff < uVar18) { - uVar18 = -uVar18; - } - if (10 < (int)uVar18) goto LAB_080baa94; - uVar18 = (this->super_TNonplayer).super_TCreature.posy - pTVar3->posy; - if (0x7fffffff < uVar18) { - uVar18 = -uVar18; - } - if ((10 < (int)uVar18) || - ((this->super_TNonplayer).super_TCreature.posz != pTVar3->posz)) goto LAB_080baa94; - } - if (pTVar3 == (TCreature *)0x0) { - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } - else { - bVar1 = IsProtectionZone(); - if (bVar1) { - this->Target = 0; - } - bVar1 = IsHouse(pTVar3->posx,pTVar3->posy,pTVar3->posz); - if (bVar1) { - this->Target = 0; - } - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - if (RaceData[iVar5].SeeInvisible == false) { - bVar1 = false; - if (((pTVar3->Outfit).OutfitID == 0) && ((pTVar3->Outfit).field_1.ObjectType == 0)) - { - bVar1 = true; - } - if (bVar1) { - this->Target = 0; - } - } - } - if (this->Master == 0) { - lVar11 = random(); - if (lVar11 < RaceData[iVar5].LoseTarget) { - this->Target = 0; - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } - else { - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } - } - } - SVar16 = (this->super_TNonplayer).State; - if ((SVar16 != PANIC) && (SVar16 != UNDERATTACK)) { - (this->super_TNonplayer).State = IDLE; - } - if ((0 < RaceData[iVar5].Talks) && (iVar5 = rand(), iVar5 == (iVar5 / 0x32) * 0x32)) { - iVar5 = rand(); - puVar14 = vector<>::operator() - (&RaceData[(this->super_TNonplayer).super_TCreature.Race].Talk, - iVar5 % RaceData[(this->super_TNonplayer).super_TCreature.Race].Talks + - 1); - pcVar15 = GetDynamicString(*puVar14); - if (*pcVar15 == '#') { - pcVar15 = pcVar15 + 3; - iVar5 = 0x11; - uVar12 = (this->super_TNonplayer).super_TCreature.ID; - } - else { - iVar5 = 0x10; - uVar12 = (this->super_TNonplayer).super_TCreature.ID; - } - // try { // try from 080baa0a to 080baa0e has its CatchHandler @ 080bade6 - Talk(uVar12,iVar5,(char *)0x0,pcVar15,false); - } - uVar12 = this->Target; - if (uVar12 == 0) { - bVar1 = true; - if (this->Master == 0) { - TieBreaker = -0x80000000; - iVar5 = 0; - Strategy = 0; - target_1 = (TCreature *)0x0; - lVar11 = random(); - do { - piVar17 = RaceData[(this->super_TNonplayer).super_TCreature.Race].Strategy + iVar5; - if (lVar11 < *piVar17) break; - target_1 = (TCreature *)((int)&target_1->_vptr_TCreature + 1); - iVar5 = iVar5 + 1; - lVar11 = lVar11 - *piVar17; - } while (iVar5 < 3); - TFindCreatures::TFindCreatures - ((TFindCreatures *)&HelpOutfit.field_1,0xc,0xc, - (this->super_TNonplayer).super_TCreature.ID,5); - uVar12 = TFindCreatures::getNext((TFindCreatures *)&HelpOutfit.field_1); - if (uVar12 == 0) { - uVar12 = this->Target; - } - else { - do { - pTVar3 = GetCreature(uVar12); - if (pTVar3 == (TCreature *)0x0) { - error("TMonster::IdleStimulus: Kreatur existiert nicht.\n"); - uVar12 = this->Target; - goto LAB_080b9794; - } - if ((pTVar3->Type != MONSTER) || - (((id = pTVar3[1].super_TSkillBase.Skills[1], id != (TSkill *)0x0 && - (pTVar13 = GetCreature((ulong)id), pTVar13 != (TCreature *)0x0)) && - (pTVar13->Type == PLAYER)))) { - iVar5 = pTVar3->posz; - if (iVar5 < 8) { - iVar20 = (this->super_TNonplayer).super_TCreature.posz; - if (iVar20 < 8) { -LAB_080ba913: - bVar1 = false; - } - } - else { - iVar20 = (this->super_TNonplayer).super_TCreature.posz; - if ((iVar20 - iVar5) + 2U < 5) goto LAB_080ba913; - } - if ((iVar20 == iVar5) && - ((pTVar3->Type != PLAYER || - (bVar2 = CheckRight(pTVar3->ID,IGNORED_BY_MONSTERS), !bVar2)))) { - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].SeeInvisible - == false) { - bVar2 = false; - if (((pTVar3->Outfit).OutfitID == 0) && - ((pTVar3->Outfit).field_1.ObjectType == 0)) { - bVar2 = true; - } - if (bVar2) goto LAB_080ba73a; - } - bVar2 = IsProtectionZone(); - if ((!bVar2) && - (bVar2 = IsHouse(pTVar3->posx,pTVar3->posy,pTVar3->posz), !bVar2)) { - uVar18 = (this->super_TNonplayer).super_TCreature.posx - - pTVar3->posx; - if (0x7fffffff < uVar18) { - uVar18 = -uVar18; - } - uVar4 = (this->super_TNonplayer).super_TCreature.posy - pTVar3->posy - ; - if (0x7fffffff < uVar4) { - uVar4 = -uVar4; - } - if (((int)uVar18 < 0xb) && ((int)uVar4 < 0xb)) { - if (target_1 == (TCreature *)0x1) { - iVar5 = TSkill::Get((pTVar3->super_TSkillBase).Skills[2]); -LAB_080ba8dc: - uVar19 = -iVar5; - } - else if ((int)target_1 < 2) { - if (target_1 == (TCreature *)0x0) { - iVar5 = uVar4 + uVar18; - goto LAB_080ba8dc; - } -LAB_080ba860: - uVar19 = 0; - error("TMonster::IdleStimulus: Unbekannte Strategie %d.\n", - target_1); - } - else if (target_1 == (TCreature *)0x2) { - uVar19 = TCombat::GetDamageByCreature - (&(this->super_TNonplayer). - super_TCreature.Combat,pTVar3->ID); - } - else { - uVar19 = 0; - if (target_1 != (TCreature *)0x3) goto LAB_080ba860; - } - lVar11 = random(); - if ((TieBreaker < (int)uVar19) || - ((uVar19 == TieBreaker && (Strategy < lVar11)))) { - this->Target = uVar12; - Strategy = lVar11; - TieBreaker = uVar19; - } - } - } - } - } -LAB_080ba73a: - uVar12 = TFindCreatures::getNext((TFindCreatures *)&HelpOutfit.field_1); - } while (uVar12 != 0); - uVar12 = this->Target; - } - } -LAB_080b9794: - if (uVar12 == 0) { - if (bVar1) { - SVar16 = (this->super_TNonplayer).State; - if ((SVar16 != UNDERATTACK) && (SVar16 != PANIC)) { - if (this->Master == 0) { -LAB_080b97c3: - (this->super_TNonplayer).State = SLEEPING; - return; - } - TCreature::ToDoWait((TCreature *)this,1000); - goto LAB_080b97e9; - } - } - else { - SVar16 = (this->super_TNonplayer).State; - } - } - else { - SVar16 = (this->super_TNonplayer).State; - } - if ((uVar12 == 0) && (SVar16 == PANIC)) { - SVar16 = IDLE; - (this->super_TNonplayer).State = IDLE; - } - if (SVar16 == UNDERATTACK) { - (this->super_TNonplayer).State = IDLE; - } - } - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - if (0 < RaceData[iVar5].Spells) { - if (uVar12 == 0) { - target = (TCreature *)0x0; - } - else { - target = GetCreature(uVar12); - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } - i = (TMonster *)0x1; - if (RaceData[iVar5].Spells < 1) { - uVar12 = this->Target; - } - else { - do { - pTVar23 = i; - pTVar10 = vector::operator()(&RaceData[iVar5].Spell,(int)i); - iVar5 = rand(); - if (iVar5 % pTVar10->Delay == 0) { - if ((this->Master == 0) && - (iVar5 = TSkill::Get((this->super_TNonplayer).super_TCreature. - super_TSkillBase.Skills[2]), - iVar5 <= RaceData[(this->super_TNonplayer).super_TCreature.Race]. - FleeThreshold)) { - pTVar23 = (TMonster *)0x3; - lVar11 = random(); - if (lVar11 != 1) goto LAB_080ba242; - } - this_01 = (TDamageImpact *)0x0; - switch(pTVar10->Impact) { - case IMPACT_DAMAGE: - this_01 = (TDamageImpact *)operator_new(0x14); - // try { // try from 080ba1ca to 080ba1f0 has its CatchHandler @ 080bae1e - iVar5 = ComputeDamage((TCreature *)this,0,pTVar10->ImpactParam2, - pTVar10->ImpactParam3); - pTVar23 = this; - TDamageImpact::TDamageImpact - (this_01,(TCreature *)this,pTVar10->ImpactParam1,iVar5,true); - break; - case IMPACT_FIELD: - this_01 = (TDamageImpact *)operator_new(0xc); - pTVar23 = this; - // try { // try from 080ba3a9 to 080ba3ad has its CatchHandler @ 080bae30 - TFieldImpact::TFieldImpact - ((TFieldImpact *)this_01,(TCreature *)this,pTVar10->ImpactParam1); - break; - case IMPACT_HEALING: - this_01 = (TDamageImpact *)operator_new(0xc); - // try { // try from 080ba3dd to 080ba3f4 has its CatchHandler @ 080bae32 - iVar5 = ComputeDamage((TCreature *)this,0,pTVar10->ImpactParam1, - pTVar10->ImpactParam2); - pTVar23 = this; - THealingImpact::THealingImpact - ((THealingImpact *)this_01,(TCreature *)this,iVar5); - break; - case IMPACT_SPEED: - this_01 = (TDamageImpact *)operator_new(0x10); - // try { // try from 080ba424 to 080ba442 has its CatchHandler @ 080bae34 - ComputeDamage((TCreature *)this,0,pTVar10->ImpactParam1, - pTVar10->ImpactParam2); - pTVar23 = this; - TSpeedImpact::TSpeedImpact((TSpeedImpact *)this_01); - break; - case IMPACT_DRUNKEN: - this_01 = (TDamageImpact *)operator_new(0x10); - // try { // try from 080ba472 to 080ba490 has its CatchHandler @ 080bae36 - iVar5 = ComputeDamage((TCreature *)this,0,pTVar10->ImpactParam1, - pTVar10->ImpactParam2); - pTVar23 = this; - TDrunkenImpact::TDrunkenImpact - ((TDrunkenImpact *)this_01,(TCreature *)this,iVar5, - pTVar10->ImpactParam3); - break; - case IMPACT_STRENGTH: - this_01 = (TDamageImpact *)operator_new(0x14); - // try { // try from 080ba4c0 to 080ba4e5 has its CatchHandler @ 080bae38 - ComputeDamage((TCreature *)this,0,pTVar10->ImpactParam2, - pTVar10->ImpactParam3); - pTVar23 = this; - TStrengthImpact::TStrengthImpact((TStrengthImpact *)this_01); - break; - case IMPACT_OUTFIT: - z_1 = pTVar10->ImpactParam1; - uVar18 = pTVar10->ImpactParam2; - if (0x7fffffff < uVar18) { - uVar18 = uVar18 + 0xff; - } - uVar4 = pTVar10->ImpactParam3; - if (0x7fffffff < uVar4) { - uVar4 = uVar4 + 0xff; - } - HelpOutfit.OutfitID = - CONCAT13((char)(uVar4 >> 8), - CONCAT12((char)pTVar10->ImpactParam3, - CONCAT11((char)(uVar18 >> 8), - (char)pTVar10->ImpactParam2))); - this_01 = (TDamageImpact *)operator_new(0x14); - _Outfit.field_1 = (anon_union_4_2_730cd3ca_for_TOutfit_2)HelpOutfit.OutfitID - ; - _Outfit.OutfitID = z_1; - pTVar23 = this; - // try { // try from 080ba554 to 080ba558 has its CatchHandler @ 080bae3a - TOutfitImpact::TOutfitImpact - ((TOutfitImpact *)this_01,(TCreature *)this,_Outfit, - pTVar10->ImpactParam4); - break; - case IMPACT_SUMMON: - if (this->Master == 0) { - this_01 = (TDamageImpact *)operator_new(0x10); - pTVar23 = this; - // try { // try from 080ba595 to 080ba599 has its CatchHandler @ 080bae1e - TSummonImpact::TSummonImpact - ((TSummonImpact *)this_01,(TCreature *)this, - pTVar10->ImpactParam1,pTVar10->ImpactParam2); - } - } - if (this_01 != (TDamageImpact *)0x0) { - iVar5 = (*(this_01->super_TImpact)._vptr_TImpact[2])(this_01,pTVar23); - if (((char)iVar5 == '\0') || - ((this->Target != 0 && (this->Target != this->Master)))) { - switch(pTVar10->Shape) { - case SHAPE_ACTOR: - ActorShapeSpell((TCreature *)this,&this_01->super_TImpact, - pTVar10->ShapeParam1); - break; - case SHAPE_VICTIM: - if (target != (TCreature *)0x0) { - TCreature::Rotate((TCreature *)this,target); - VictimShapeSpell((TCreature *)this,target,pTVar10->ShapeParam1, - pTVar10->ShapeParam2,&this_01->super_TImpact, - pTVar10->ShapeParam3); - } - break; - case SHAPE_ORIGIN: - OriginShapeSpell((TCreature *)this,pTVar10->ShapeParam1, - &this_01->super_TImpact,pTVar10->ShapeParam2); - break; - case SHAPE_DESTINATION: - if (target != (TCreature *)0x0) { - TCreature::Rotate((TCreature *)this,target); - DestinationShapeSpell - ((TCreature *)this,target,pTVar10->ShapeParam1, - pTVar10->ShapeParam2,pTVar10->ShapeParam3, - &this_01->super_TImpact,pTVar10->ShapeParam4); - } - break; - case SHAPE_ANGLE: - if (target != (TCreature *)0x0) { - TCreature::Rotate((TCreature *)this,target); - } - AngleShapeSpell((TCreature *)this,pTVar10->ShapeParam1, - pTVar10->ShapeParam2,&this_01->super_TImpact, - pTVar10->ShapeParam3); - } - } - operator_delete(this_01); - } - } -LAB_080ba242: - i = (TMonster *)((int)&(i->super_TNonplayer).super_TCreature._vptr_TCreature + 1); - iVar5 = (this->super_TNonplayer).super_TCreature.Race; - } while ((int)i <= RaceData[iVar5].Spells); - uVar12 = this->Target; - } - } - if (uVar12 == 0) { - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - iVar20 = (this->super_TNonplayer).super_TCreature.posy; - } - else { - pTVar3 = GetCreature(uVar12); - if (pTVar3 == (TCreature *)0x0) { - error("TMonster::IdleStimulus: Kreatur existiert nicht.\n"); - this->Target = 0; -LAB_080ba0e7: - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - iVar20 = (this->super_TNonplayer).super_TCreature.posy; - } - else { - uVar12 = this->Master; - if (uVar12 == 0) { - iVar5 = TSkill::Get((this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills - [2]); - if (iVar5 <= RaceData[(this->super_TNonplayer).super_TCreature.Race].FleeThreshold) - { - bVar1 = SearchFlightField(); - if (bVar1) { - TCreature::ToDoGo((TCreature *)this,z,x_1,y_1,true,0x7fffffff); - goto LAB_080b97e9; - } - goto LAB_080ba0e7; - } - uVar12 = this->Master; - } - if (this->Target != uVar12) { - // try { // try from 080b99d7 to 080ba124 has its CatchHandler @ 080bae3c - iVar5 = TSkill::Get((this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills - [0xb]); - if (iVar5 < 1) { - SVar16 = (this->super_TNonplayer).State; - } - else { - SVar16 = (this->super_TNonplayer).State; - if (SVar16 != PANIC) { - SVar16 = ATTACKING; - (this->super_TNonplayer).State = ATTACKING; - } - } - if (SVar16 - ATTACKING < 2) { - this_00 = &(this->super_TNonplayer).super_TCreature.Combat; - TCombat::SetAttackDest(this_00,this->Target,false); - TCombat::SetChaseMode(this_00,'\0'); - } - if (((RaceData[(this->super_TNonplayer).super_TCreature.Race].DistanceFighting == - false) || (this->Target == this->Master)) || - (bVar1 = ThrowPossible((this->super_TNonplayer).super_TCreature.posx, - (this->super_TNonplayer).super_TCreature.posy, - (this->super_TNonplayer).super_TCreature.posz,pTVar3->posx - ,pTVar3->posy,pTVar3->posz,0), !bVar1)) { - TCombat::SetChaseMode(&(this->super_TNonplayer).super_TCreature.Combat,'\x01'); - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - if (((iVar5 - pTVar3->posx) + 1U < 3) && - (iVar20 = (this->super_TNonplayer).super_TCreature.posy, - (iVar20 - pTVar3->posy) + 1U < 3)) { - iVar21 = rand(); - iVar21 = iVar21 % 5; - if (iVar21 == 1) { - iVar5 = iVar5 + 1; - } - else if (iVar21 < 2) { - if (iVar21 == 0) { - iVar5 = iVar5 + -1; - } - } - else if (iVar21 == 2) { - iVar20 = iVar20 + -1; - } - else if (iVar21 == 3) { - iVar20 = iVar20 + 1; - } - if (((iVar21 < 4) && - (iVar21 = (*(this->super_TNonplayer).super_TCreature._vptr_TCreature[3]) - (this,iVar5,iVar20, - (this->super_TNonplayer).super_TCreature.posz,1,0), - (char)iVar21 != '\0')) && - (((iVar5 - pTVar3->posx) + 1U < 3 && ((iVar20 - pTVar3->posy) + 1U < 3))) - ) { - TCreature::ToDoGo((TCreature *)this,iVar5,iVar20, - (this->super_TNonplayer).super_TCreature.posz,true, - 0x7fffffff); - } - SVar16 = (this->super_TNonplayer).State; - if (SVar16 == PANIC) { - SVar16 = ATTACKING; - (this->super_TNonplayer).State = ATTACKING; - } - } - else { - SVar16 = (this->super_TNonplayer).State; - if (1 < SVar16 - ATTACKING) { - iVar5 = 3; - goto LAB_080b9ba0; - } - } - } - else { - HelpOutfit.field_1 = - (anon_union_4_2_730cd3ca_for_TOutfit_2)(pTVar3->CrObject).ObjectID; - Search.endy = (this->super_TNonplayer).super_TCreature.CrObject.ObjectID; - iVar5 = ObjectDistance((Object *)&Search.endy,(Object *)&HelpOutfit.field_1); - if (iVar5 < 4) { - bVar1 = SearchFlightField(); - if (!bVar1) { - TCreature::ToDoWait((TCreature *)this,1000); - SVar16 = (this->super_TNonplayer).State; - goto LAB_080b9afc; - } - TCreature::ToDoGo((TCreature *)this,local_6c,x,y,true,0x7fffffff); - } - else if (iVar5 == 4) { - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - iVar20 = (this->super_TNonplayer).super_TCreature.posy; - iVar21 = rand(); - iVar21 = iVar21 % 5; - if (iVar21 == 1) { - iVar5 = iVar5 + 1; - } - else if (iVar21 < 2) { - if (iVar21 == 0) { - iVar5 = iVar5 + -1; - } - } - else if (iVar21 == 2) { - iVar20 = iVar20 + -1; - } - else if (iVar21 == 3) { - iVar20 = iVar20 + 1; - } - if ((iVar21 < 4) && - (iVar21 = (*(this->super_TNonplayer).super_TCreature._vptr_TCreature[3]) - (this,iVar5,iVar20, - (this->super_TNonplayer).super_TCreature.posz,1,0), - (char)iVar21 != '\0')) { - iVar21 = iVar5 - pTVar3->posx; - local_70 = -iVar21; - if (-1 < iVar21) { - local_70 = iVar21; - } - iVar21 = iVar20 - pTVar3->posy; - iStack_74 = -iVar21; - if (-1 < iVar21) { - iStack_74 = iVar21; - } - piVar17 = &stack0xffffff8c; - if (iStack_74 <= local_70) { - piVar17 = &local_70; - } - if (*piVar17 == 4) { - TCreature::ToDoGo((TCreature *)this,iVar5,iVar20, - (this->super_TNonplayer).super_TCreature.posz,true - ,0x7fffffff); - } - } - TCreature::ToDoWait((TCreature *)this,1000); - } - else { - iVar5 = iVar5 + -4; -LAB_080b9ba0: - TCreature::ToDoGo((TCreature *)this,pTVar3->posx,pTVar3->posy,pTVar3->posz, - false,iVar5); - } - SVar16 = (this->super_TNonplayer).State; - } -LAB_080b9afc: - if (SVar16 - ATTACKING < 2) { - pTVar3 = GetCreature(this->Target); - TCreature::Rotate((TCreature *)this,pTVar3); - if ((this->Master == 0) || - (uVar12 = (this->super_TNonplayer).super_TCreature.Combat.AttackDest, - uVar12 != this->Master)) { - TCreature::ToDoAttack((TCreature *)this); - } - else { - error("TMonster::IdleStimulus: %s greift Master %lu an (St=%d, T: %lu).\n", - (this->super_TNonplayer).super_TCreature.Name,uVar12, - (this->super_TNonplayer).State,this->Target); - } - } - else { - TCreature::ToDoWait((TCreature *)this,1000); - } - goto LAB_080b97e9; - } - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - iVar20 = (this->super_TNonplayer).super_TCreature.posy; - uVar22 = pTVar3->posx - iVar5; - uVar4 = pTVar3->posy - iVar20; - uVar7 = -uVar22; - uVar18 = uVar7; - if (-1 < (int)uVar22) { - uVar18 = uVar22; - } - uVar8 = -uVar4; - uVar9 = uVar8; - if (-1 < (int)uVar4) { - uVar9 = uVar4; - } - if (1 < (int)(uVar18 + uVar9)) { - uVar18 = uVar7; - if (-1 < (int)uVar22) { - uVar18 = uVar22; - } - uVar9 = uVar8; - if (-1 < (int)uVar4) { - uVar9 = uVar4; - } - if (uVar18 + uVar9 == 2) { - TCreature::ToDoWait((TCreature *)this,1000); - } - else { - if (0x7fffffff < uVar22) { - uVar22 = uVar7; - } - if (0x7fffffff < uVar4) { - uVar4 = uVar8; - } - if (uVar4 + uVar22 == 3) { - TCreature::ToDoWait((TCreature *)this,1000); - } - TCreature::ToDoGo((TCreature *)this,pTVar3->posx,pTVar3->posy,pTVar3->posz,false - ,3); - } - TCreature::ToDoStart((TCreature *)this); - return; - } - } - } - iVar21 = 0; - while (iVar21 = iVar21 + 1, iVar21 < 0xb) { - iVar6 = rand(); - iVar6 = iVar6 % 4; - if (iVar6 == 1) { - iVar5 = iVar5 + 1; - } - else if (iVar6 < 2) { - if (iVar6 == 0) { - iVar5 = iVar5 + -1; - } - } - else if (iVar6 == 2) { - iVar20 = iVar20 + -1; - } - else if (iVar6 == 3) { - iVar20 = iVar20 + 1; - } - // try { // try from 080b98ef to 080b98f1 has its CatchHandler @ 080baea0 - iVar6 = (*(this->super_TNonplayer).super_TCreature._vptr_TCreature[3]) - (this,iVar5,iVar20,(this->super_TNonplayer).super_TCreature.posz,1,0); - if ((char)iVar6 != '\0') { - // try { // try from 080b9932 to 080b9951 has its CatchHandler @ 080baeca - TCreature::ToDoGo((TCreature *)this,iVar5,iVar20, - (this->super_TNonplayer).super_TCreature.posz,true,0x7fffffff); - TCreature::ToDoWait((TCreature *)this,1000); - TCreature::ToDoStart((TCreature *)this); - return; - } - iVar5 = (this->super_TNonplayer).super_TCreature.posx; - iVar20 = (this->super_TNonplayer).super_TCreature.posy; - } - TCreature::ToDoWait((TCreature *)this,1000); -LAB_080b97e9: - TCreature::ToDoStart((TCreature *)this); - return; -} - - - -// DWARF original prototype: void CreatureMoveStimulus(TMonster * this, ulong CreatureID, int Type) - -void __thiscall TMonster::CreatureMoveStimulus(TMonster *this,ulong CreatureID,int Type) - -{ - TSkill *id; - TCreature *cr; - TCreature *pTVar1; - - if (CreatureID == (this->super_TNonplayer).super_TCreature.ID) { - if (((this->super_TNonplayer).State == SLEEPING) && (Type != 0)) { - (this->super_TNonplayer).State = IDLE; - TCreature::ToDoYield((TCreature *)this); - return; - } - return; - } - if (((this->super_TNonplayer).State == SLEEPING) && (Type != 0)) { - pTVar1 = GetCreature(CreatureID); - if (pTVar1 == (TCreature *)0x0) { - error("TMonster::CreatureMoveStimulus: Kreatur %d existiert nicht.\n"); - return; - } - if (pTVar1->Type == NPC) { - return; - } - if (pTVar1->Type == MONSTER) { - id = pTVar1[1].super_TSkillBase.Skills[1]; - if (id == (TSkill *)0x0) { - return; - } - pTVar1 = GetCreature((ulong)id); - if (pTVar1 == (TCreature *)0x0) { - return; - } - if (pTVar1->Type != PLAYER) { - return; - } - } - (this->super_TNonplayer).State = IDLE; - TCreature::ToDoYield((TCreature *)this); - } - TCreature::CreatureMoveStimulus((TCreature *)this,CreatureID,Type); - return; -} - - - -// DWARF original prototype: void DamageStimulus(TMonster * this, ulong Attacker, int Damage, int -// param_3) - -void __thiscall TMonster::DamageStimulus(TMonster *this,ulong Attacker,int Damage,int param_3) - -{ - STATE SVar1; - - if ((Attacker != 0) && (Damage != 0)) { - SVar1 = (this->super_TNonplayer).State; - if (SVar1 == SLEEPING) { - (this->super_TNonplayer).State = (uint)(this->Target == 0) * 4 + UNDERATTACK; - TCreature::ToDoYield((TCreature *)this); - return; - } - if (SVar1 == IDLE) { - (this->super_TNonplayer).State = UNDERATTACK; - } - if (this->Target == 0) { - (this->super_TNonplayer).State = PANIC; - } - } - return; -} - - - -// WARNING: Type propagation algorithm not settling -// DWARF original prototype: bool MovePossible(TMonster * this, int x, int y, int z, bool Execute, -// bool param_5) - -bool __thiscall TMonster::MovePossible(TMonster *this,int x,int y,int z,bool Execute,bool param_5) - -{ - bool bVar1; - bool bVar2; - TCreature *master_1; - int iVar3; - TCreature *pTVar4; - undefined4 *puVar5; - ulong uVar6; - TMonster *pTVar7; - uint uVar8; - TCreature *master; - int *piVar9; - STATE SVar10; - uint uVar11; - TCreature *Obstacle; - ulong *puVar12; - Object *local_64; - int local_60; - int Try; - int iStack_54; - bool Execute_local; - int local_50; - Object local_4c [4]; - Object local_3c [4]; - ulong local_2c; - Object Obj; - - iVar3 = (this->super_TNonplayer).super_TCreature.posz; - if (iVar3 == z) { - SVar10 = (this->super_TNonplayer).State; - if (1 < SVar10 - ATTACKING) { - bVar1 = MonsterhomeInRange(); - if (!bVar1) { - return false; - } - SVar10 = (this->super_TNonplayer).State; - } - if (1 < SVar10 - ATTACKING) { - iVar3 = x - (this->super_TNonplayer).super_TCreature.startx; - local_50 = -iVar3; - if (-1 < iVar3) { - local_50 = iVar3; - } - iVar3 = y - (this->super_TNonplayer).super_TCreature.starty; - iStack_54 = -iVar3; - if (-1 < iVar3) { - iStack_54 = iVar3; - } - piVar9 = &stack0xffffffac; - if (iStack_54 <= local_50) { - piVar9 = &local_50; - } - if ((this->super_TNonplayer).super_TCreature.Radius < *piVar9) { - return false; - } - } - if (-1 < (this->super_TNonplayer).super_TCreature.super_TSkillBase.Skills[4]->Act) { - bVar1 = IsProtectionZone(); - if (bVar1) { - return false; - } - bVar1 = IsHouse(x,y,z); - if (bVar1) { - return false; - } - if (((((Execute) && (this->Master != 0)) && - (SVar10 = (this->super_TNonplayer).State, SVar10 != ATTACKING)) && - ((SVar10 != PANIC && - (pTVar4 = GetCreature(this->Master), pTVar4 != (TCreature *)0x0)))) && - (pTVar4->posz == (this->super_TNonplayer).super_TCreature.posz)) { - uVar11 = pTVar4->posx - (this->super_TNonplayer).super_TCreature.posx; - if (0x7fffffff < uVar11) { - uVar11 = -uVar11; - } - uVar8 = pTVar4->posy - (this->super_TNonplayer).super_TCreature.posy; - if (0x7fffffff < uVar8) { - uVar8 = -uVar8; - } - if (1 < (int)(uVar11 + uVar8)) { - uVar11 = pTVar4->posx - x; - if (0x7fffffff < uVar11) { - uVar11 = -uVar11; - } - uVar8 = pTVar4->posy - y; - if (0x7fffffff < uVar8) { - uVar8 = -uVar8; - } - if ((int)(uVar11 + uVar8) < 2) { - return false; - } - } - } - local_60 = 0; -LAB_080bb1d4: - local_60 = local_60 + 1; - if (local_60 < 0x65) { - GetFirstObject(); - bVar1 = false; - local_3c[0].ObjectID = ::NONE.ObjectID; - if (local_2c != ::NONE.ObjectID) { - Object::getObjectType(local_4c); - bVar2 = ObjectType::getFlag((ObjectType *)local_4c,BANK); - if (bVar2) goto LAB_080bb216; - } - bVar1 = true; -LAB_080bb216: - if (bVar1) { - return false; - } - if (local_2c == ::NONE.ObjectID) { - return true; - } - do { - local_64 = local_4c; - puVar12 = &local_2c; - local_4c[0].ObjectID = ::NONE.ObjectID; - local_3c[0].ObjectID = ::NONE.ObjectID; - Object::getObjectType(local_64); - if (local_4c[0].ObjectID == 99) { - if (1 < (this->super_TNonplayer).State - ATTACKING) { - return false; - } - if (this->Target == 0) { - return false; - } - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].KickCreatures == - false) { - return false; - } - local_4c[0].ObjectID = local_2c; - pTVar4 = GetCreature(local_64); - if (pTVar4 == (TCreature *)0x0) { - error("TMonster::MovePossible: Kann Hindernis-Kreatur nicht identifizieren.\n" - ,puVar12); - return false; - } - if (RaceData[pTVar4->Race].Unpushable != false) { - return false; - } - if (pTVar4->Type == NPC) { - return false; - } - if (pTVar4->Type == PLAYER) { - if (this->Master != 0) { - return false; - } - bVar1 = CheckRight(pTVar4->ID,IGNORED_BY_MONSTERS); - if (bVar1) { - return false; - } - } - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].SeeInvisible == - false) { - bVar1 = false; - if (((pTVar4->Outfit).OutfitID == 0) && - ((pTVar4->Outfit).field_1.ObjectType == 0)) { - bVar1 = true; - } - if (bVar1) { - return false; - } - } - if (pTVar4->ID == this->Master) { - return false; - } - if (pTVar4->ID == this->Target) { - return false; - } - if (Execute) goto LAB_080bb399; - } - else { - Object::getObjectType(local_64); - bVar1 = ObjectType::getFlag((ObjectType *)local_64,UNPASS); - if (bVar1) { - Object::getObjectType(local_64); - bVar1 = ObjectType::getFlag((ObjectType *)local_64,UNMOVE); - if (bVar1) { - return false; - } - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].KickBoxes == - false) { - bVar1 = false; - if (this->Master != 0) { - pTVar7 = (TMonster *)GetCreature(this->Master); - if ((pTVar7 == (TMonster *)0x0) || - ((pTVar7->super_TNonplayer).super_TCreature.Type != MONSTER)) - { - bVar1 = false; - } - else { - bVar1 = CanKickBoxes(pTVar7); - } - } - } - else { - bVar1 = true; - } - if (bVar1 == false) { - return false; - } - if (!Execute) goto LAB_080bb4af; -LAB_080bb5cf: - local_4c[0].ObjectID = local_2c; - KickBoxes(this,local_4c); - goto LAB_080bb1d4; - } -LAB_080bb4af: - Object::getObjectType(local_4c); - bVar1 = ObjectType::getFlag((ObjectType *)local_4c,AVOID); - if (bVar1) { - if ((this->super_TNonplayer).State == PANIC) { - Object::getObjectType(local_4c); - uVar6 = ObjectType::getAttribute - ((ObjectType *)local_4c,AVOIDDAMAGETYPES); - if (uVar6 != 0) goto LAB_080bb357; - } - iVar3 = (this->super_TNonplayer).super_TCreature.Race; - if (RaceData[iVar3].NoBurning != false) { - Object::getObjectType(local_4c); - uVar6 = ObjectType::getAttribute - ((ObjectType *)local_4c,AVOIDDAMAGETYPES); - if (uVar6 == 4) goto LAB_080bb357; - iVar3 = (this->super_TNonplayer).super_TCreature.Race; - } - if (RaceData[iVar3].NoPoison != false) { - Object::getObjectType(local_4c); - uVar6 = ObjectType::getAttribute - ((ObjectType *)local_4c,AVOIDDAMAGETYPES); - if (uVar6 == 2) goto LAB_080bb357; - iVar3 = (this->super_TNonplayer).super_TCreature.Race; - } - if (RaceData[iVar3].NoEnergy != false) { - Object::getObjectType(local_4c); - uVar6 = ObjectType::getAttribute - ((ObjectType *)local_4c,AVOIDDAMAGETYPES); - if (uVar6 == 8) goto LAB_080bb357; - } - Object::getObjectType(local_4c); - bVar1 = ObjectType::getFlag((ObjectType *)local_4c,UNMOVE); - if (bVar1) { - return false; - } - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].KickBoxes == - false) { - bVar1 = false; - if (this->Master != 0) { - pTVar7 = (TMonster *)GetCreature(this->Master); - if ((pTVar7 == (TMonster *)0x0) || - ((pTVar7->super_TNonplayer).super_TCreature.Type != MONSTER)) - { - bVar1 = false; - } - else { - bVar1 = CanKickBoxes(pTVar7); - } - } - } - else { - bVar1 = true; - } - if (bVar1 == false) { - return false; - } - if (Execute) goto LAB_080bb5cf; - } - } -LAB_080bb357: - Object::getNextObject(local_3c); - local_2c = local_3c[0].ObjectID; - if (local_3c[0].ObjectID == ::NONE.ObjectID) { - return true; - } - } while( true ); - } - error(&DAT_08106140,(this->super_TNonplayer).super_TCreature.Name,x,y,z); - goto LAB_080bb3b7; - } - error("TMonster::MovePossible: Monster %s an [%d,%d,%d] darf sich nicht bewegen.\n", - (this->super_TNonplayer).super_TCreature.Name, - (this->super_TNonplayer).super_TCreature.posx, - (this->super_TNonplayer).super_TCreature.posy, - (this->super_TNonplayer).super_TCreature.posz); - } - else { - // try { // try from 080bb0ca to 080bb83f has its CatchHandler @ 080bb855 - error(&DAT_08106080,(this->super_TNonplayer).super_TCreature.posx, - (this->super_TNonplayer).super_TCreature.posy,iVar3,x,y,z); - } - return false; -LAB_080bb399: - if (pTVar4->Type == PLAYER) { - this->Target = 0; - goto LAB_080bb3b7; - } - bVar1 = KickCreature(this,pTVar4); - if (!bVar1) { -LAB_080bb3b7: - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x31; - // WARNING: Subroutine does not return - __cxa_throw(puVar5,&RESULT::typeinfo,0); - } - goto LAB_080bb1d4; -} - - - -// DWARF original prototype: bool IsPeaceful(TMonster * this) - -bool __thiscall TMonster::IsPeaceful(TMonster *this) - -{ - return this->Master != 0 && this->Master < 0x40000000; -} - - - -// DWARF original prototype: bool CanKickBoxes(TMonster * this) - -bool __thiscall TMonster::CanKickBoxes(TMonster *this) - -{ - bool bVar1; - TCreature *master; - TMonster *this_00; - - if (RaceData[(this->super_TNonplayer).super_TCreature.Race].KickBoxes == false) { - bVar1 = false; - if (this->Master != 0) { - this_00 = (TMonster *)GetCreature(this->Master); - if ((this_00 == (TMonster *)0x0) || - ((this_00->super_TNonplayer).super_TCreature.Type != MONSTER)) { - bVar1 = false; - } - else { - bVar1 = CanKickBoxes(this_00); - } - } - } - else { - bVar1 = true; - } - return bVar1; -} - - - -// DWARF original prototype: void KickBoxes(TMonster * this, Object * Obj) - -void __thiscall TMonster::KickBoxes(TMonster *this,Object *Obj) - -{ - bool bVar1; - bool bVar2; - int iVar3; - ulong CreatureID; - RESULT r; - Object *Obj_00; - Object *Ignore; - int local_58; - int x; - int y; - int z; - Object local_3c [4]; - Object local_2c [7]; - - bVar1 = Object::exists(Obj); - if (!bVar1) { - error(&DAT_08106200); - return; - } - local_2c[0] = (Object)Obj->ObjectID; - GetObjectCoordinates(local_2c,&local_58,&x,&y); - iVar3 = (this->super_TNonplayer).super_TCreature.posy; - if (x + -1 != iVar3) { - bVar1 = false; - // try { // try from 080bb98b to 080bbc66 has its CatchHandler @ 080bbc80 - bVar2 = CoordinateFlag(); - if (bVar2) { - bVar1 = CoordinateFlag(); - bVar1 = !bVar1; - } - if (bVar1) { - local_2c[0].ObjectID = ::NONE.ObjectID; - GetMapContainer((int)local_3c,local_58,x + -1); - Ignore = local_2c; - Obj_00 = (Object *)&z; - z = Obj->ObjectID; - CreatureID = (this->super_TNonplayer).super_TCreature.ID; - goto LAB_080bbb13; - } - iVar3 = (this->super_TNonplayer).super_TCreature.posy; - } - if (x + 1 == iVar3) { -LAB_080bb9ec: - iVar3 = (this->super_TNonplayer).super_TCreature.posx; - if (local_58 + -1 != iVar3) { - bVar1 = false; - bVar2 = CoordinateFlag(); - if (bVar2) { - bVar1 = CoordinateFlag(); - bVar1 = !bVar1; - } - if (bVar1) { - z = ::NONE.ObjectID; - local_58 = local_58 + -1; - goto LAB_080bbad4; - } - iVar3 = (this->super_TNonplayer).super_TCreature.posx; - } - if (local_58 + 1 == iVar3) { -LAB_080bba7c: - z = Obj->ObjectID; - GraphicalEffect((Object *)&z,4); - z = Obj->ObjectID; - Delete((Object *)&z,-1); - return; - } - bVar1 = false; - bVar2 = CoordinateFlag(); - if (bVar2) { - bVar1 = CoordinateFlag(); - bVar1 = !bVar1; - } - if (!bVar1) goto LAB_080bba7c; - z = ::NONE.ObjectID; - local_58 = local_58 + 1; - } - else { - bVar1 = false; - bVar2 = CoordinateFlag(); - if (bVar2) { - bVar1 = CoordinateFlag(); - bVar1 = !bVar1; - } - if (!bVar1) goto LAB_080bb9ec; - z = ::NONE.ObjectID; - x = x + 1; - } -LAB_080bbad4: - Ignore = (Object *)&z; - ::NONE.ObjectID = z; - GetMapContainer((int)local_3c,local_58,x); - local_2c[0] = (Object)Obj->ObjectID; - Obj_00 = local_2c; - CreatureID = (this->super_TNonplayer).super_TCreature.ID; -LAB_080bbb13: - Move(CreatureID,Obj_00,local_3c,-1,false,Ignore); - return; -} - - - -// DWARF original prototype: bool KickCreature(TMonster * this, TCreature * Obstacle) - -bool __thiscall TMonster::KickCreature(TMonster *this,TCreature *Obstacle) - -{ - int iVar1; - TSkill *pTVar2; - bool bVar3; - RESULT r; - int iVar4; - int iVar5; - int iVar6; - Object *Obj_00; - int iVar7; - int x; - char *Text; - Object *Ignore; - int z; - int y; - ushort HelpY; - ushort HelpX; - Object local_4c; - Object Obj; - Object local_3c [4]; - Object local_2c [7]; - - if (Obstacle == (TCreature *)0x0) { - Text = &DAT_08106360; -LAB_080bbd4d: - error(Text); - return false; - } - if (Obstacle->Type != MONSTER) { - Text = "TMonster::KickCreature: Zu verschiebende Kreatur ist kein Monster.\n"; - goto LAB_080bbd4d; - } - print(); - iVar6 = Obstacle->posy; - iVar5 = Obstacle->posx; - iVar1 = Obstacle->posz; - iVar7 = iVar6 + -1; - iVar4 = (this->super_TNonplayer).super_TCreature.posy; - if (iVar7 != iVar4) { - // try { // try from 080bbde8 to 080bc0cf has its CatchHandler @ 080bc0f3 - iVar4 = (*Obstacle->_vptr_TCreature[3])(Obstacle,iVar5,iVar7,iVar1,1,0); - if (((char)iVar4 != '\0') && (bVar3 = CoordinateFlag(), !bVar3)) { - local_2c[0].ObjectID = ::NONE.ObjectID; - GetMapContainer((int)local_3c,iVar5,iVar7); - local_4c.ObjectID = (Obstacle->CrObject).ObjectID; - Obj_00 = &local_4c; - Ignore = local_2c; - goto LAB_080bbfb2; - } - iVar4 = (this->super_TNonplayer).super_TCreature.posy; - } - iVar7 = iVar6 + 1; - if (((iVar7 != iVar4) && - (iVar4 = (*Obstacle->_vptr_TCreature[3])(Obstacle,iVar5,iVar7,iVar1,1,0), - (char)iVar4 != '\0')) && (bVar3 = CoordinateFlag(), !bVar3)) { - local_4c.ObjectID = ::NONE.ObjectID; - GetMapContainer((int)local_3c,iVar5,iVar7); - local_2c[0].ObjectID = (Obstacle->CrObject).ObjectID; - Obj_00 = local_2c; - Ignore = &local_4c; - goto LAB_080bbfb2; - } - iVar4 = iVar5 + -1; - iVar7 = (this->super_TNonplayer).super_TCreature.posx; - if (iVar4 == iVar7) { -LAB_080bbe7e: - iVar4 = iVar5 + 1; - if (((iVar4 == iVar7) || - (iVar5 = (*Obstacle->_vptr_TCreature[3])(Obstacle,iVar4,iVar6,iVar1,1,0), - (char)iVar5 == '\0')) || (bVar3 = CoordinateFlag(), bVar3)) { - print(); - local_4c.ObjectID = (Obstacle->CrObject).ObjectID; - GraphicalEffect(&local_4c,4); - iVar6 = TSkill::Get((Obstacle->super_TSkillBase).Skills[2]); - TCombat::AddDamageToCombatList - (&Obstacle->Combat,(this->super_TNonplayer).super_TCreature.ID,(ushort)iVar6); - pTVar2 = (Obstacle->super_TSkillBase).Skills[2]; - (*pTVar2->_vptr_TSkill[2])(pTVar2,0); - (*Obstacle->_vptr_TCreature[2])(Obstacle); - return false; - } - } - else { - iVar7 = (*Obstacle->_vptr_TCreature[3])(Obstacle,iVar4,iVar6,iVar1,1,0); - if (((char)iVar7 == '\0') || (bVar3 = CoordinateFlag(), bVar3)) { - iVar7 = (this->super_TNonplayer).super_TCreature.posx; - goto LAB_080bbe7e; - } - } - local_4c.ObjectID = ::NONE.ObjectID; - GetMapContainer((int)local_3c,iVar4,iVar6); - local_2c[0].ObjectID = (Obstacle->CrObject).ObjectID; - Ignore = &local_4c; - Obj_00 = local_2c; -LAB_080bbfb2: - Move((this->super_TNonplayer).super_TCreature.ID,Obj_00,local_3c,-1,false,Ignore); - return true; -} - - - -// DWARF original prototype: void Convince(TMonster * this, TCreature * newMaster) - -void __thiscall TMonster::Convince(TMonster *this,TCreature *newMaster) - -{ - TCreature *oldMaster; - TCreature *pTVar1; - - if (this->Home != 0) { - NotifyMonsterhomeOfDeath(this->Home); - } - this->Home = 0; - if (this->Master != 0) { - pTVar1 = GetCreature(this->Master); - if (pTVar1 != (TCreature *)0x0) { - pTVar1->SummonedCreatures = pTVar1->SummonedCreatures + -1; - } - } - this->Master = newMaster->ID; - newMaster->SummonedCreatures = newMaster->SummonedCreatures + 1; - TCreature::ToDoClear((TCreature *)this); - TCreature::ToDoWait((TCreature *)this,100); - TCreature::ToDoStart((TCreature *)this); - return; -} - - - -// DWARF original prototype: void SetTarget(TMonster * this, TCreature * NewTarget) - -void __thiscall TMonster::SetTarget(TMonster *this,TCreature *NewTarget) - -{ - if (NewTarget == (TCreature *)0x0) { - error("TMonster::SetTarget: NewTarget ist NULL.\n"); - return; - } - if (this->Master != 0) { - return; - } - TCreature::Rotate((TCreature *)this,NewTarget); - this->Target = NewTarget->ID; - TCreature::ToDoYield((TCreature *)this); - return; -} - - - -// WARNING: Variable defined which should be unmapped: ShowEffect_local - -TCreature * CreateMonster(int Race,int x,int y,int z,int Home,ulong Father,bool ShowEffect) - -{ - bool bVar1; - TMonster *this; - char *Text; - bool ShowEffect_local; - - bVar1 = ShowEffect; - if (Race - 1U < 0x1ff) { - if (RaceData[Race].Name[0] != '\0') { - SearchFreeField(&x,&y,&z,2,0,false); - this = (TMonster *)operator_new(0x32c); - // try { // try from 080bc3ec to 080bc3f0 has its CatchHandler @ 080bc431 - TMonster::TMonster(this); - if (!bVar1) { - return (TCreature *)this; - } - GraphicalEffect(); - return (TCreature *)this; - } - Text = &DAT_08104f00; - } - else { - Text = &DAT_08104f40; - } - error(Text,Race); - return (TCreature *)0x0; -} - - - -void ConvinceMonster(TCreature *Master,TCreature *Slave) - -{ - if (Master == (TCreature *)0x0) { - Master = (TCreature *)s_ConvinceMonster__Master_existier_08106520; - } - else if (Slave == (TCreature *)0x0) { - Master = (TCreature *)s_ConvinceMonster__Slave_existiert_081064e0; - } - else { - if (Slave->Type == MONSTER) { - TMonster::Convince((TMonster *)Slave,Master); - return; - } - Master = (TCreature *)s_ConvinceMonster__Slave_ist_kein_M_081064a0; - } - error((char *)Master); - return; -} - - - -void ChallengeMonster(TCreature *Challenger,TCreature *Monster) - -{ - if (Challenger == (TCreature *)0x0) { - Challenger = (TCreature *)s_ChallengeMonster__Herausforderer_081065e0; - } - else if (Monster == (TCreature *)0x0) { - Challenger = (TCreature *)s_ChallengeMonster__Monster_existi_081065a0; - } - else { - if (Monster->Type == MONSTER) { - if (Monster[1].super_TSkillBase.Skills[1] != (TSkill *)0x0) { - return; - } - TCreature::Rotate(Monster,Challenger); - Monster[1].super_TSkillBase.Skills[2] = (TSkill *)Challenger->ID; - TCreature::ToDoYield(Monster); - return; - } - Challenger = (TCreature *)s_ChallengeMonster__Monster_ist_ke_08106560; - } - error((char *)Challenger); - return; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void InitNPCs(void) - -{ - char *__s; - dirent *DirEntry; - DIR *__dirp; - dirent *pdVar1; - size_t sVar2; - TNPC *this; - undefined4 *puVar3; - int iVar4; - char *s; - char *pcVar5; - char *pcVar6; - bool bVar7; - DIR *WorkDir; - char FileName [4096]; - - __dirp = opendir(NPCPATH); - if (__dirp == (DIR *)0x0) { - // try { // try from 080bc5fd to 080bc62b has its CatchHandler @ 080bc649 - error("InitNPCs: Unterverzeichnis %s nicht gefunden\n",NPCPATH); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = "Cannot init NPCs"; - // WARNING: Subroutine does not return - __cxa_throw(puVar3,char_const*::typeinfo,0); - } - do { - do { - pdVar1 = readdir(__dirp); - if (pdVar1 == (dirent *)0x0) { - closedir(__dirp); - return; - } - __s = pdVar1->d_name; - sVar2 = strlen(__s); - bVar7 = sVar2 == 4; - } while (sVar2 < 5); - iVar4 = 4; - pcVar5 = __s + (sVar2 - 4); - pcVar6 = ".npc"; - do { - if (iVar4 == 0) break; - iVar4 = iVar4 + -1; - bVar7 = *pcVar5 == *pcVar6; - pcVar5 = pcVar5 + 1; - pcVar6 = pcVar6 + 1; - } while (bVar7); - if (bVar7) { - sprintf((char *)&WorkDir,"%s/%s",NPCPATH,__s); - // try { // try from 080bc5c0 to 080bc5c4 has its CatchHandler @ 080bc649 - this = (TNPC *)operator_new(900); - // try { // try from 080bc5ce to 080bc5d2 has its CatchHandler @ 080bc62c - TNPC::TNPC(this,(char *)&WorkDir); - } - } while( true ); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void InitNonplayer(void) - -{ - store *psVar1; - list<> *plVar2; - - // try { // try from 080bc67e to 080bc69e has its CatchHandler @ 080bc710 - print(); - FirstFreeNonplayer = 0; - CurrentNonplayer = 0; - psVar1 = (store *)operator_new(8); - // try { // try from 080bc6a8 to 080bc6ac has its CatchHandler @ 080bc6e0 - plVar2 = (list<> *)operator_new(8); - plVar2->firstNode = (listnode<> *)0x0; - plVar2->lastNode = (listnode<> *)0x0; - psVar1->Units = plVar2; - psVar1->firstFreeItem = (storeitem *)0x0; - NodeTable = psVar1; - // try { // try from 080bc6c9 to 080bc6d2 has its CatchHandler @ 080bc710 - InitNPCs(); - LoadMonsterhomes(); - return; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void ExitNonplayer(void) - -{ - TNonplayer *pTVar1; - list<> *this; - listnode<> *__filename; - store *psVar2; - TNonplayer **ppTVar3; - - if (0 < FirstFreeNonplayer) { - do { - ppTVar3 = vector<>::operator()(&NonplayerList,0); - pTVar1 = *ppTVar3; - if (pTVar1 != (TNonplayer *)0x0) { - (*(pTVar1->super_TCreature)._vptr_TCreature[1])(pTVar1); - } - } while (0 < FirstFreeNonplayer); - } - psVar2 = NodeTable; - if (NodeTable != (store *)0x0) { - this = NodeTable->Units; - if (this != (list<> *)0x0) { - __filename = this->firstNode; - while (__filename != (listnode<> *)0x0) { - list<>::remove(this,(char *)__filename); - __filename = this->firstNode; - } - operator_delete(this); - } - operator_delete(psVar2); - } - return; -} - - - void __tcf_0(void *param_1) { @@ -20285,16 +18570,6 @@ void _GLOBAL__I_NonplayerList(void) return; } - - -// DWARF original prototype: ulong GetMaster(TMonster * this) - -ulong __thiscall TMonster::GetMaster(TMonster *this) - -{ - return this->Master; -} - void __static_initialization_and_destruction_0(int __initialize_p,int __priority) { diff --git a/src/connections.cc b/src/connections.cc index fe26fea..7cf70c7 100644 --- a/src/connections.cc +++ b/src/connections.cc @@ -307,8 +307,7 @@ bool TConnection::IsVisible(int x, int y, int z){ int PlayerX, PlayerY, PlayerZ; this->GetPosition(&PlayerX, &PlayerY, &PlayerZ); - // NOTE(fusion): The player can see only floors above ground when above - // ground, or two floors up and down when underground. + // TODO(fusion): Have a standalone version of `TCreature::CanSeeFloor`? if(PlayerZ <= 7){ if(z > 7){ return false; diff --git a/src/cr.hh b/src/cr.hh index de40fcc..e095903 100644 --- a/src/cr.hh +++ b/src/cr.hh @@ -26,6 +26,10 @@ struct TOutfit{ return this->OutfitID == Other.OutfitID && this->PackedData == Other.PackedData; } + + static constexpr TOutfit Invisible(void){ + return TOutfit{}; + } }; struct TSkillData { @@ -555,6 +559,23 @@ struct TCreature: TSkillBase { void NotifyDelete(void); void NotifyChangeInventory(void); + void Kill(void){ + this->Skills[SKILL_HITPOINTS]->Set(0); + this->Death(); + } + + bool IsInvisible(void){ + return this->Outfit == TOutfit::Invisible(); + } + + bool CanSeeFloor(int FloorZ){ + if(this->posz <= 7){ + return FloorZ <= 7; + }else{ + return std::abs(this->posz - FloorZ) <= 2; + } + } + // VIRTUAL FUNCTIONS // ================= virtual ~TCreature(void); // VTABLE[ 0] @@ -756,10 +777,23 @@ struct TMonsterhome { struct TMonster: TNonplayer { TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID); + bool CanKickBoxes(void); + void KickBoxes(Object Obj); + bool KickCreature(TCreature *Creature); + void Convince(TCreature *NewMaster); + void SetTarget(TCreature *NewTarget); + bool IsPlayerControlled(void); + bool IsFleeing(void); // VIRTUAL FUNCTIONS // ================= ~TMonster(void) override; + bool MovePossible(int x, int y, int z, bool Execute, bool Jump) override; + bool IsPeaceful(void) override; + uint32 GetMaster(void) override; + void DamageStimulus(uint32 AttackerID, int Damage, int DamageType) override; + void IdleStimulus(void) override; + void CreatureMoveStimulus(uint32 CreatureID, int Type) override; // DATA //TNonplayer super_TNonplayer; // IMPLICIT @@ -945,9 +979,17 @@ void LoadMonsterhomes(void); void ProcessMonsterhomes(void); void NotifyMonsterhomeOfDeath(int Nr); bool MonsterhomeInRange(int Nr, int x, int y, int z); + void ChangeNPCState(TCreature *Npc, int NewState, bool Stimulus); -void InitNonplayer(); -void ExitNonplayer(); + +TCreature *CreateMonster(int Race, int x, int y, int z, + int Home, uint32 MasterID, bool ShowEffect); +void ConvinceMonster(TCreature *Master, TCreature *Slave); +void ChallengeMonster(TCreature *Challenger, TCreature *Monster); + +void InitNPCs(void); +void InitNonplayer(void); +void ExitNonplayer(void); // crplayer.cc // ============================================================================= diff --git a/src/crcombat.cc b/src/crcombat.cc index 0b45ea4..df8b312 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -420,7 +420,7 @@ void TCombat::SetAttackDest(uint32 TargetID, bool Follow){ } if(Master->Type == PLAYER && Target->Type != PLAYER){ - if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + if(Target->IsInvisible()){ this->StopAttack(0); throw TARGETLOST; } @@ -463,7 +463,7 @@ void TCombat::CanToDoAttack(void){ } if(Master->Type == PLAYER && Target->Type != PLAYER){ - if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + if(Target->IsInvisible()){ this->StopAttack(0); throw TARGETLOST; } @@ -559,7 +559,7 @@ void TCombat::Attack(void){ } if(Master->Type == PLAYER && Target->Type != PLAYER){ - if(Target->Outfit.OutfitID == 0 && Target->Outfit.ObjectType == 0){ + if(Target->IsInvisible()){ this->StopAttack(0); throw TARGETLOST; } @@ -696,7 +696,7 @@ void TCombat::WandAttack(TCreature *Target){ ASSERT(this->Wand != NONE); TCreature *Master = this->Master; - int Distance = std::min( + int Distance = std::max( std::abs(Master->posx - Target->posx), std::abs(Master->posy - Target->posy)); diff --git a/src/crmain.cc b/src/crmain.cc index 3d45827..b786676 100644 --- a/src/crmain.cc +++ b/src/crmain.cc @@ -589,11 +589,9 @@ int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){ this->DamageStimulus(Attacker->ID, Damage, DamageType); - // NOTE(fusion): Remove non-player illusion. Might as well be an inlined + // NOTE(fusion): Remove non-player invisibility. Might as well be an inlined // function. - if(this->Type != PLAYER - && this->Outfit.OutfitID == 0 - && this->Outfit.ObjectType == 0){ + if(this->Type != PLAYER && this->IsInvisible()){ this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); this->Outfit = this->OrgOutfit; AnnounceChangedCreature(this->ID, CREATURE_OUTFIT_CHANGED); diff --git a/src/crnonpl.cc b/src/crnonpl.cc index 433718a..3d43012 100644 --- a/src/crnonpl.cc +++ b/src/crnonpl.cc @@ -6,6 +6,8 @@ #include "stubs.hh" +#include + static vector NonplayerList(0, 10000, 1000, NULL); static int FirstFreeNonplayer; @@ -1422,16 +1424,8 @@ void ProcessMonsterhomes(void){ break; } - // TODO(fusion): There is probably a helper function to check whether - // a floor is visible from another. - if(Player->posz <= 7){ - if(MH->z > 7){ - continue; - } - }else{ - if(std::abs(Player->posz - MH->z) > 2){ - continue; - } + if(!Player->CanSeeFloor(MH->z)){ + continue; } int DistanceX = std::abs(Player->posx - MH->x); @@ -1772,18 +1766,17 @@ void TNPC::IdleStimulus(void){ } } - if(!FoundDest){ + if(FoundDest){ + try{ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + this->ToDoWait(2000); + this->ToDoStart(); + }catch(RESULT r){ + error("TNPC::IdleStimulus: Exception %d!\n", r); + } + }else{ this->ToDoWait(2000); this->ToDoStart(); - return; - } - - try{ - this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); - this->ToDoWait(2000); - this->ToDoStart(); - }catch(RESULT r){ - error("TNPC::IdleStimulus: Exception %d!\n", r); } } } @@ -2025,6 +2018,8 @@ TMonster::TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID) : return; } + // TODO(fusion): This part is very similar to `ProcessMonsterRaids` when + // adding extra items to spawned monsters. try{ if(this->Master == 0 && RaceData[Race].Items > 0){ Object Bag = Create(GetBodyContainer(this->ID, INVENTORY_BAG), @@ -2045,7 +2040,6 @@ TMonster::TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID) : } for(int j = 0; j < Repeat; j += 1){ - // TODO(fusion): Same as `ProcessMonsterRaids`. Object Item = NONE; try{ if(ItemType.getFlag(WEAPON) @@ -2108,3 +2102,1045 @@ TMonster::~TMonster(void){ NotifyMonsterhomeOfDeath(this->Home); } } + +bool TMonster::MovePossible(int x, int y, int z, bool Execute, bool Jump){ + if(this->posz != z){ + error("TMonster::MovePossible: Prüfung über Ebenen hinweg ([%d,%d,%d] -> [%d,%d,%d]).\n", + this->posx, this->posy, this->posz, x, y, z); + return false; + } + + if(this->State != ATTACKING && this->State != PANIC){ + if(!MonsterhomeInRange(this->Home, x, y, z)){ + return false; + } + + // NOTE(fusion): Max distance. + int Distance = std::max( + std::abs(x - this->posx), + std::abs(y - this->posy)); + if(Distance > this->Radius){ + return false; + } + } + + if(this->Skills[SKILL_GO_STRENGTH]->Act < 0){ + error("TMonster::MovePossible: Monster %s an [%d,%d,%d] darf sich nicht bewegen.\n", + this->Name, this->posx, this->posy, this->posz); + return false; + } + + if(IsProtectionZone(x, y, z) || IsHouse(x, y, z)){ + return false; + } + + if(Execute && this->Master != 0 && this->State != ATTACKING && this->State != PANIC){ + TCreature *Master = GetCreature(this->Master); + if(Master != NULL && Master->posz == this->posz){ + // NOTE(fusion): Manhattan distance. + if((std::abs(Master->posx - this->posx) + std::abs(Master->posy - this->posy)) > 1 + && (std::abs(Master->posx - x) + std::abs(Master->posy - y)) <= 1){ + return false; + } + } + } + + // NOTE(fusion): Check destination and retry while we keep kicking blocking + // objects away. + for(int Attempt = 0; Attempt < 100; Attempt += 1){ + Object Obj = GetFirstObject(x, y, z); + if(!Obj.getObjectType().getFlag(BANK)){ + return false; + } + + while(Obj != NONE){ + ObjectType ObjType = Obj.getObjectType(); + if(ObjType.isCreatureContainer()){ + if(this->State != ATTACKING && this->State != PANIC){ + return false; + } + + if(this->Target == 0){ + return false; + } + + if(!RaceData[this->Race].KickCreatures){ + return false; + } + + TCreature *Creature = GetCreature(Obj); + if(Creature == NULL){ + error("TMonster::MovePossible: Kann Hindernis-Kreatur nicht identifizieren.\n"); + return false; + } + + if(Creature->ID == this->Target || Creature->ID == this->Master){ + return false; + } + + if(RaceData[Creature->Race].Unpushable){ + return false; + } + + // TODO(fusion): Why? + if(!RaceData[this->Race].SeeInvisible && Creature->IsInvisible()){ + return false; + } + + if(Creature->Type == NPC){ + return false; + } + + if(Creature->Type == PLAYER){ + if(this->Master != 0 || CheckRight(Creature->ID, IGNORED_BY_MONSTERS)){ + return false; + } + } + + if(Execute){ + if(Creature->Type == PLAYER){ + this->Target = 0; + throw EXHAUSTED; + } + + if(!this->KickCreature(Creature)){ + throw EXHAUSTED; + } + + // NOTE(fusion): Break from the inner loop and retry. + break; + } + }else{ + if(ObjType.getFlag(UNPASS)){ + if(ObjType.getFlag(UNMOVE) || !this->CanKickBoxes()){ + return false; + } + + if(Execute){ + this->KickBoxes(Obj); + + // NOTE(fusion): Break from the inner loop and retry. + break; + } + } + + if(ObjType.getFlag(AVOID)){ + int AvoidDamageTypes = (int)ObjType.getAttribute(AVOIDDAMAGETYPES); + bool IgnoreHazard = (this->State == PANIC && AvoidDamageTypes != 0) + || (RaceData[this->Race].NoPoison && AvoidDamageTypes == DAMAGE_POISON) + || (RaceData[this->Race].NoBurning && AvoidDamageTypes == DAMAGE_FIRE) + || (RaceData[this->Race].NoEnergy && AvoidDamageTypes == DAMAGE_ENERGY); + if(!IgnoreHazard){ + if(ObjType.getFlag(UNMOVE) || !this->CanKickBoxes()){ + return false; + } + + if(Execute){ + this->KickBoxes(Obj); + + // NOTE(fusion): Break from the inner loop and retry. + break; + } + } + } + } + Obj = Obj.getNextObject(); + } + + if(Obj == NONE){ + return true; + } + } + + error("TMonster::MovePossible: Endlosschleife vermutet für %s an [%d,%d,%d].\n", + this->Name, x, y, z); + return false; +} + +bool TMonster::IsPeaceful(void){ + return this->Master != 0 + && IsCreaturePlayer(this->Master); +} + +uint32 TMonster::GetMaster(void){ + return this->Master; +} + +void TMonster::DamageStimulus(uint32 AttackerID, int Damage, int DamageType){ + if(AttackerID != 0 && Damage != 0){ + if(this->State == SLEEPING){ + if(this->Target == 0){ + this->State = PANIC; + }else{ + this->State = UNDERATTACK; + } + this->ToDoYield(); + }else{ + if(this->Target == 0){ + this->State = PANIC; + }else if(this->State == IDLE){ + this->State = UNDERATTACK; + } + } + } +} + +void TMonster::IdleStimulus(void){ + if(this->LockToDo || this->LoggingOut){ + return; + } + + if(this->LifeEndRound != 0 && this->LifeEndRound <= RoundNr){ + print(3, "Lebenszeit für %s abgelaufen.\n", this->Name); + this->StartLogout(true, true); + this->State = SLEEPING; + return; + } + + if(this->Master != 0){ + TCreature *Master = GetCreature(this->Master); + bool MasterIsPlayer = IsCreaturePlayer(this->Master); + bool ShouldDespawn = false; + if(Master == NULL){ + if(MasterIsPlayer){ + print(3, "Kreatur %s verliert ihren Spieler-Master.\n", this->Name); + }else{ + print(3, "Kreatur %s verliert ihr Mutter-Monster.\n", this->Name); + } + ShouldDespawn = true; + }else if(MasterIsPlayer && Master->SummonedCreatures == 0){ + print(3, "Spieler-Master hat sich eben wieder neu eingeloggt.\n"); + ShouldDespawn = true; + }else if(!MasterIsPlayer && Master->posz != this->posz){ + print(3, "Mutter-Monster ist zu weit entfernt.\n"); + ShouldDespawn = true; + }else if(std::abs(Master->posz - this->posz) > 1 + || std::abs(Master->posx - this->posx) > 30 + || std::abs(Master->posy - this->posy) > 30){ + if(MasterIsPlayer){ + print(3, "Spieler-Master ist zu weit entfernt.\n"); + }else{ + print(3, "Mutter-Monster ist zu weit entfernt.\n"); + } + ShouldDespawn = true; + } + + if(ShouldDespawn){ + if(MasterIsPlayer){ + this->StartLogout(true, true); + }else{ + this->Kill(); + } + this->State = SLEEPING; + return; + } + + if(Master->Combat.Following){ + this->Target = 0; + }else{ + this->Target = Master->Combat.AttackDest; + } + + if(this->Target == 0 || this->Target == this->ID){ + this->Target = this->Master; + } + + }else{ + if(!MonsterhomeInRange(this->Home, this->posx, this->posy, this->posz)){ + TMonsterhome *MH = Monsterhome.at(this->Home); + print(3, "%s an [%d,%d,%d] zu weit von [%d,%d,%d] entfernt.\n", + this->Name, this->posx, this->posy, this->posz, MH->x, MH->y, MH->z); + this->StartLogout(true, true); + this->State = SLEEPING; + return; + } + } + + if(this->Target != 0){ + TCreature *Target = GetCreature(this->Target); + bool LoseTarget = (Target == NULL) + // RANGE CHECK + || Target->posz != this->posz + || std::abs(Target->posx - this->posx) > 10 + || std::abs(Target->posy - this->posy) > 10 + // FIELD CHECK + || IsProtectionZone(Target->posx, Target->posy, Target->posz) + || IsHouse(Target->posx, Target->posy, Target->posz) + // INVISIBILITY CHECK + || (Target->IsInvisible() && !RaceData[this->Race].SeeInvisible) + // LOSETARGET CHECK + || (this->Master == 0 && random(0, 99) < RaceData[this->Race].LoseTarget); + if(LoseTarget){ + this->Target = 0; + } + } + + if(this->State != PANIC && this->State != UNDERATTACK){ + this->State = IDLE; + } + + // NOTE(fusion): TALKING. + if(RaceData[this->Race].Talks > 0 && (rand() % 50) == 0){ + int TalkNr = rand() % (RaceData[this->Race].Talks + 1); + const char *Text = GetDynamicString(*RaceData[this->Race].Talk.at(TalkNr)); + if(Text != 0 && Text[0] != 0){ + // TODO(fusion): We were only checking for a `#` but it could be + // problematic for any two character nul terminated string, since + // adding 3 would make it point out of bounds. Poor string management + // is a recurring theme. + int Mode = TALK_ANIMAL_LOW; + if(Text[0] == '#' && (Text[1] == 'y' || Text[1] == 'Y') && Text[2] == ' '){ + Mode = TALK_ANIMAL_LOUD; + Text += 3; + } + + try{ + Talk(this->ID, Mode, NULL, Text, false); + }catch(RESULT r){ + error("TMonster::IdleStimulus: Exception %d bei Talk.\n", r); + } + }else{ + // TODO(fusion): The original wouldn't check if `Text` is actually + // valid which could also be a problem. + error("TMonster::IdleStimulus: Race %d talk entry %d is invalid.", + this->Race, TalkNr); + } + } + + // NOTE(fusion): TARGETING. + if(this->Target == 0){ + bool ShouldSleep = true; + if(this->Master == 0){ + // IMPORTANT(fusion): We don't iterate over the last strategy on purpose. + int Strategy = 0; + int StrategyRoll = random(0, 99); + while(Strategy < (NARRAY(TRaceData::Strategy) - 1)){ + if(StrategyRoll < RaceData[this->Race].Strategy[Strategy]){ + break; + } + StrategyRoll -= RaceData[this->Race].Strategy[Strategy]; + Strategy += 1; + } + + int BestStrategyParam = INT_MIN; + int BestTieBreaker = 0; + TFindCreatures Search(12, 12, this->ID, FIND_PLAYERS | FIND_MONSTERS); + while(true){ + uint32 TargetID = Search.getNext(); + if(TargetID == 0){ + break; + } + + TCreature *Target = GetCreature(TargetID); + if(Target == NULL){ + error("TMonster::IdleStimulus: Kreatur existiert nicht.\n"); + continue; + } + + if(Target->Type == MONSTER && !((TMonster*)Target)->IsPlayerControlled()){ + continue; + } + + if(Target->CanSeeFloor(this->posz)){ + ShouldSleep = false; + } + + // TODO(fusion): This is quite similar to the conditions for losing + // the target. Perhaps there is some common inlined function. + int DistanceX = std::abs(Target->posx - this->posx); + int DistanceY = std::abs(Target->posy - this->posy); + if((Target->posz != this->posz || DistanceX > 10 || DistanceY > 10) + ||(Target->Type == PLAYER && CheckRight(Target->ID, IGNORED_BY_MONSTERS)) + || (Target->IsInvisible() && !RaceData[this->Race].SeeInvisible) + || IsProtectionZone(Target->posx, Target->posy, Target->posz) + || IsHouse(Target->posx, Target->posy, Target->posz) + || std::abs(Target->posx - this->posx) > 10 + || std::abs(Target->posy - this->posy) > 10){ + continue; + } + + int StrategyParam = 0; + if(Strategy == 0){ // STRATEGY_NEAREST + StrategyParam = -(DistanceX + DistanceY); + }else if(Strategy == 1){ // STRATEGY_LOWEST_HEALTH + StrategyParam = -Target->Skills[SKILL_HITPOINTS]->Get(); + }else if(Strategy == 2){ // STRATEGY_MOST_DAMAGE + StrategyParam = this->Combat.GetDamageByCreature(TargetID); + }else if(Strategy == 3){ // STRATEGY_RANDOM + // no-op + }else{ + error("TMonster::IdleStimulus: Unbekannte Strategie %d.\n", Strategy); + } + + // NOTE(fusion): We're looking for the creature that maximizes + // the strategy parameter. + int TieBreaker = random(0, 99); + if(StrategyParam >= BestStrategyParam + ||(StrategyParam == BestStrategyParam && TieBreaker > BestTieBreaker)){ + this->Target = TargetID; + BestTieBreaker = TieBreaker; + BestStrategyParam = StrategyParam; + } + } + } + + if(ShouldSleep && this->Target == 0 + && this->State != UNDERATTACK + && this->State != PANIC){ + if(this->Master == 0){ + this->State = SLEEPING; + }else{ + this->ToDoWait(1000); + this->ToDoStart(); + } + return; + } + + if(this->State == PANIC && this->Target == 0){ + this->State = IDLE; + } + + if(this->State == UNDERATTACK){ + this->State = IDLE; + } + } + + // NOTE(fusion): CASTING. + // TODO(fusion): It is highly unlikely but we could cast all spells at once. + // I'm not sure this is correct but I can see it happening. + if(RaceData[this->Race].Spells > 0){ + TCreature *Target = GetCreature(this->Target); + for(int SpellNr = 1; + SpellNr <= RaceData[this->Race].Spells; + SpellNr += 1){ + TSpellData *SpellData = RaceData[this->Race].Spell.at(SpellNr); + if((rand() % SpellData->Delay) != 0){ + continue; + } + + if(this->IsFleeing() && random(1, 3) == 1){ + continue; + } + + TImpact *Impact = NULL; + switch(SpellData->Impact){ + case IMPACT_DAMAGE:{ + int DamageType = SpellData->ImpactParam1; + int Damage = SpellData->ImpactParam2; + int Variation = SpellData->ImpactParam3; + Damage = ComputeDamage(this, 0, Damage, Variation); + Impact = new TDamageImpact(this, DamageType, Damage, true); + break; + } + + case IMPACT_FIELD:{ + int FieldType = SpellData->ImpactParam1; + Impact = new TFieldImpact(this, FieldType); + break; + } + + case IMPACT_HEALING:{ + int Amount = SpellData->ImpactParam1; + int Variation = SpellData->ImpactParam2; + Amount = ComputeDamage(this, 0, Amount, Variation); + Impact = new THealingImpact(this, Amount); + break; + } + + case IMPACT_SPEED:{ + int Percent = SpellData->ImpactParam1; + int Variation = SpellData->ImpactParam2; + int Duration = SpellData->ImpactParam3; + Percent = ComputeDamage(this, 0, Percent, Variation); + Impact = new TSpeedImpact(this, Percent, Duration); + break; + } + + case IMPACT_DRUNKEN:{ + int Strength = SpellData->ImpactParam1; + int Variation = SpellData->ImpactParam2; + int Duration = SpellData->ImpactParam3; + Strength = ComputeDamage(this, 0, Strength, Variation); + Impact = new TDrunkenImpact(this, Strength, Duration); + break; + } + + case IMPACT_STRENGTH:{ + int Skills = SpellData->ImpactParam1; + int Percent = SpellData->ImpactParam2; + int Variation = SpellData->ImpactParam3; + int Duration = SpellData->ImpactParam4; + Percent = ComputeDamage(this, 0, Percent, Variation); + Impact = new TStrengthImpact(this, Skills, Percent, Duration); + break; + } + + case IMPACT_OUTFIT:{ + TOutfit Outfit = {}; + Outfit.OutfitID = SpellData->ImpactParam1; + Outfit.PackedData = (uint32)SpellData->ImpactParam2; + int Duration = SpellData->ImpactParam3; + Impact = new TOutfitImpact(this, Outfit, Duration); + break; + } + + case IMPACT_SUMMON:{ + int SummonRace = SpellData->ImpactParam1; + int MaxSummons = SpellData->ImpactParam2; + Impact = new TSummonImpact(this, SummonRace, MaxSummons); + break; + } + } + + if(Impact != NULL){ + switch(SpellData->Shape){ + case SHAPE_ACTOR:{ + int Effect = SpellData->ShapeParam1; + ActorShapeSpell(this, Impact, Effect); + break; + } + + case SHAPE_VICTIM:{ + if(Target != NULL){ + this->Rotate(Target); + + int Range = SpellData->ShapeParam1; + int Animation = SpellData->ShapeParam2; + int Effect = SpellData->ShapeParam3; + VictimShapeSpell(this, Target, Range, + Animation, Impact, Effect); + } + break; + } + + case SHAPE_ORIGIN:{ + int Radius = SpellData->ShapeParam1; + int Effect = SpellData->ShapeParam2; + OriginShapeSpell(this, Radius, Impact, Effect); + break; + } + + case SHAPE_DESTINATION:{ + if(Target != NULL){ + this->Rotate(Target); + + int Range = SpellData->ShapeParam1; + int Animation = SpellData->ShapeParam2; + int Radius = SpellData->ShapeParam3; + int Effect = SpellData->ShapeParam4; + DestinationShapeSpell(this, Target, Range, + Animation, Radius, Impact, Effect); + } + break; + } + + case SHAPE_ANGLE:{ + if(Target != NULL){ + this->Rotate(Target); + } + + int Angle = SpellData->ShapeParam1; + int Range = SpellData->ShapeParam2; + int Effect = SpellData->ShapeParam3; + AngleShapeSpell(this, Angle, Range, Impact, Effect); + break; + } + } + + delete Impact; + } + } + } + + // NOTE(fusion): WALKING. What was already bad got even worse. + TCreature *Target = GetCreature(this->Target); + if(this->Target != 0 && Target == NULL){ + error("TMonster::IdleStimulus: Kreatur existiert nicht.\n"); + this->Target = 0; + } + + try{ + if(Target != NULL){ + if(this->IsFleeing()){ + int DestX, DestY, DestZ; + if(SearchFlightField(this->ID, this->Target, &DestX, &DestY, &DestZ)){ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + this->ToDoStart(); + return; + } + }else if(this->Target == this->Master){ + // NOTE(fusion): Manhattan distance. + int DistanceX = std::abs(Target->posx - this->posx); + int DistanceY = std::abs(Target->posy - this->posy); + int Distance = DistanceX + DistanceY; + if(Distance > 1){ + if(Distance == 2){ + this->ToDoWait(1000); + }else{ + if(Distance == 3){ + this->ToDoWait(1000); + } + this->ToDoGo(Target->posx, Target->posy, Target->posz, false, 3); + } + this->ToDoStart(); + return; + } + }else{ + if(this->Skills[SKILL_FIST]->Get() > 0 && this->State != PANIC){ + this->State = ATTACKING; + } + + if(this->State == ATTACKING || this->State == PANIC){ + this->Combat.SetAttackDest(this->Target, false); + this->Combat.SetChaseMode(CHASE_MODE_NONE); + } + + // TODO(fusion): We're doing something similar to `TCombat::CanToDoAttack` + // with added random steps around the attacking position. This function is + // too convoluted and we should definitely split it into smaller ones. I + // don't have a problem with large functions but this it too much. + + // NOTE(fusion): Max distance. + int Distance = std::max( + std::abs(Target->posx - this->posx), + std::abs(Target->posy - this->posy)); + if(!RaceData[this->Race].DistanceFighting + || !ThrowPossible(this->posx, this->posy, this->posz, + Target->posx, Target->posy, Target->posz, 0)){ + this->Combat.SetChaseMode(CHASE_MODE_CLOSE); + if(Distance > 1){ + // TODO(fusion): Not sure what is going on here. I think + // it's because `ToDoAttack` with `CHASE_MODE_CLOSE` will + // already take care of walking to the target? + if(this->State != ATTACKING && this->State != PANIC){ + this->ToDoGo(Target->posx, Target->posy, Target->posz, false, 3); + } + }else{ + int DestX = this->posx; + int DestY = this->posy; + int DestZ = this->posz; + switch(rand() % 5){ + case 0: DestX -= 1; break; + case 1: DestX += 1; break; + case 2: DestY -= 1; break; + case 3: DestY += 1; break; + case 4: break; // no step + } + + int DestDistance = std::max( + std::abs(Target->posx - DestX), + std::abs(Target->posy - DestY)); + if(DestDistance == 1 && this->MovePossible(DestX, DestY, DestZ, true, false)){ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + } + + if(this->State == PANIC){ + this->State = ATTACKING; + } + } + }else{ + if(Distance < 4){ + int DestX, DestY, DestZ; + if(SearchFlightField(this->ID, this->Target, &DestX, &DestY, &DestZ)){ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + }else{ + this->ToDoWait(1000); + } + }else if(Distance > 4){ + this->ToDoGo(Target->posx, Target->posy, Target->posz, false, Distance - 4); + }else{ + int DestX = this->posx; + int DestY = this->posy; + int DestZ = this->posz; + switch(rand() % 5){ + case 0: DestX -= 1; break; + case 1: DestX += 1; break; + case 2: DestY -= 1; break; + case 3: DestY += 1; break; + case 4: break; // no step + } + + int DestDistance = std::max( + std::abs(Target->posx - DestX), + std::abs(Target->posy - DestY)); + if(DestDistance == 4 && this->MovePossible(DestX, DestY, DestZ, true, false)){ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + } + + this->ToDoWait(1000); + } + } + + if(this->State == ATTACKING || this->State == PANIC){ + this->Rotate(Target); + if(this->Master != this->Target){ + this->ToDoAttack(); + }else{ + error("TMonster::IdleStimulus: %s greift Master %u an (St=%d, T: %u).\n", + this->Name, this->Master, this->State, this->Target); + } + }else{ + this->ToDoWait(1000); + } + + this->ToDoStart(); + return; + } + } + }catch(RESULT r){ + this->Target = 0; + this->ToDoClear(); + if(r == EXHAUSTED){ + this->ToDoWait(1000); + this->ToDoStart(); + return; + } + } + + // TODO(fusion): This part is similar to `TNPC::IdleStimulus`. Perhaps there + // is a common function in `TNonplayer` that does this? + bool FoundDest = false; + int DestX, DestY, DestZ; + for(int i = 0; i < 10; i += 1){ + DestX = this->posx; + DestY = this->posy; + DestZ = this->posz; + switch(rand() % 4){ + case 0: DestX -= 1; break; + case 1: DestX += 1; break; + case 2: DestY -= 1; break; + case 3: DestY += 1; break; + } + + if(this->MovePossible(DestX, DestY, DestZ, true, false)){ + FoundDest = true; + break; + } + } + + if(FoundDest){ + try{ + this->ToDoGo(DestX, DestY, DestZ, true, INT_MAX); + this->ToDoWait(1000); + this->ToDoStart(); + }catch(RESULT r){ + error("TMonster::IdleStimulus: Exception %d.\n", r); + } + }else{ + this->ToDoWait(1000); + this->ToDoStart(); + } +} + +void TMonster::CreatureMoveStimulus(uint32 CreatureID, int Type){ + if(CreatureID == this->ID){ + if(this->State == SLEEPING && Type != OBJECT_DELETED){ + this->State = IDLE; + this->ToDoYield(); + } + return; + } + + if(this->State == SLEEPING && Type != OBJECT_DELETED){ + TCreature *Creature = GetCreature(CreatureID); + if(Creature == NULL){ + error("TMonster::CreatureMoveStimulus: Kreatur %u existiert nicht.\n", CreatureID); + return; + } + + if(Creature->Type == NPC){ + return; + } + + if(Creature->Type == MONSTER && !((TMonster*)Creature)->IsPlayerControlled()){ + return; + } + + this->State = IDLE; + this->ToDoYield(); + } + + TCreature::CreatureMoveStimulus(CreatureID, Type); +} + +bool TMonster::CanKickBoxes(void){ + bool Result = RaceData[this->Race].KickBoxes; + if(!Result && this->Master != 0){ + TCreature *Master = GetCreature(this->Master); + Result = (Master != NULL && Master->Type == MONSTER + && ((TMonster*)Master)->CanKickBoxes()); + } + return Result; +} + +void TMonster::KickBoxes(Object Obj){ + if(!Obj.exists()){ + error("TMonster::KickBoxes: Übergebenes Objekt existiert nicht.\n"); + return; + } + + int ObjX, ObjY, ObjZ; + GetObjectCoordinates(Obj, &ObjX, &ObjY, &ObjZ); + + try{ + bool ObjMoved = false; + int OffsetX[4] = { 0, 0, -1, 1}; + int OffsetY[4] = {-1, 1, 0, 0}; + for(int i = 0; i < 4; i += 1){ + int DestX = ObjX + OffsetX[i]; + int DestY = ObjY + OffsetY[i]; + int DestZ = ObjZ; + if(DestX == this->posx && DestY == this->posy){ + continue; + } + + if(CoordinateFlag(DestX, DestY, DestZ, BANK) + && !CoordinateFlag(DestX, DestY, DestZ, UNPASS)){ + Object Dest = GetMapContainer(DestX, DestY, DestZ); + ::Move(this->ID, Obj, Dest, -1, false, NONE); + ObjMoved = true; + break; + } + } + + if(!ObjMoved){ + GraphicalEffect(Obj, EFFECT_BLOCK_HIT); + Delete(Obj, -1); + } + }catch(RESULT r){ + error("TMonster::KickBoxes: Exception %d, Objekt %d.\n", + r, Obj.getObjectType().TypeID); + error("# eigene Position: [%d,%d,%d] - Objektposition: [%d,%d,%d]\n", + this->posx, this->posy, this->posz, ObjX, ObjY, ObjZ); + } +} + +bool TMonster::KickCreature(TCreature *Creature){ + if(Creature == NULL){ + error("TMonster::KickCreature: Übergebene Kreatur existiert nicht.\n"); + return false; + } + + if(Creature->Type != MONSTER){ + error("TMonster::KickCreature: Zu verschiebende Kreatur ist kein Monster.\n"); + return false; + } + + print(3, "%s verschiebt %s.\n", this->Name, Creature->Name); + + // TODO(fusion): Declare these here so they can be used within the catch + // block to print out what's on the last position we tried to move the + // creature to. + int DestX = Creature->posx; + int DestY = Creature->posy; + int DestZ = Creature->posz; + bool CreatureMoved = false; + try{ + int OffsetX[4] = { 0, 0, -1, 1}; + int OffsetY[4] = {-1, 1, 0, 0}; + for(int i = 0; i < 4; i += 1){ + DestX = Creature->posx + OffsetX[i]; + DestY = Creature->posy + OffsetY[i]; + if(DestX == this->posx && DestY == this->posy){ + continue; + } + + if(Creature->MovePossible(DestX, DestY, DestZ, true, false) + && !CoordinateFlag(DestX, DestY, DestZ, AVOID)){ + Object Dest = GetMapContainer(DestX, DestY, DestZ); + ::Move(this->ID, Creature->CrObject, Dest, -1, false, NONE); + CreatureMoved = true; + break; + } + } + + if(!CreatureMoved){ + print(3, "Kein Platz zum Verschieben => Töten.\n"); + GraphicalEffect(Creature->CrObject, EFFECT_BLOCK_HIT); + Creature->Combat.AddDamageToCombatList(this->ID, + Creature->Skills[SKILL_HITPOINTS]->Get()); + Creature->Kill(); + } + }catch(RESULT r){ + error("TMonster::KickCreature: Exception %d, Kreatur %s.\n", + r, Creature->Name); + error("# eigene Position: [%d,%d,%d] - Hindernisposition: [%d,%d,%d]\n", + this->posx, this->posy, this->posz, + Creature->posx, Creature->posy, Creature->posz); + + error("# Objekte auf Zielfeld [%d,%d,%d]:\n", DestX, DestY, DestZ); + Object Obj = GetFirstObject(DestX, DestY, DestZ); + while(Obj != NONE){ + error("# %d\n", Obj.getObjectType().TypeID); + Obj = Obj.getNextObject(); + } + } + + return CreatureMoved; +} + +void TMonster::Convince(TCreature *NewMaster){ + if(NewMaster == NULL){ + error("TMonster::Convince: NewMaster ist NULL."); + return; + } + + if(this->Home != 0){ + NotifyMonsterhomeOfDeath(this->Home); + } + + this->Home = 0; + if(this->Master != 0){ + TCreature *OldMaster = GetCreature(this->Master); + if(OldMaster != NULL){ + OldMaster->SummonedCreatures -= 1; + } + } + + this->Master = NewMaster->ID; + NewMaster->SummonedCreatures += 1; + + this->ToDoClear(); + this->ToDoWait(100); + this->ToDoStart(); +} + +void TMonster::SetTarget(TCreature *NewTarget){ + if(NewTarget == NULL){ + error("TMonster::SetTarget: NewTarget ist NULL.\n"); + return; + } + + if(this->Master == 0){ + this->Target = NewTarget->ID; + this->Rotate(NewTarget); + this->ToDoYield(); + } +} + +bool TMonster::IsPlayerControlled(void){ + bool Result = false; + if(this->Master != 0){ + TCreature *Master = GetCreature(this->Master); + Result = (Master && Master->Type == PLAYER); + } + return Result; +} + +bool TMonster::IsFleeing(void){ + bool Result = false; + if(this->Master == 0){ + int HitPoints = this->Skills[SKILL_HITPOINTS]->Get(); + int FleeThreshold = RaceData[this->Race].FleeThreshold; + Result = HitPoints <= FleeThreshold; + } + return Result; +} + +TCreature *CreateMonster(int Race, int x, int y, int z, int Home, uint32 MasterID, bool ShowEffect){ + if(!IsRaceValid(Race)){ + error("CreateMonster: Ungültige Rassennummer %d.\n", Race); + return NULL; + } + + if(RaceData[Race].Name[0] == 0){ + error("CreateMonster: Daten für Rasse %d nicht definiert.\n", Race); + return NULL; + } + + SearchFreeField(&x, &y, &z, 2, 0, false); + TMonster *Monster = new TMonster(Race, x, y, z, Home, MasterID); + if(ShowEffect){ + GraphicalEffect(x, y, z, EFFECT_ENERGY); + } + return Monster; +} + +void ConvinceMonster(TCreature *Master, TCreature *Slave){ + if(Master == NULL){ + error("ConvinceMonster: Master existiert nicht.\n"); + return; + } + + if(Slave == NULL){ + error("ConvinceMonster: Slave existiert nicht.\n"); + return; + } + + if(Slave->Type != MONSTER){ + error("ConvinceMonster: Slave ist kein Monster.\n"); + return; + } + + ((TMonster*)Slave)->Convince(Master); +} + +void ChallengeMonster(TCreature *Challenger, TCreature *Monster){ + if(Challenger == NULL){ + error("ChallengeMonster: Herausforderer existiert nicht.\n"); + return; + } + + if(Monster == NULL){ + error("ChallengeMonster: Monster existiert nicht.\n"); + return; + } + + if(Monster->Type != MONSTER){ + error("ChallengeMonster: Monster ist kein Monster.\n"); + return; + } + + ((TMonster*)Monster)->Target = Challenger->ID; + Monster->Rotate(Challenger); + Monster->ToDoYield(); +} + +// Initialization +// ============================================================================= +void InitNPCs(void){ + DIR *NpcDir = opendir(NPCPATH); + if(NpcDir == NULL){ + error("InitNPCs: Unterverzeichnis %s nicht gefunden\n", NPCPATH); + throw "Cannot init NPCs"; + } + + char FileName[4096]; + while(dirent *DirEntry = readdir(NpcDir)){ + if(DirEntry->d_type != DT_REG){ + continue; + } + + const char *FileExt = findLast(DirEntry->d_name, '.'); + if(FileExt == NULL || strcmp(FileExt, ".npc") != 0){ + continue; + } + + snprintf(FileName, sizeof(FileName), "%s/%s", NPCPATH, DirEntry->d_name); + new TNPC(FileName); + } + + closedir(NpcDir); +} + +void InitNonplayer(void){ + print(1, "Initializing Nonplayers ...\n"); + FirstFreeNonplayer = 0; + InitNPCs(); + LoadMonsterhomes(); +} + +void ExitNonplayer(void){ + while(FirstFreeNonplayer > 0){ + // NOTE(fusion): Deleting a non player or any other creature will + // automatically remove it from its related creature lists. + TNonplayer *Nonplayer = *NonplayerList.at(0); + delete Nonplayer; + } + + // TODO(fusion): For any reason `BehaviourNodeTable` was originally a + // pointer and it was deleted here. I doesn't really make a difference + // because we'd usually exit after calling this function but if we wanted + // to cleanup all memory, we'd need to add some `freeAll` method to the + // `store` container. +} diff --git a/src/magic.cc b/src/magic.cc index 4ed1826..19a9dc8 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -968,8 +968,7 @@ void KillAllMonsters(TCreature *Actor, int Effect, int Radius){ error("KillAllMonsters: Ungültige Kreatur.\n"); }else if(Actor != Victim && Victim->Type == MONSTER){ print(3, "Töte %s...\n", Victim->Name); - Victim->Skills[SKILL_HITPOINTS]->Set(0); - Victim->Death(); + Victim->Kill(); } } Obj = Obj.getNextObject(); @@ -2395,7 +2394,7 @@ void CancelInvisibility(TCreature *Actor, Object Target, int ManaPoints, int Sou TCreature *Victim = GetCreature(Obj); if(Victim == NULL){ error("CancelInvisibility: Ungültige Kreatur.\n"); - }else if(Victim->Outfit.OutfitID == 0 && Victim->Outfit.ObjectType == 0 + }else if(Victim->IsInvisible() && (WorldType != NON_PVP || !Victim->IsPeaceful())){ Victim->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); if(Victim->Skills[SKILL_ILLUSION]->Get() == 0){ diff --git a/src/magic.hh b/src/magic.hh index adc648f..ff14c48 100644 --- a/src/magic.hh +++ b/src/magic.hh @@ -19,6 +19,13 @@ struct TImpact{ virtual void handleCreature(TCreature *Victim); // VTABLE[1] virtual bool isAggressive(void); // VTABLE[2] + // NOTE(fusion): I don't think the original version had a destructor declared + // here but the compiler complains when calling delete (which seems to only be + // used in `TMonster::IdleStimulus`). + virtual ~TImpact(void){ + // no-op + } + // DATA // ========================================================================= //void *VTABLE; // IMPLICIT diff --git a/src/operate.cc b/src/operate.cc index 6fb7f20..e742806 100644 --- a/src/operate.cc +++ b/src/operate.cc @@ -2787,17 +2787,8 @@ bool SectorRefreshable(int SectorX, int SectorY, int SectorZ){ continue; } - // TODO(fusion): Maybe an inlined function to check whether a player - // can see some floor. All floors above ground, when above ground. Or - // two floors up and down, when underground. - if(Player->posz <= 7){ - if(SectorZ <= 7){ - return false; - } - }else{ - if(std::abs(Player->posz - SectorZ) <= 2){ - return false; - } + if(Player->CanSeeFloor(SectorZ)){ + return false; } } diff --git a/src/stubs.hh b/src/stubs.hh index d8b9839..4b9e3cc 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -24,9 +24,6 @@ extern void ChangeNameDoor(Object Door, TPlayer *Player, const char *Text); extern void CharacterDeathOrder(TCreature *Creature, int OldLevel, uint32 Offender, const char *Remark, bool Unjustified); extern void CleanHouseField(int x, int y, int z); -extern void ConvinceMonster(TCreature *Master, TCreature *Slave); -extern void ChallengeMonster(TCreature *Challenger, TCreature *Monster); -extern TCreature *CreateMonster(int Race, int x, int y, int z, int Home, uint32 Master, bool ShowEffect); extern void DecrementIsOnlineOrder(uint32 CharacterID); extern void GetExitPosition(uint16 HouseID, int *x, int *y, int *z); extern int GetOrderBufferSpace(void); -- cgit v1.2.3