diff options
| -rw-r--r-- | TODO.md | 1 | ||||
| -rw-r--r-- | reference/game.c | 1725 | ||||
| -rw-r--r-- | src/connections.cc | 3 | ||||
| -rw-r--r-- | src/cr.hh | 46 | ||||
| -rw-r--r-- | src/crcombat.cc | 8 | ||||
| -rw-r--r-- | src/crmain.cc | 6 | ||||
| -rw-r--r-- | src/crnonpl.cc | 1078 | ||||
| -rw-r--r-- | src/magic.cc | 5 | ||||
| -rw-r--r-- | src/magic.hh | 7 | ||||
| -rw-r--r-- | src/operate.cc | 13 | ||||
| -rw-r--r-- | src/stubs.hh | 3 |
11 files changed, 1120 insertions, 1775 deletions
@@ -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; @@ -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); |
