aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-06-25 10:09:17 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-06-25 10:09:17 -0300
commitea319bc2fbef91e3ea062be554b215756648d1d3 (patch)
treeaa26422a754346e9881e5e0b510f78fd4cfe068c
parent8bc2e5dc3a523af471065752a534d20d878c60b0 (diff)
downloadgame-ea319bc2fbef91e3ea062be554b215756648d1d3.tar.gz
game-ea319bc2fbef91e3ea062be554b215756648d1d3.zip
finish `crnonpl.cc`
-rw-r--r--TODO.md1
-rw-r--r--reference/game.c1725
-rw-r--r--src/connections.cc3
-rw-r--r--src/cr.hh46
-rw-r--r--src/crcombat.cc8
-rw-r--r--src/crmain.cc6
-rw-r--r--src/crnonpl.cc1078
-rw-r--r--src/magic.cc5
-rw-r--r--src/magic.hh7
-rw-r--r--src/operate.cc13
-rw-r--r--src/stubs.hh3
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<TSpellData>::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<TNode,256> *psVar1;
- list<> *plVar2;
-
- // try { // try from 080bc67e to 080bc69e has its CatchHandler @ 080bc710
- print();
- FirstFreeNonplayer = 0;
- CurrentNonplayer = 0;
- psVar1 = (store<TNode,256> *)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<TNode> *)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<TNode,256> *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<TNode,256> *)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>(
+ int Distance = std::max<int>(
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 <dirent.h>
+
static vector<TNonplayer*> 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<int>(
+ 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<int>(
+ 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<int>(
+ 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<int>(
+ 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);