From dbee845a5cfb6ca419604ac984d1845440f76007 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Tue, 3 Jun 2025 16:00:35 -0300 Subject: more spells --- Makefile | 2 +- reference/game.c | 1139 ------------------------------------------------------ src/creature.cc | 22 +- src/creature.hh | 1 + src/magic.cc | 584 +++++++++++++++++++++++++++- src/stubs.hh | 4 + 6 files changed, 604 insertions(+), 1148 deletions(-) diff --git a/Makefile b/Makefile index d804d60..99b89aa 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ endif HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/crcombat.hh $(SRCDIR)/creature.hh $(SRCDIR)/crskill.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/monster.hh $(SRCDIR)/objects.hh $(SRCDIR)/player.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh $(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/creature.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/player.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj - $(CC) $(CFLAGS) $(LFLAGS) -o $@ $^ + $(CC) -c $(CFLAGS) $(LFLAGS) -o $@ $^ $(BUILDDIR)/config.obj: $(SRCDIR)/config.cc $(HEADERS) @mkdir -p $(@D) diff --git a/reference/game.c b/reference/game.c index 65ac6f5..b469c0e 100644 --- a/reference/game.c +++ b/reference/game.c @@ -20852,1145 +20852,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority return; } -void SummonCreature(TCreature *cr,int Mana,int Race,bool God) - -{ - bool bVar1; - ulong Father; - int iVar2; - undefined4 *puVar3; - int Delay; - int iStack_28; - bool God_local; - int posx; - int posy; - int posz; - - if (cr == (TCreature *)0x0) { - error(&DAT_080fb980); - } - else { - if (Race - 1U < 0x1ff) { - if (God) { - bVar1 = CheckRight(cr->ID,CREATE_MONSTERS); - if (!bVar1) { - return; - } - } - else { - if (((cr->Type == PLAYER) && (bVar1 = CheckRight(cr->ID,CREATE_MONSTERS), !bVar1)) - && (bVar1 = GetRaceNoSummon(Race), bVar1)) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 1; - goto LAB_08083d9d; - } - if (((!God) && (cr->Type == PLAYER)) && (1 < cr->SummonedCreatures)) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x38; - goto LAB_08083d9d; - } - } - iStack_28 = cr->posx; - posx = cr->posy; - posy = cr->posz; - // try { // try from 08083cd8 to 08083e67 has its CatchHandler @ 08083e70 - bVar1 = SearchSummonField(&stack0xffffffd8,&posx,&posy,2); - if (bVar1) { - if (!God) { - iVar2 = GetRaceSummonCost(Race); - Delay = 1000; - if (WorldType != PVP_ENFORCED) { - Delay = 2000; - } - CheckMana(cr,iVar2 + Mana,0,Delay); - } - Father = 0; - if (!God) { - Father = cr->ID; - } - CreateMonster(Race,iStack_28,posx,posy,0,Father,true); - posz = (cr->CrObject).ObjectID; - GraphicalEffect((Object *)&posz,0xd); - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 4; - goto LAB_08083d9d; - } - error(&DAT_080fb940,Race); - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_08083d9d: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Variable defined which should be unmapped: God_local - -void SummonCreature(TCreature *cr,int Mana,char *RaceName,bool God) - -{ - char *pcVar1; - int iVar2; - undefined4 *puVar3; - int i; - int Race_00; - int Race; - int Race_01; - bool God_local; - - if (cr == (TCreature *)0x0) { - pcVar1 = &DAT_080fb980; - } - else { - if (RaceName != (char *)0x0) { - Race_00 = 1; - do { - // try { // try from 08083ec6 to 08083f54 has its CatchHandler @ 08083f72 - pcVar1 = GetRaceName(Race_00); - iVar2 = stricmp(RaceName,pcVar1,-1); - Race_01 = Race_00; - if (iVar2 == 0) break; - Race_00 = Race_00 + 1; - Race_01 = 0; - } while (Race_00 < 0x200); - if (Race_01 != 0) { - SummonCreature(cr,Mana,Race_01,God); - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x19; - goto LAB_08083f2d; - } - pcVar1 = &DAT_080fb9c0; - } - error(pcVar1); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_08083f2d: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -void StartMonsterraid(TCreature *cr,char *RaidName) - -{ - bool bVar1; - undefined4 *puVar2; - char *Text; - Object local_102c [4]; - char local_101c [4]; - char FileName [4096]; - - if (cr == (TCreature *)0x0) { - Text = &DAT_080fba40; - } - else { - if (RaidName != (char *)0x0) { - // try { // try from 08083fc9 to 0808403f has its CatchHandler @ 08084130 - bVar1 = CheckRight(cr->ID,CREATE_MONSTERS); - if (bVar1) { - strLower(RaidName); - snprintf(local_101c,0x1000,"%s/%s.evt",MONSTERPATH,RaidName); - bVar1 = FileExists(local_101c); - if (bVar1) { - // try { // try from 0808406e to 08084094 has its CatchHandler @ 080840d9 - LoadMonsterRaid(local_101c,RoundNr,(bool *)0x0,(int *)0x0,(int *)0x0,(int *)0x0) - ; - local_102c[0].ObjectID = (cr->CrObject).ObjectID; - GraphicalEffect(local_102c,0xd); - } - else { - local_102c[0].ObjectID = (cr->CrObject).ObjectID; - GraphicalEffect(local_102c,3); - } - } - return; - } - Text = &DAT_080fba00; - } - // try { // try from 080840a1 to 080840cf has its CatchHandler @ 08084130 - error(Text); - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void RaiseDead(void) - -{ - bool bVar1; - uint uVar2; - undefined4 *puVar3; - ulong uVar4; - int Delay; - TCreature *in_stack_00000004; - Object *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - char *Text; - int *y_00; - int *z_00; - int local_48; - int x; - int y; - int z; - Object local_2c [7]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbac0; - } - else { - // try { // try from 08084160 to 080843fa has its CatchHandler @ 08084420 - bVar1 = Object::exists(in_stack_00000008); - if (bVar1) { - local_2c[0].ObjectID = in_stack_00000008->ObjectID; - GetObjectCoordinates(local_2c,&local_48,&x,&y); - if (y == in_stack_00000004->posz) { - uVar2 = local_48 - in_stack_00000004->posx; - if (0x7fffffff < uVar2) { - uVar2 = -uVar2; - } - if ((int)uVar2 < 2) { - uVar2 = x - in_stack_00000004->posy; - if (0x7fffffff < uVar2) { - uVar2 = -uVar2; - } - if ((int)uVar2 < 2) { - GetFirstObject(); - in_stack_00000008->ObjectID = z; - local_2c[0].ObjectID = NONE.ObjectID; - if (z != NONE.ObjectID) { - do { - z = NONE.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - Object::getObjectType((Object *)&z); - bVar1 = ObjectType::getFlag((ObjectType *)&z,CORPSE); - if (bVar1) { - Object::getObjectType((Object *)&z); - uVar4 = ObjectType::getAttribute((ObjectType *)&z,CORPSETYPE); - if (uVar4 == 1) break; - } - Object::getNextObject(local_2c); - in_stack_00000008->ObjectID = local_2c[0].ObjectID; - bVar1 = local_2c[0].ObjectID != NONE.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - } while (bVar1); - } - z = NONE.ObjectID; - if (in_stack_00000008->ObjectID == NONE.ObjectID) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x1d; - } - else { - z_00 = &y; - y_00 = &x; - bVar1 = SearchFreeField(&local_48,y_00,z_00,1,0,false); - if ((bVar1) && - (y_00 = (int *)x, z_00 = (int *)y, bVar1 = IsProtectionZone(), !bVar1 - )) { - Delay = 1000; - if (WorldType != PVP_ENFORCED) { - Delay = 2000; - } - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010, - Delay); - z = in_stack_00000008->ObjectID; - Delete((Object *)&z,-1); - CreateMonster(0x21,local_48,x,y,0,in_stack_00000004->ID,true); - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4,y_00,z_00); - *puVar3 = 4; - } - goto LAB_08084322; - } - } - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 5; - goto LAB_08084322; - } - Text = &DAT_080fba80; - } - error(Text); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_08084322: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void MassRaiseDead(void) - -{ - bool bVar1; - ulong uVar2; - undefined4 *puVar3; - int zx; - int iVar4; - TCreature *in_stack_00000004; - Object *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - int in_stack_00000014; - char *Text; - int dx; - int *local_88; - int dy; - int j; - int i; - int sx; - int sy; - int sz; - int x; - int y; - int z; - ulong local_4c; - Object Help; - Object local_3c; - Object Obj; - Object local_2c [7]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbb40; - } - else { - // try { // try from 08084463 to 08084842 has its CatchHandler @ 08084860 - bVar1 = Object::exists(in_stack_00000008); - if (bVar1) { - local_2c[0].ObjectID = in_stack_00000008->ObjectID; - GetObjectCoordinates(local_2c,&sz,&x,&y); - bVar1 = ThrowPossible(in_stack_00000004->posx,in_stack_00000004->posy, - in_stack_00000004->posz,sz,x,y,0); - if (bVar1) { - iVar4 = 1000; - if (WorldType != PVP_ENFORCED) { - iVar4 = 2000; - } - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010,iVar4); - if (((in_stack_00000004->posx != sz) || (in_stack_00000004->posy != x)) || - (in_stack_00000004->posz != y)) { - local_2c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GetMapContainer((int)&local_3c,sz,x); - Missile(local_2c,&local_3c,5); - } - j = 0; - if (-1 < in_stack_00000014) { - local_88 = &Circle[0].Count; - dx = 0; - do { - dy = 0; - iVar4 = dx; - if (0 < *local_88) { - do { - zx = Circle[0].x[iVar4 + dy] + sz; - iVar4 = Circle[0].y[iVar4 + dy] + x; - bVar1 = ThrowPossible(sz,x,y,zx,iVar4,y,0); - if (bVar1) { - GraphicalEffect(); - GetFirstObject(); - local_4c = NONE.ObjectID; - local_2c[0].ObjectID = local_4c; - if (local_3c.ObjectID != NONE.ObjectID) { - do { - local_4c = local_3c.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - Object::getNextObject((Object *)&z); - local_3c.ObjectID = z; - local_2c[0].ObjectID = z; - Object::getObjectType((Object *)&z); - bVar1 = ObjectType::getFlag((ObjectType *)&z,CORPSE); - if (bVar1) { - Object::getObjectType((Object *)&z); - uVar2 = ObjectType::getAttribute - ((ObjectType *)&z,CORPSETYPE); - if (uVar2 == 1) { - sy = y; - i = zx; - sx = iVar4; - bVar1 = SearchFreeField(&i,&sx,&sy,1,0,false); - if ((bVar1) && - (bVar1 = IsProtectionZone(), !bVar1)) { - z = local_4c; - Delete((Object *)&z,-1); - CreateMonster(0x21,i,sx,sy,0, - in_stack_00000004->ID,false); - } - } - } - local_2c[0].ObjectID = NONE.ObjectID; - local_4c = NONE.ObjectID; - } while (local_3c.ObjectID != NONE.ObjectID); - } - } - dy = dy + 1; - iVar4 = j * 0x41; - } while (dy < *local_88); - } - j = j + 1; - local_88 = local_88 + 0x41; - dx = dx + 0x41; - } while (j <= in_stack_00000014); - } - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 7; - goto LAB_0808481f; - } - Text = &DAT_080fbb00; - } - error(Text); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_0808481f: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void Heal(void) - -{ - TSkill *this; - int iVar1; - undefined4 *puVar2; - TSkill *sk; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - char *Text; - Object local_1c [4]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbbc0; - } - else { - if (WorldType == NON_PVP) { - iVar1 = (*in_stack_00000004->_vptr_TCreature[4])(); - if ((char)iVar1 == '\0') { - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 1; - goto LAB_0808496b; - } - } - // try { // try from 080848bd to 0808498a has its CatchHandler @ 080849b0 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - this = (in_stack_00000004->super_TSkillBase).Skills[2]; - if (this != (TSkill *)0x0) { - iVar1 = TSkill::Get(this); - if (0 < iVar1) { - TSkill::Change(this,in_stack_00000010); - if ((in_stack_00000004->super_TSkillBase).Skills[4]->MDAct < 0) { - TSkillBase::SetTimer(&in_stack_00000004->super_TSkillBase); - } - local_1c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_1c,0xd); - } - return; - } - Text = "Heal: Skill HITPOINTS existiert nicht.\n"; - } - error(Text); - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0xffffffff; -LAB_0808496b: - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void MassHeal(void) - -{ - TSkill *this; - bool bVar1; - TCreature *pTVar2; - int iVar3; - undefined4 *puVar4; - TCreature *in_stack_00000004; - Object *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - int in_stack_00000014; - int in_stack_00000018; - char *Text; - ulong *puVar5; - int local_78; - int *local_74; - int local_70; - int j; - int i; - int x; - int y; - int z; - ulong local_4c; - Object Help; - Object local_3c; - Object Obj; - Object local_2c [7]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbc80; - } - else { - // try { // try from 080849f3 to 08084d1a has its CatchHandler @ 08084d73 - bVar1 = Object::exists(in_stack_00000008); - if (bVar1) { - local_2c[0].ObjectID = in_stack_00000008->ObjectID; - GetObjectCoordinates(local_2c,&i,&x,&y); - bVar1 = ThrowPossible(in_stack_00000004->posx,in_stack_00000004->posy, - in_stack_00000004->posz,i,x,y,0); - if (bVar1) { - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010,1000); - if (((in_stack_00000004->posx != i) || (in_stack_00000004->posy != x)) || - (in_stack_00000004->posz != y)) { - local_2c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GetMapContainer((int)&local_3c,i,x); - Missile(local_2c,&local_3c,5); - } - j = 0; - if (-1 < in_stack_00000018) { - local_74 = &Circle[0].Count; - local_78 = 0; - do { - local_70 = 0; - iVar3 = local_78; - if (0 < *local_74) { - do { - bVar1 = ThrowPossible(i,x,y,i + Circle[0].x[iVar3 + local_70], - x + Circle[0].y[iVar3 + local_70],y,0); - if (bVar1) { - GraphicalEffect(); - GetFirstObject(); - local_4c = NONE.ObjectID; - local_2c[0].ObjectID = local_4c; - if (local_3c.ObjectID != NONE.ObjectID) { - do { - puVar5 = &local_4c; - local_4c = local_3c.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - Object::getNextObject((Object *)&z); - local_3c.ObjectID = z; - local_2c[0].ObjectID = z; - Object::getObjectType((Object *)&z); - if (z == 99) { - z = local_4c; - pTVar2 = GetCreature((Object *)&z); - if (pTVar2 == (TCreature *)0x0) { - error(&DAT_080fe061,puVar5); - } - else if ((WorldType != NON_PVP) || - (iVar3 = (*pTVar2->_vptr_TCreature[4]) - (pTVar2), - (char)iVar3 != '\0')) { - this = (pTVar2->super_TSkillBase).Skills[2]; - if (this == (TSkill *)0x0) { - Text = - "MassHeal: Skill HITPOINTS existiert nicht.\n"; - goto LAB_08084ccb; - } - iVar3 = TSkill::Get(this); - if ((0 < iVar3) && - (TSkill::Change(this,in_stack_00000014), - (pTVar2->super_TSkillBase).Skills[4]->MDAct < 0 - )) { - TSkillBase::SetTimer - (&pTVar2->super_TSkillBase); - } - } - } - local_2c[0].ObjectID = NONE.ObjectID; - local_4c = NONE.ObjectID; - } while (local_3c.ObjectID != NONE.ObjectID); - } - } - local_70 = local_70 + 1; - iVar3 = j * 0x41; - } while (local_70 < *local_74); - } - j = j + 1; - local_74 = local_74 + 0x41; - local_78 = local_78 + 0x41; - } while (j <= in_stack_00000018); - } - return; - } - puVar4 = (undefined4 *)__cxa_allocate_exception(4); - *puVar4 = 7; - goto LAB_08084ce2; - } - Text = &DAT_080fbc40; - } -LAB_08084ccb: - error(Text); - puVar4 = (undefined4 *)__cxa_allocate_exception(4); - *puVar4 = 0xffffffff; -LAB_08084ce2: - // WARNING: Subroutine does not return - __cxa_throw(puVar4,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void HealFriend(void) - -{ - TSkill *this; - int iVar1; - uint uVar2; - undefined4 *puVar3; - TSkill *sk; - TCreature *in_stack_00000004; - char *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - int in_stack_00000014; - char *Text; - TPlayer *local_20; - TPlayer *pl; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbd40; - } - else { - if (in_stack_00000008 != (char *)0x0) { - // try { // try from 08084dc8 to 08084f34 has its CatchHandler @ 08084f83 - iVar1 = IdentifyPlayer(in_stack_00000008,false,true,&local_20); - if (iVar1 == -1) { -LAB_08084eff: - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x1b; - } - else { - if (iVar1 == -2) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x1c; - goto LAB_08084f11; - } - if (in_stack_00000004->posz == (local_20->super_TCreature).posz) { - uVar2 = in_stack_00000004->posx - (local_20->super_TCreature).posx; - if (0x7fffffff < uVar2) { - uVar2 = -uVar2; - } - if ((int)uVar2 < 8) { - uVar2 = in_stack_00000004->posy - (local_20->super_TCreature).posy; - if (0x7fffffff < uVar2) { - uVar2 = -uVar2; - } - if ((int)uVar2 < 6) { - this = (local_20->super_TCreature).super_TSkillBase.Skills[2]; - if (this == (TSkill *)0x0) { - Text = "HealFriend: Skill HITPOINTS existiert nicht.\n"; - goto LAB_08084f30; - } - iVar1 = TSkill::Get(this); - if (0 < iVar1) { - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010,1000 - ); - TSkill::Change(this,in_stack_00000014); - if ((local_20->super_TCreature).super_TSkillBase.Skills[4]->MDAct < - 0) { - TSkillBase::SetTimer - (&(local_20->super_TCreature).super_TSkillBase); - } - pl = (TPlayer *)(in_stack_00000004->CrObject).ObjectID; - GraphicalEffect((Object *)&pl,0xd); - pl = (TPlayer *)(local_20->super_TCreature).CrObject.ObjectID; - GraphicalEffect((Object *)&pl,0xf); - return; - } - goto LAB_08084eff; - } - } - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 5; - } - goto LAB_08084f11; - } - Text = &DAT_080fbd00; - } -LAB_08084f30: - error(Text); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_08084f11: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void RefreshMana(void) - -{ - TSkill *this; - undefined4 *puVar1; - TSkill *sk; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - char *Text; - Object local_1c [5]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbdc0; - } - else { - // try { // try from 08084fcd to 08085040 has its CatchHandler @ 08085050 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - this = (in_stack_00000004->super_TSkillBase).Skills[3]; - if (this != (TSkill *)0x0) { - TSkill::Change(this,in_stack_00000010); - local_1c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_1c,0xd); - return; - } - Text = "RefreshMana: Skill MANA existiert nicht.\n"; - } - error(Text); - puVar1 = (undefined4 *)__cxa_allocate_exception(4); - *puVar1 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar1,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void MagicGoStrength(void) - -{ - TSkill *this; - bool bVar1; - char cVar2; - undefined4 *puVar3; - TSkill *sk; - int mdact; - int iVar4; - TCreature *in_stack_00000004; - int *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - int in_stack_00000014; - char *Text; - Object local_2c [7]; - - if (in_stack_00000008 == (int *)0x0) { - Text = &DAT_080fbe40; - } - else { - if (in_stack_00000004 != (TCreature *)0x0) { - if (-1 < in_stack_00000014) { -LAB_080850a0: - // try { // try from 080850b9 to 0808522b has its CatchHandler @ 08085250 - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010,1000); - this = (TSkill *)in_stack_00000008[5]; - if (in_stack_00000014 < -100) { - iVar4 = -0x14 - this->Act; - } - else { - iVar4 = (this->Act * in_stack_00000014) / 100; - } - TSkill::SetMDAct(this,iVar4); - TSkillBase::SetTimer((TSkillBase *)(in_stack_00000008 + 1)); - if (in_stack_00000014 < 0) { - TCreature::BlockLogout(in_stack_00000004,0x3c,in_stack_00000008[0xa3] == 0); - } - local_2c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_2c,0xf); - local_2c[0].ObjectID = in_stack_00000008[0xb4]; - GraphicalEffect(local_2c,0xe); - return; - } - bVar1 = GetRaceNoParalyze(in_stack_00000008[0xa0]); - if (!bVar1) { - if (WorldType == NON_PVP) { - iVar4 = (*in_stack_00000004->_vptr_TCreature[4])(); - if ((char)iVar4 != '\0') { - cVar2 = (**(code **)(*in_stack_00000008 + 0x10))(); - if (cVar2 != '\0') { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x2b; - goto LAB_080851f1; - } - } - } - goto LAB_080850a0; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 1; - goto LAB_080851f1; - } - Text = &DAT_080fbe00; - } - error(Text); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_080851f1: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void Shielding(void) - -{ - undefined4 *puVar1; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - Object local_1c [4]; - - if (in_stack_00000004 != (TCreature *)0x0) { - // try { // try from 0808529f to 0808532a has its CatchHandler @ 08085330 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - TSkillBase::SetTimer(&in_stack_00000004->super_TSkillBase); - local_1c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_1c,0xd); - return; - } - error(&DAT_080fbe80); - puVar1 = (undefined4 *)__cxa_allocate_exception(4); - *puVar1 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar1,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void NegatePoison(void) - -{ - undefined4 *puVar1; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - char *Text; - Object local_1c [5]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fbf00; - } - else { - if (in_stack_00000010 != 0) { - // try { // try from 08085384 to 08085414 has its CatchHandler @ 08085420 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - TSkillBase::SetTimer((TSkillBase *)(in_stack_00000010 + 4)); - local_1c[0].ObjectID = *(ulong *)(in_stack_00000010 + 0x2d0); - GraphicalEffect(local_1c,0xd); - return; - } - Text = "NegatePoison: Zielkreatur existiert nicht.\n"; - } - error(Text); - puVar1 = (undefined4 *)__cxa_allocate_exception(4); - *puVar1 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar1,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void Enlight(void) - -{ - TSkill *pTVar1; - int iVar2; - undefined4 *puVar3; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - Object local_1c [4]; - - if (in_stack_00000004 != (TCreature *)0x0) { - // try { // try from 08085473 to 08085511 has its CatchHandler @ 08085512 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - pTVar1 = (in_stack_00000004->super_TSkillBase).Skills[0xf]; - iVar2 = (*pTVar1->_vptr_TSkill[0xd])(pTVar1); - if (iVar2 <= in_stack_00000010) { - TSkillBase::SetTimer(&in_stack_00000004->super_TSkillBase); - } - local_1c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_1c,0xd); - return; - } - error(&DAT_080fbf40); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void Invisibility(void) - -{ - undefined4 *puVar1; - TCreature *in_stack_00000004; - int in_stack_00000008; - int in_stack_0000000c; - Object local_1c [4]; - - if (in_stack_00000004 != (TCreature *)0x0) { - // try { // try from 08085563 to 080855ff has its CatchHandler @ 08085600 - CheckMana(in_stack_00000004,in_stack_00000008,in_stack_0000000c,1000); - (in_stack_00000004->Outfit).field_1.ObjectType = 0; - (in_stack_00000004->Outfit).OutfitID = 0; - TSkillBase::SetTimer(&in_stack_00000004->super_TSkillBase); - local_1c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GraphicalEffect(local_1c,0xd); - return; - } - error(&DAT_080fbf80); - puVar1 = (undefined4 *)__cxa_allocate_exception(4); - *puVar1 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar1,&RESULT::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void CancelInvisibility(void) - -{ - anon_union_4_2_730cd3ca_for_TOutfit_2 aVar1; - bool bVar2; - bool bVar3; - bool bVar4; - TCreature *pTVar5; - int iVar6; - long lVar7; - ulong uVar8; - undefined4 *puVar9; - TCreature *in_stack_00000004; - Object *in_stack_00000008; - int in_stack_0000000c; - int in_stack_00000010; - int in_stack_00000014; - char *Text; - int dx; - int local_a0; - int *local_9c; - ulong local_94; - int b; - TCreature *dest; - bool EffectShown; - int dy; - int j; - int i; - int x; - int y; - int z; - Object local_5c; - Object Item; - _func_int_varargs **local_4c; - Object Help; - Object local_3c; - Object Obj; - Object local_2c [7]; - - if (in_stack_00000004 == (TCreature *)0x0) { - Text = &DAT_080fc040; - } - else { - // try { // try from 08085643 to 08085bec has its CatchHandler @ 08085c10 - bVar3 = Object::exists(in_stack_00000008); - if (bVar3) { - local_2c[0].ObjectID = in_stack_00000008->ObjectID; - GetObjectCoordinates(local_2c,&i,&x,&y); - bVar3 = ThrowPossible(in_stack_00000004->posx,in_stack_00000004->posy, - in_stack_00000004->posz,i,x,y,0); - if (bVar3) { - CheckMana(in_stack_00000004,in_stack_0000000c,in_stack_00000010,1000); - if (((in_stack_00000004->posx != i) || (in_stack_00000004->posy != x)) || - (in_stack_00000004->posz != y)) { - local_2c[0].ObjectID = (in_stack_00000004->CrObject).ObjectID; - GetMapContainer((int)&local_3c,i,x); - Missile(local_2c,&local_3c,5); - } - j = 1; - if (0 < in_stack_00000014) { - local_9c = &Circle[1].Count; - local_a0 = 0x41; - do { - dy = 0; - iVar6 = local_a0; - if (0 < *local_9c) { - do { - bVar3 = ThrowPossible(i,x,y,Circle[0].x[iVar6 + dy] + i, - Circle[0].y[iVar6 + dy] + x,y,0); - if ((bVar3) && (bVar3 = IsProtectionZone(), !bVar3)) { - bVar3 = false; - GetFirstObject(); - if (local_3c.ObjectID != NONE.ObjectID) { - do { - dest = (TCreature *)&local_4c; - local_4c = (_func_int_varargs **)local_3c.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - Object::getNextObject(&local_5c); - local_3c.ObjectID = local_5c.ObjectID; - local_2c[0].ObjectID = local_5c.ObjectID; - Object::getObjectType(&local_5c); - if ((_func_int_varargs **)local_5c.ObjectID == - (_func_int_varargs **)0x63) { - local_5c.ObjectID = (ulong)local_4c; - pTVar5 = GetCreature(&local_5c); - if (pTVar5 == (TCreature *)0x0) { - error(&DAT_080fbfc0,dest); - } - else { - bVar2 = false; - if (((pTVar5->Outfit).OutfitID == 0) && - ((pTVar5->Outfit).field_1.ObjectType == 0)) { - bVar2 = true; - } - if ((bVar2) && - ((WorldType != NON_PVP || - (iVar6 = (*pTVar5->_vptr_TCreature[4]) - (pTVar5), - (char)iVar6 == '\0')))) { - TSkillBase::SetTimer - (&pTVar5->super_TSkillBase); - iVar6 = TSkill::Get((pTVar5-> - super_TSkillBase).Skills[0x10]); - if (iVar6 == 0) { - aVar1 = (pTVar5->OrgOutfit).field_1; - (pTVar5->Outfit).OutfitID = - (pTVar5->OrgOutfit).OutfitID; - (pTVar5->Outfit).field_1 = aVar1; - AnnounceChangedCreature(pTVar5->ID,3); - local_5c.ObjectID = NONE.ObjectID; - local_2c[0].ObjectID = - (pTVar5->CrObject).ObjectID; - NotifyAllCreatures(local_2c,2,&local_5c); - } - else { - GraphicalEffect(); - bVar3 = true; - if (WorldType == PVP_ENFORCED) { - local_94 = 1; - do { - GetBodyObject((ulong)&local_5c, - pTVar5->ID); - bVar2 = false; - local_2c[0].ObjectID = NONE.ObjectID; - z = NONE.ObjectID; - if (local_5c.ObjectID != NONE.ObjectID - ) { - Object::getObjectType - ((Object *)&z); - bVar4 = ObjectType::getFlag(( - ObjectType *)&z,SKILLBOOST); - if (bVar4) { - Object::getObjectType((Object *)&z); - uVar8 = ObjectType::getAttribute - ((ObjectType *)&z, - BODYPOSITION); - if (uVar8 == local_94) { - Object::getObjectType((Object *)&z); - uVar8 = ObjectType::getAttribute - ((ObjectType *)&z, - SKILLNUMBER); - if (uVar8 == 0x10) { - bVar2 = true; - } - } - } - } - if (bVar2) { - lVar7 = random(); - if (lVar7 == 1) { - z = local_5c.ObjectID; - Delete((Object *)&z,-1); - } - break; - } - local_94 = local_94 + 1; - } while ((int)local_94 < 0xb); - } - } - } - } - } - } while (local_3c.ObjectID != NONE.ObjectID); - } - local_4c = (_func_int_varargs **)NONE.ObjectID; - local_2c[0].ObjectID = NONE.ObjectID; - if (!bVar3) { - GraphicalEffect(); - } - } - dy = dy + 1; - iVar6 = j * 0x41; - } while (dy < *local_9c); - } - j = j + 1; - local_9c = local_9c + 0x41; - local_a0 = local_a0 + 0x41; - } while (j <= in_stack_00000014); - } - return; - } - puVar9 = (undefined4 *)__cxa_allocate_exception(4); - *puVar9 = 7; - goto LAB_08085bc9; - } - Text = &DAT_080fc000; - } - error(Text); - puVar9 = (undefined4 *)__cxa_allocate_exception(4); - *puVar9 = 0xffffffff; -LAB_08085bc9: - // WARNING: Subroutine does not return - __cxa_throw(puVar9,&RESULT::typeinfo,0); -} - - - // WARNING: Unknown calling convention -- yet parameter storage is locked void CreatureIllusion(void) diff --git a/src/creature.cc b/src/creature.cc index 65beee0..d1cb7bf 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -56,14 +56,16 @@ void TCreature::Attack(void){ this->Combat.Attack(); } -// TODO(fusion): This probably belongs elsewhere but we should come back to +// Temporary Location +// ============================================================================= +// ============================================================================= +// TODO(fusion): These probably belong elsewhere but we should come back to // this when we're wrapping up creature files. + bool IsCreaturePlayer(uint32 CreatureID){ return CreatureID < 0x40000000; } -// TODO(fusion): This probably belongs elsewhere but we should come back to -// this when we're wrapping up creature files. void AddKillStatistics(int AttackerRace, int DefenderRace){ // NOTE(fusion): I think the race name can be "human" only for players, // which means we're probably tracking how many creatures are killed by @@ -80,6 +82,20 @@ void AddKillStatistics(int AttackerRace, int DefenderRace){ } } +int GetRaceByName(const char *RaceName){ + int Result = 0; + for(int Race = 1; Race < NARRAY(RaceData); Race += 1){ + if(stricmp(RaceName, RaceData[Race].Name) == 0){ + Result = Race; + break; + } + } + return Result; +} + +// ============================================================================= +// ============================================================================= + int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){ if(this->IsDead || this->Type == NPC){ return 0; diff --git a/src/creature.hh b/src/creature.hh index 79fee7c..96367ff 100644 --- a/src/creature.hh +++ b/src/creature.hh @@ -148,5 +148,6 @@ struct TCreature: TSkillBase { // this when we're wrapping up creature files. bool IsCreaturePlayer(uint32 CreatureID); void AddKillStatistics(int AttackerRace, int DefenderRace); +int GetRaceByName(const char *RaceName); #endif //TIBIA_CREATURE_HH_ diff --git a/src/magic.cc b/src/magic.cc index dc91695..6c890ea 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -927,7 +927,7 @@ void KillAllMonsters(TCreature *Actor, int Effect, int Radius){ } // TODO(fusion): This is similar to `ExecuteCircleSpell` which makes me think - // there is perhaps a common denominator? + // it got inlined and whatever `TImpact` this is got devirtualized. if(Radius >= NARRAY(Circle)){ Radius = NARRAY(Circle) - 1; } @@ -1076,8 +1076,7 @@ void MassCreateField(TCreature *Actor, Object Target, Missile(Actor->CrObject, Target, Animation); } - // TODO(fusion): Same as `KillAllMonsters`. Perhaps the `ExecuteCircleSpell` - // function is getting inlined and TFieldImpact devirtualized? + // TODO(fusion): Same as `KillAllMonsters`. if(Radius >= NARRAY(Circle)){ Radius = NARRAY(Circle) - 1; } @@ -1282,7 +1281,7 @@ void CleanupField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoint int TargetX, TargetY, TargetZ; GetObjectCoordinates(Target, &TargetX, &TargetY, &TargetZ); - int Distance = std::min( + int Distance = std::max( std::abs(Actor->posx - TargetX), std::abs(Actor->posy - TargetY)); if(Distance > 1 || Actor->posz != TargetZ){ @@ -1753,6 +1752,7 @@ void CreateFood(TCreature *Actor, int ManaPoints, int SoulPoints){ for(int i = 0; i < Count; i += 1){ uint8 Group, Number; switch(rand() % 7){ + default: // NOTE(fusion): To avoid compiler warnings. case 0: Group = 134; Number = 0; break; case 1: Group = 130; Number = 2; break; case 2: Group = 136; Number = 0; break; @@ -1760,7 +1760,6 @@ void CreateFood(TCreature *Actor, int ManaPoints, int SoulPoints){ case 4: Group = 131; Number = 1; break; case 5: Group = 131; Number = 9; break; case 6: Group = 134; Number = 1; break; - default: ASSERT(!"unreachable"); break; } CreateAtCreature(Actor->ID, GetNewObjectType(Group, Number), 1); } @@ -1793,6 +1792,581 @@ void CreateArrows(TCreature *Actor, int ManaPoints, int SoulPoints, int ArrowTyp GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); } +void SummonCreature(TCreature *Actor, int ManaPoints, int Race, bool God){ + if(Actor == NULL){ + error("SummonCreature: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(Race < 1 || Race >= NARRAY(RaceData)){ + error("SummonCreature: Ungültige Rassennummer %d übergeben.\n", Race); + throw ERROR; + } + + // TODO(fusion): These checks are weird. We should probably just split the + // function in two? + if(God){ + // TODO(fusion): What happened to checking if the creature is a player + // before using `CheckRight`? + if(!CheckRight(Actor->ID, CREATE_MONSTERS)){ + return; + } + }else if(Actor->Type == PLAYER){ + if(!CheckRight(Actor->ID, CREATE_MONSTERS) && GetRaceNoSummon(Race)){ + throw NOTACCESSIBLE; + } + + if(Actor->SummonedCreatures > 1){ + throw TOOMANYSLAVES; + } + } + + int SummonX = Actor->posx; + int SummonY = Actor->posy; + int SummonZ = Actor->posz; + if(!SearchSummonField(&SummonX, &SummonY, &SummonZ, 2)){ + throw NOROOM; + } + + uint32 Master = 0; + if(!God){ + int Delay = (WorldType == PVP_ENFORCED) ? 1000 : 2000; + ManaPoints += GetRaceSummonCost(Race); + CheckMana(Actor, ManaPoints, 0, Delay); + Master = Actor->ID; + } + + CreateMonster(Race, SummonX, SummonY, SummonZ, 0, Master, true); + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); +} + +void SummonCreature(TCreature *Actor, int Mana, const char *RaceName, bool God){ + if(Actor == NULL){ + error("SummonCreature: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(RaceName == NULL){ + error("SummonCreature: Ungültiger Rassenname übergeben.\n"); + throw ERROR; + } + + int Race = GetRaceByName(RaceName); + if(Race == 0){ + throw CREATURENOTEXISTING; + } + + SummonCreature(Actor, Mana, Race, God); +} + +void StartMonsterraid(TCreature *Actor, const char *RaidName){ + if(Actor == NULL){ + error("StartMonsterraid: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(RaidName == NULL){ + error("StartMonsterraid: Ungültiger Raidname übergeben.\n"); + throw ERROR; + } + + // TODO(fusion): What happened to checking if the creature is a player + // before using `CheckRight`? + if(!CheckRight(Actor->ID, CREATE_MONSTERS)){ + return; + } + + char RaidNameLower[512]; + strcpy(RaidNameLower, RaidName); + strLower(RaidNameLower); + + char FileName[4096]; + snprintf(FileName, sizeof(FileName), "%s/%s.evt", MONSTERPATH, RaidNameLower); + if(FileExists(FileName)){ + LoadMonsterRaid(FileName, RoundNr, NULL, NULL, NULL, NULL); + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); + }else{ + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_POFF); + } +} + +void RaiseDead(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints){ + if(Actor == NULL){ + error("RaiseDead: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(!Target.exists()){ + error("RaiseDead: Übergebenes Objekt existiert nicht.\n"); + throw ERROR; + } + + int TargetX, TargetY, TargetZ; + GetObjectCoordinates(Target, &TargetX, &TargetY, &TargetZ); + + // TODO(fusion): Could this be some `CheckRange` function inlined? + int Distance = std::max( + std::abs(Actor->posx - TargetX), + std::abs(Actor->posy - TargetY)); + if(Distance > 1 || Actor->posz != TargetZ){ + throw OUTOFRANGE; + } + + Object Obj = GetFirstObject(TargetX, TargetY, TargetZ); + while(Obj != NONE){ + ObjectType ObjType = Obj.getObjectType(); + // TODO(fusion): Same as in `CleanupField` except we're looking for a + // non human corpse to summon a skeleton from. + if(ObjType.getFlag(CORPSE) && ObjType.getAttribute(CORPSETYPE) == 1){ + break; + } + Obj = Obj.getNextObject(); + } + + if(Obj == NONE){ + throw NOTUSABLE; + } + + if(!SearchFreeField(&TargetX, &TargetY, &TargetZ, 1, 0, false) + || IsProtectionZone(TargetX, TargetY, TargetZ)){ + throw NOROOM; + } + + int Delay = (WorldType == PVP_ENFORCED) ? 1000 : 2000; + CheckMana(Actor, ManaPoints, SoulPoints, Delay); + Delete(Obj, -1); + + // NOTE(fusion): The race of a common skeleton is 33 but it should probably + // be a constant somewhere. + CreateMonster(33, TargetX, TargetY, TargetZ, 0, Actor->ID, true); +} + +void MassRaiseDead(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Radius){ + if(Actor == NULL){ + error("MassRaiseDead: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(!Target.exists()){ + error("MassRaiseDead: Übergebenes Objekt existiert nicht.\n"); + throw ERROR; + } + + // TODO(fusion): Unless `ThrowPossible` also does some max range check, this + // spell doesn't check range or floor. + int TargetX, TargetY, TargetZ; + GetObjectCoordinates(Target, &TargetX, &TargetY, &TargetZ); + if(!ThrowPossible(Actor->posx, Actor->posy, Actor->posz, TargetX, TargetY, TargetZ, 0)){ + throw CANNOTTHROW; + } + + int Delay = (WorldType == PVP_ENFORCED) ? 1000 : 2000; + CheckMana(Actor, ManaPoints, SoulPoints, Delay); + + if(Actor->posx != TargetX || Actor->posy != TargetY || Actor->posz != TargetZ){ + Missile(Actor->CrObject, Target, ANIMATION_ENERGY); + } + + // TODO(fusion): Same as `KillAllMonsters`. + if(Radius >= NARRAY(Circle)){ + Radius = NARRAY(Circle) - 1; + } + + for(int R = 0; R <= Radius; R += 1){ + int CirclePoints = Circle[R].Count; + for(int Point = 0; Point < CirclePoints; Point += 1){ + int FieldX = TargetX + Circle[R].x[Point]; + int FieldY = TargetY + Circle[R].y[Point]; + int FieldZ = TargetZ; + + if(!ThrowPossible(TargetX, TargetY, TargetZ, FieldX, FieldY, FieldZ, 0)){ + continue; + } + + GraphicalEffect(FieldX, FieldY, FieldZ, EFFECT_MAGIC_BLUE); + Object Obj = GetFirstObject(FieldX, FieldY, FieldZ); + while(Obj != NONE){ + Object Next = Obj.getNextObject(); + ObjectType ObjType = Obj.getObjectType(); + // NOTE(fusion): Same as `RaiseDead`. + if(ObjType.getFlag(CORPSE) && ObjType.getAttribute(CORPSETYPE) == 1){ + int SummonX = FieldX; + int SummonY = FieldY; + int SummonZ = FieldZ; + if(SearchFreeField(&SummonX, &SummonY, &SummonZ, 1, 0, false) + && !IsProtectionZone(SummonX, SummonY, SummonZ)){ + Delete(Obj, -1); + CreateMonster(33, SummonX, SummonY, SummonZ, 0, Actor->ID, false); + } + } + Obj = Next; + } + } + } +} + +void Heal(TCreature *Actor, int ManaPoints, int SoulPoints, int Amount){ + if(Actor == NULL){ + error("Heal: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + // TODO(fusion): This looks weird, then you peek at `IsPeaceful` and realize + // it actually determines whether the creature is a player's summon, then it + // gets weirder. + if(WorldType == NON_PVP && !Actor->IsPeaceful()){ + throw NOTACCESSIBLE; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + TSkill *HitPoints = Actor->Skills[SKILL_HITPOINTS]; + if(HitPoints == NULL){ + error("Heal: Skill HITPOINTS existiert nicht.\n"); + throw ERROR; + } + + if(HitPoints->Get() > 0){ + // TODO(fusion): This looks a lot like `THealingImpact::handleCreature` + // and I'm starting to think most of these spells use a single `TImpact` + // on the stack to execute their effects. We don't see any sign of them + // because they either get devirtualized by the optimizer or don't exist. + HitPoints->Change(Amount); + if(Actor->Skills[SKILL_GO_STRENGTH]->MDAct < 0){ + Actor->SetTimer(SKILL_GO_STRENGTH, 0, 0, 0, -1); + } + + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); + } +} + + +void MassHeal(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Amount, int Radius){ + if(Actor == NULL){ + error("MassHeal: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(!Target.exists()){ + error("MassHeal: Übergebenes Ziel existiert nicht.\n"); + throw ERROR; + } + + // TODO(fusion): Same as `MassRaiseDead`. + int TargetX, TargetY, TargetZ; + GetObjectCoordinates(Target, &TargetX, &TargetY, &TargetZ); + if(!ThrowPossible(Actor->posx, Actor->posy, Actor->posz, TargetX, TargetY, TargetZ, 0)){ + throw CANNOTTHROW; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + if(Actor->posx != TargetX || Actor->posy != TargetY || Actor->posz != TargetZ){ + Missile(Actor->CrObject, Target, ANIMATION_ENERGY); + } + + // TODO(fusion): Same as `KillAllMonsters`. + if(Radius >= NARRAY(Circle)){ + Radius = NARRAY(Circle) - 1; + } + + for(int R = 0; R <= Radius; R += 1){ + int CirclePoints = Circle[R].Count; + for(int Point = 0; Point < CirclePoints; Point += 1){ + int FieldX = TargetX + Circle[R].x[Point]; + int FieldY = TargetY + Circle[R].y[Point]; + int FieldZ = TargetZ; + + if(!ThrowPossible(TargetX, TargetY, TargetZ, FieldX, FieldY, FieldZ, 0)){ + continue; + } + + GraphicalEffect(FieldX, FieldY, FieldZ, EFFECT_MAGIC_BLUE); + Object Obj = GetFirstObject(FieldX, FieldY, FieldZ); + while(Obj != NONE){ + ObjectType ObjType = Obj.getObjectType(); + if(ObjType.isCreatureContainer()){ + TCreature *Victim = GetCreature(Obj); + if(Victim == NULL){ + error("MassHeal: Ungültige Kreatur.\n"); + }else if(WorldType != NON_PVP || Victim->IsPeaceful()){ + // TODO(fusion): Do we really want to throw here? If not + // having hitpoints is a problem, it should have been + // enforced ealier for all creatures. + TSkill *HitPoints = Victim->Skills[SKILL_HITPOINTS]; + if(HitPoints == NULL){ + error("MassHeal: Skill HITPOINTS existiert nicht.\n"); + throw ERROR; + } + + if(HitPoints->Get() > 0){ + HitPoints->Change(Amount); + if(Victim->Skills[SKILL_GO_STRENGTH]->MDAct < 0){ + Victim->SetTimer(SKILL_GO_STRENGTH, 0, 0, 0, -1); + } + } + } + } + Obj = Obj.getNextObject(); + } + } + } +} + +void HealFriend(TCreature *Actor, char *TargetName, int ManaPoints, int SoulPoints, int Amount){ + if(Actor == NULL){ + error("HealFriend: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(TargetName == NULL){ + error("HealFriend: Ungültigen Namen übergeben.\n"); + throw ERROR; + } + + TPlayer *Target; + switch(IdentifyPlayer(TargetName, false, true, &Target)){ + default: + case 0: break; // PLAYERFOUND ? + case -1: throw PLAYERNOTONLINE; + case -2: throw NAMEAMBIGUOUS; + } + + // TODO(fusion): These distances are related to the client's viewport. + if(Actor->posz != Target->posz + || std::abs(Actor->posx - Target->posx) > 7 + || std::abs(Actor->posy - Target->posy) > 5){ + throw OUTOFRANGE; + } + + TSkill *HitPoints = Target->Skills[SKILL_HITPOINTS]; + if(HitPoints == NULL){ + error("HealFriend: Skill HITPOINTS existiert nicht.\n"); + throw ERROR; + } + + if(HitPoints->Get() <= 0){ + throw PLAYERNOTONLINE; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + HitPoints->Change(Amount); + if(Target->Skills[SKILL_GO_STRENGTH]->MDAct < 0){ + Target->SetTimer(SKILL_GO_STRENGTH, 0, 0, 0, -1); + } + + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); + GraphicalEffect(Target->posx, Target->posy, Target->posz, EFFECT_MAGIC_GREEN); +} + +void RefreshMana(TCreature *Actor, int ManaPoints, int SoulPoints, int Amount){ + if(Actor == NULL){ + error("RefreshMana: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + TSkill *Mana = Actor->Skills[SKILL_MANA]; + if(Mana == NULL){ + error("RefreshMana: Skill MANA existiert nicht.\n"); + throw ERROR; + } + + Mana->Change(Amount); + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); +} + +void MagicGoStrength(TCreature *Actor, TCreature *Target, int ManaPoints, int SoulPoints, int Percent, int Duration){ + if(Actor == NULL){ + error("MagicGoStrength: Übergebene Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(Target == NULL){ + error("MagicGoStrength: Übergebene Ziel Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(Percent < 0){ + if(GetRaceNoParalyze(Target->Race)){ + throw NOTACCESSIBLE; + } + + if(WorldType == NON_PVP && Actor->IsPeaceful() && Target->IsPeaceful()){ + throw ATTACKNOTALLOWED; + } + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + TSkill *GoStrength = Target->Skills[SKILL_GO_STRENGTH]; + if(Percent < -100){ + GoStrength->SetMDAct(-GoStrength->Act - 20); + }else{ + GoStrength->SetMDAct((GoStrength->Act * Percent) / 100); + } + + Target->SetTimer(SKILL_GO_STRENGTH, Duration, 10, 10, -1); + + // TODO(fusion): We should probably check if the actor is different from the + // target before blocking the actor's logout or sending the first graphical + // effect. + if(Percent < 0){ + Actor->BlockLogout(60, Target->Type == PLAYER); + } + + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_GREEN); + GraphicalEffect(Target->posx, Target->posy, Target->posz, EFFECT_MAGIC_RED); +} + +void Shielding(TCreature *Actor, int ManaPoints, int SoulPoints, int Duration){ + if(Actor == NULL){ + error("Shielding: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + Actor->SetTimer(SKILL_MANASHIELD, 1, Duration, Duration, -1); + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); +} + +void NegatePoison(TCreature *Actor, TCreature *Target, int ManaPoints, int SoulPoints){ + if(Actor == NULL){ + error("NegatePoison: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(Target == NULL){ + error("NegatePoison: Zielkreatur existiert nicht.\n"); + throw ERROR; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + Target->SetTimer(SKILL_POISON, 0, 0, 0, -1); + GraphicalEffect(Target->posx, Target->posy, Target->posz, EFFECT_MAGIC_BLUE); +} + +void Enlight(TCreature *Actor, int ManaPoints, int SoulPoints, int Radius, int Duration){ + if(Actor == NULL){ + error("Enlight: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + if(Actor->Skills[SKILL_LIGHT]->TimerValue() <= Radius){ + Actor->SetTimer(SKILL_LIGHT, Radius, Duration / Radius, Duration / Radius, -1); + } + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); +} + +void Invisibility(TCreature *Actor, int ManaPoints, int SoulPoints, int Duration){ + if(Actor == NULL){ + error("Invisibility: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + Actor->Outfit = TOutfit{}; + Actor->SetTimer(SKILL_ILLUSION, 1, Duration, Duration, -1); + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); +} + +void CancelInvisibility(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Radius){ + if(Actor == NULL){ + error("CancelInvisibility: Ungültige Kreatur übergeben.\n"); + throw ERROR; + } + + if(!Target.exists()){ + error("CancelInvisibility: Übergebenes Ziel existiert nicht.\n"); + throw ERROR; + } + + int TargetX, TargetY, TargetZ; + GetObjectCoordinates(Target, &TargetX, &TargetY, &TargetZ); + if(!ThrowPossible(Actor->posx, Actor->posy, Actor->posz, TargetX, TargetY, TargetZ, 0)){ + throw CANNOTTHROW; + } + + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + + if(Actor->posx != TargetX || Actor->posy != TargetY || Actor->posz != TargetZ){ + Missile(Actor->CrObject, Target, ANIMATION_ENERGY); + } + + // TODO(fusion): Same as `KillAllMonsters`. + if(Radius >= NARRAY(Circle)){ + Radius = NARRAY(Circle) - 1; + } + + for(int R = 0; R <= Radius; R += 1){ + int CirclePoints = Circle[R].Count; + for(int Point = 0; Point < CirclePoints; Point += 1){ + int FieldX = TargetX + Circle[R].x[Point]; + int FieldY = TargetY + Circle[R].y[Point]; + int FieldZ = TargetZ; + + if(IsProtectionZone(FieldX, FieldY, FieldZ)){ + continue; + } + + if(!ThrowPossible(TargetX, TargetY, TargetZ, FieldX, FieldY, FieldZ, 0)){ + continue; + } + + int Effect = EFFECT_MAGIC_BLUE; + Object Obj = GetFirstObject(FieldX, FieldY, FieldZ); + while(Obj != NONE){ + ObjectType ObjType = Obj.getObjectType(); + if(ObjType.isCreatureContainer()){ + TCreature *Victim = GetCreature(Obj); + if(Victim == NULL){ + error("CancelInvisibility: Ungültige Kreatur.\n"); + }else if(Victim->Outfit.OutfitID == 0 && Victim->Outfit.ObjectType == 0 + && (WorldType != NON_PVP || !Victim->IsPeaceful())){ + Victim->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); + if(Victim->Skills[SKILL_ILLUSION]->Get() == 0){ + Victim->Outfit = Victim->OrgOutfit; + AnnounceChangedCreature(Victim->ID, 3); // CREATURE_OUTFIT_CHANGED ? + NotifyAllCreatures(Victim->CrObject, 2, NONE); // CREATURE_APPEAR ? + }else{ + Effect = EFFECT_BLOCK_HIT; + + // NOTE(fusion): If the victim still has an illusion effect up, it + // it must come from an item and it seems there is a change to destroy + // it on pvp enforced worlds. + // TODO(fusion): This is probably an inlined function. + if(WorldType == PVP_ENFORCED){ + for(int Position = 1; Position <= 10; Position += 1){ + Object Item = GetBodyObject(Victim->ID, Position); + if(Item == NONE){ + continue; + } + + ObjectType ItemType = Item.getObjectType(); + if(ItemType.getFlag(SKILLBOOST) + && (int)ItemType.getAttribute(BODYPOSITION) == Position + && (int)ItemType.getAttribute(SKILLNUMBER) == SKILL_ILLUSION){ + if(random(1, 5) == 1){ + Delete(Item, -1); + } + break; + } + } + } + } + } + } + Obj = Obj.getNextObject(); + } + + GraphicalEffect(FieldX, FieldY, FieldZ, Effect); + } + } +} + // Magic Init Functions // ============================================================================= static void InitCircles(void){ diff --git a/src/stubs.hh b/src/stubs.hh index 4157c61..3e69990 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -41,11 +41,15 @@ extern TConnection *GetFirstConnection(void); extern TConnection *GetNextConnection(void); extern TPlayer *GetPlayer(uint32 CreatureID); extern bool GetRaceNoParalyze(int Race); +extern bool GetRaceNoSummon(int Race); extern int GetRacePoison(int Race); +extern int GetRaceSummonCost(int Race); extern void GraphicalEffect(int x, int y, int z, int Type); extern void GraphicalEffect(Object Obj, int Type); extern int IdentifyPlayer(const char *Name, bool ExactMatch, bool IgnoreGamemasters, TPlayer **Player); extern void InitLog(const char *ProtocolName); +extern void LoadMonsterRaid(const char *FileName, int Start, + bool *Type, int *Date, int *Interval, int *Duration); extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3); extern void LogoutAllPlayers(void); extern void Missile(Object Start, Object Dest, int Type); -- cgit v1.2.3