From 3c8bcba6b540f5cb84572994d9ec2426dd348a93 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Thu, 5 Jun 2025 02:00:10 -0300 Subject: finish `magic.cc` --- TODO.md | 3 + reference/game.c | 1473 ------------------------------------------------------ src/crcombat.cc | 15 +- src/creature.cc | 4 +- src/enums.hh | 17 +- src/magic.cc | 1235 ++++++++++++++++++++++++++++++++++++++++++--- src/magic.hh | 126 ++++- src/main.cc | 4 +- src/stubs.hh | 6 +- 9 files changed, 1309 insertions(+), 1574 deletions(-) diff --git a/TODO.md b/TODO.md index 16235e8..76f56d2 100644 --- a/TODO.md +++ b/TODO.md @@ -8,6 +8,9 @@ ## 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. +## Exceptions +I didn't dive into how exceptions are handled, but it seems that the ones related to creature actions have the outter most endpoint at `TCreature::Execute`. There can also be some checkpoints in between to modify a `RESULT` value or handle a failure. + ## Estimate The decompiled file has ~115K lines of C. If we take ~15K lines to be rubbish, this can be round to ~100K. Considering a low estimate of 200 lines per day, the whole process could take up to 500 days which is quite a bit but not impossible. Now considering a high estimate of 1K lines per day, it could take 100 days which is also quite a bit. diff --git a/reference/game.c b/reference/game.c index 0a5f3af..9fbabaf 100644 --- a/reference/game.c +++ b/reference/game.c @@ -20852,1479 +20852,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority return; } -// MAGIC.CC - -void SpellFailed(ulong CreatureID) - -{ - TCreature *pTVar1; - Object local_1c [6]; - - pTVar1 = GetCreature(CreatureID); - if (pTVar1 == (TCreature *)0x0) { - error("SpellFailed: Kreatur existiert nicht.\n"); - } - else { - local_1c[0].ObjectID = (pTVar1->CrObject).ObjectID; - GraphicalEffect(local_1c,3); - } - return; -} - - - -void CharacterRightSpell(ulong CreatureID,int SpellNr,char (*SpellStr) [512]) - -{ - TCreature *cr_00; - undefined4 *puVar1; - TCreature *cr; - char (*pacVar2) [512]; - - // try { // try from 08088582 to 080887ce has its CatchHandler @ 080887d0 - cr_00 = GetCreature(CreatureID); - if (cr_00 == (TCreature *)0x0) { - error("CharacterRightSpell: Kreatur existiert nicht.\n"); - puVar1 = (undefined4 *)__cxa_allocate_exception(4); - *puVar1 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar1,&RESULT::typeinfo,0); - } - if (cr_00->Type != PLAYER) { - return; - } - switch(SpellNr) { - case 0x22: - pacVar2 = SpellStr + 3; - goto LAB_080885b7; - case 0x23: - pacVar2 = (char (*) [512])0x0; -LAB_080885b7: - CreateThing(cr_00,SpellStr[2],*pacVar2); - break; - case 0x25: - Teleport(cr_00,SpellStr[2]); - break; - case 0x28: - pacVar2 = (char (*) [512])0x0; - goto LAB_080885fd; - case 0x29: - ChangeData(cr_00,SpellStr[2]); - break; - case 0x2e: - pacVar2 = SpellStr + 4; -LAB_080885fd: - CreateKnowledge(cr_00,SpellStr[3],*pacVar2); - break; - case 0x2f: - TeleportToCreature(cr_00,SpellStr[3]); - break; - case 0x34: - TeleportPlayerToMe(cr_00,SpellStr[3]); - break; - case 0x35: - SummonCreature(cr_00,0,SpellStr[3],true); - break; - case 0x3a: - GetPosition(cr_00); - break; - case 0x3f: - CreateMoney(cr_00,SpellStr[3]); - break; - case 0x40: - ChangeProfession(cr_00,SpellStr[3]); - break; - case 0x47: - EditGuests(cr_00); - break; - case 0x48: - EditSubowners(cr_00); - break; - case 0x49: - KickGuest(cr_00,SpellStr[3]); - break; - case 0x4a: - EditNameDoor(cr_00); - break; - case 0x60: - GetQuestValue(cr_00,SpellStr[3]); - break; - case 0x61: - SetQuestValue(cr_00,SpellStr[3],SpellStr[4]); - break; - case 0x62: - CleanupField(cr_00); - break; - case 99: - MagicClimbing(cr_00,SpellStr[3]); - break; - case 100: - ClearQuestValues(cr_00); - break; - case 0x65: - KillAllMonsters(cr_00,0x12,2); - break; - case 0x66: - StartMonsterraid(cr_00,SpellStr[4]); - } - return; -} - - - -void AccountRightSpell(ulong CreatureID,int SpellNr,char (*SpellStr) [512]) - -{ - TCreature *cr_00; - int iVar1; - undefined4 *puVar2; - TCreature *cr; - - // try { // try from 08088805 to 080889de has its CatchHandler @ 080889e0 - cr_00 = GetCreature(CreatureID); - if (cr_00 != (TCreature *)0x0) { - if (cr_00->Type == PLAYER) { - switch(SpellNr) { - case 0x39: - iVar1 = __strtol_internal(SpellStr + 4,0,10,0); - BanishAccount(cr_00,SpellStr[3],iVar1,SpellStr[5]); - break; - case 0x3c: - HomeTeleport(cr_00,SpellStr[2]); - break; - case 0x3d: - DeleteAccount(cr_00,SpellStr[4],SpellStr[5]); - break; - case 0x3e: - SetNameRule(cr_00,SpellStr[2]); - break; - case 0x41: - Notation(cr_00,SpellStr[2],SpellStr[3]); - break; - case 0x42: - NameLock(cr_00,SpellStr[3]); - break; - case 0x43: - KickPlayer(cr_00,SpellStr[2]); - break; - case 0x44: - DeleteCharacter(cr_00,SpellStr[4],SpellStr[5]); - break; - case 0x45: - IPBanishment(cr_00,SpellStr[3],SpellStr[4]); - break; - case 0x46: - iVar1 = __strtol_internal(SpellStr + 4,0,10,0); - BanishCharacter(cr_00,SpellStr[3],iVar1,SpellStr[5]); - } - } - return; - } - error("AccountRightSpell: Kreatur existiert nicht.\n"); - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); -} - - - -void CastSpell(ulong CreatureID,int SpellNr,char (*SpellStr) [512]) - -{ - bool bVar1; - TCreature *cr_00; - undefined4 *puVar2; - byte bVar3; - TCreature *cr; - int iVar4; - int iVar5; - int Damage; - - // try { // try from 08088a18 to 080894d1 has its CatchHandler @ 080894e6 - cr_00 = GetCreature(CreatureID); - if (cr_00 == (TCreature *)0x0) { - error("CastSpell: Kreatur existiert nicht.\n",CreatureID); - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0xffffffff; -LAB_0808947c: - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); - } - CheckSpellbook(cr_00,SpellNr); - CheckAccount(cr_00,SpellNr); - CheckLevel(cr_00,SpellNr); - CheckRing(cr_00,SpellNr); - if (ServerMilliseconds < cr_00->EarliestSpellTime) { - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0x31; - goto LAB_0808947c; - } - if (SpellNr - 1U < 0xff) { - bVar3 = (byte)SpellList[SpellNr].Flags & 1; - } - else { - error(&DAT_080fa6c0,SpellNr); - bVar3 = 0; - } - if (((bVar3 != 0) && (cr_00->Type == PLAYER)) && - (bVar1 = CheckRight(cr_00->ID,ATTACK_EVERYWHERE), !bVar1)) { - iVar4 = cr_00->posz; - iVar5 = cr_00->posy; - bVar1 = IsProtectionZone(); - if (bVar1) { - puVar2 = (undefined4 *)__cxa_allocate_exception(4,iVar5,iVar4); - *puVar2 = 0x2f; - goto LAB_0808947c; - } - } - switch(SpellNr) { - case 1: - iVar5 = 10; - iVar4 = 0x14; - goto LAB_08088ad0; - case 2: - iVar5 = 0x14; - iVar4 = 0x28; - goto LAB_08088ad0; - case 3: - iVar5 = 0x32; - iVar4 = 0xfa; -LAB_08088ad0: - ComputeDamage(cr_00,SpellNr,iVar4,iVar5); - Heal(); - break; - case 6: - goto LAB_08088ba1; - case 9: - SummonCreature(cr_00,SpellList[SpellNr].Mana,SpellStr[3],false); - break; - case 10: - goto LAB_08088c10; - case 0xb: - goto LAB_08088c10; - case 0xd: - ComputeDamage(cr_00,SpellNr,0x96,0x32); - goto LAB_08088c8f; - case 0x13: - ComputeDamage(cr_00,SpellNr,0x1e,10); - goto LAB_08088c8f; - case 0x14: - FindPerson(); - break; - case 0x16: - ComputeDamage(cr_00,SpellNr,0x3c,0x14); - goto LAB_08088c8f; - case 0x17: - ComputeDamage(cr_00,SpellNr,0x78,0x50); - goto LAB_08088c8f; - case 0x18: - ComputeDamage(cr_00,SpellNr,0xfa,0x32); - goto LAB_08088e31; - case 0x1d: - NegatePoison(); - break; - case 0x26: - CreatureIllusion(); - break; - case 0x27: -LAB_08088ba1: - MagicGoStrength(); - break; - case 0x2a: - CreateFood(); - break; - case 0x2c: - Shielding(); - break; - case 0x2d: - Invisibility(); - break; - case 0x30: - goto LAB_08088f60; - case 0x31: - goto LAB_08088f60; - case 0x33: - goto LAB_08088f60; - case 0x38: - ComputeDamage(cr_00,SpellNr,200,0x32); - goto LAB_08088e31; - case 0x4b: -LAB_08088c10: - Enlight(); - break; - case 0x4c: - MagicRope(); - break; - case 0x4f: - goto LAB_08088f60; - case 0x50: - ComputeDamage(cr_00,SpellNr,0x50,0x14); - TSkill::Get((cr_00->super_TSkillBase).Skills[0]); - TSkill::Get((cr_00->super_TSkillBase).Skills[0]); -LAB_08088e31: - MassCombat(); - break; - case 0x51: - MagicClimbing(); - break; - case 0x52: - ComputeDamage(cr_00,SpellNr,200,0x28); - MassHeal(); - break; - case 0x54: - ComputeDamage(cr_00,SpellNr,0x78,0x28); - HealFriend(); - break; - case 0x55: - MassRaiseDead(); - break; - case 0x57: - ComputeDamage(cr_00,SpellNr,0x2d,10); - goto LAB_08088c8f; - case 0x58: - ComputeDamage(cr_00,SpellNr,0x2d,10); - goto LAB_08088c8f; - case 0x59: - ComputeDamage(cr_00,SpellNr,0x2d,10); -LAB_08088c8f: - AngleCombat(); - break; - case 0x5a: - CancelInvisibility(); - break; - case 0x5c: - GetNewObjectType(0xd4,'Z'); - GetNewObjectType(0xc4,'Z'); - EnchantObject(); - break; - case 0x5d: - Challenge(); - break; - case 0x5e: - CreateField(); - break; - case 0x5f: -LAB_08088f60: - CreateArrows(); - } - if (SpellNr - 1U < 0xff) { - bVar3 = (byte)SpellList[SpellNr].Flags & 1; - } - else { - error(&DAT_080fa6c0,SpellNr); - bVar3 = 0; - } - if (bVar3 != 0) { - TCreature::BlockLogout(cr_00,0x3c,false); - } - return; -} - - - -void RuneSpell(ulong CreatureID,int SpellNr) - -{ - bool bVar1; - bool bVar2; - TCreature *cr_00; - undefined4 *puVar3; - ObjectType *pOVar4; - ObjectType *pOVar5; - bool executed; - TCreature *cr; - ObjectType local_5c [4]; - Object local_4c [4]; - Object local_3c; - Object Obj2; - Object local_2c; - Object Obj1; - - // try { // try from 0808951b to 0808966f has its CatchHandler @ 08089836 - cr_00 = GetCreature(CreatureID); - if (cr_00 == (TCreature *)0x0) { - error("RuneSpell: Kreatur existiert nicht.\n"); - } - else { - if (SpellList[SpellNr].RuneGr != '\0') { - GetBodyObject((ulong)&local_2c,CreatureID); - GetBodyObject((ulong)&local_3c,CreatureID); - CheckSpellbook(cr_00,SpellNr); - CheckAccount(cr_00,SpellNr); - CheckLevel(cr_00,SpellNr); - pOVar4 = (ObjectType *)SpellNr; - CheckRing(cr_00,SpellNr); - if (ServerMilliseconds < cr_00->EarliestSpellTime) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x31; - } - else { - bVar1 = false; - bVar2 = Object::exists(&local_2c); - if (bVar2) { - // try { // try from 0808970d to 080897f2 has its CatchHandler @ 08089836 - Object::getObjectType(local_4c); - pOVar5 = local_5c; - pOVar4 = (ObjectType *)0x28; - GetSpecialObject((SPECIALMEANING)pOVar5); - if (local_4c[0].ObjectID == local_5c[0].TypeID) { - CheckMana(cr_00,SpellList[SpellNr].Mana,SpellList[SpellNr].SoulPoints,1000); - GetNewObjectType((uchar)pOVar5,SpellList[SpellNr].RuneGr); - cr = (TCreature *)local_2c.ObjectID; - Change((Object *)&cr,pOVar5,SpellList[SpellNr].Amount); - bVar1 = true; - pOVar4 = pOVar5; - } - } - bVar2 = Object::exists(&local_3c); - if (bVar2) { - Object::getObjectType((Object *)&cr); - pOVar5 = local_5c; - pOVar4 = (ObjectType *)0x28; - GetSpecialObject((SPECIALMEANING)pOVar5); - if (cr == (TCreature *)local_5c[0].TypeID) { - // try { // try from 080896ac to 080896f0 has its CatchHandler @ 080897f5 - CheckMana(cr_00,SpellList[SpellNr].Mana,SpellList[SpellNr].SoulPoints,1000); - GetNewObjectType((uchar)pOVar5,SpellList[SpellNr].RuneGr); - local_4c[0].ObjectID = local_3c.ObjectID; - Change(local_4c,pOVar5,SpellList[SpellNr].Amount); - bVar1 = true; - pOVar4 = pOVar5; - } - } - if (bVar1) { - cr = (TCreature *)(cr_00->CrObject).ObjectID; - GraphicalEffect((Object *)&cr,0xe); - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4,pOVar4); - *puVar3 = 0x22; - } - goto LAB_08089630; - } - error("RuneSpell: Spell %d ist Runenspruch, hat aber keine Rune.\n",SpellNr); - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; -LAB_08089630: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -int TypeOfSpell(char *Text) - -{ - int iVar1; - char (*s2) [6]; - int i; - int iVar2; - - if (Text != (char *)0x0) { - iVar2 = 1; - s2 = SpellSyllable; - do { - s2 = s2 + 1; - iVar1 = stricmp(Text,*s2,2); - if (iVar1 == 0) { - return iVar2; - } - iVar2 = iVar2 + 1; - } while (iVar2 < 6); - } - return 0; -} - - - -// WARNING: Variable defined which should be unmapped: Spell - -int FindSpell(uchar *Syllable) - -{ - uchar uVar1; - int j; - int iVar2; - int Params; - int iVar3; - int iVar4; - int local_1c; - int i; - int MinParams; - int Spell; - - local_1c = 1; - MinParams = 0; - i = 100; - iVar4 = 0x24; - do { - iVar3 = 0; - iVar2 = 0; - do { - uVar1 = SpellList[0].Syllable[iVar4 + iVar2]; - if (uVar1 == '\x06') { - if (Syllable[iVar2] == '\0') break; - } - else if (uVar1 != Syllable[iVar2]) break; - if (uVar1 == '\x06') { - iVar3 = iVar3 + 1; - } - if (uVar1 == '\0') { - if (iVar3 < i) { - MinParams = local_1c; - i = iVar3; - } - break; - } - iVar2 = iVar2 + 1; - } while (iVar2 < 10); - local_1c = local_1c + 1; - iVar4 = iVar4 + 0x24; - if (0xff < local_1c) { - return MinParams; - } - } while( true ); -} - - - -int FindSpell(Object *Obj) - -{ - bool bVar1; - int iVar2; - int i; - int iVar3; - char *Text; - ulong local_3c [4]; - Object local_2c [7]; - - bVar1 = Object::exists(Obj); - if (bVar1) { - iVar3 = 1; - iVar2 = 0x24; - Object::getObjectType(local_2c); - bVar1 = ObjectType::getFlag((ObjectType *)local_2c,RUNE); - if (bVar1) { - do { - Object::getObjectType(local_2c); - GetNewObjectType((uchar)local_3c,SpellList[0].Syllable[iVar2 + 10]); - if (local_2c[0].ObjectID == local_3c[0]) { - return iVar3; - } - iVar3 = iVar3 + 1; - iVar2 = iVar2 + 0x24; - } while (iVar3 < 0x100); - return 0; - } - Text = &DAT_080fd940; - } - else { - Text = &DAT_080fd900; - } - error(Text); - return 0; -} - - - -void GetSpellString(int SpellNr,char *Text) - -{ - size_t sVar1; - int i; - int iVar2; - - *Text = '\0'; - if (0xfe < SpellNr - 1U) { - error(&DAT_080fd980); - return; - } - iVar2 = 0; - do { - if (SpellSyllable[SpellList[SpellNr].Syllable[iVar2]][0] == '\0') break; - strcat(Text,SpellSyllable[SpellList[SpellNr].Syllable[iVar2]]); - if (0 < iVar2) { - strcat(Text," "); - } - iVar2 = iVar2 + 1; - } while (iVar2 < 10); - if (*Text != '\0') { - sVar1 = strlen(Text); - Text[sVar1 - 1] = '\0'; - } - return; -} - - - -void GetMagicItemDescription(Object *Obj,char *SpellString,int *MagicLevel) - -{ - bool bVar1; - int SpellNr_00; - int SpellNr; - char *Text; - Object local_2c [7]; - - *MagicLevel = 0; - *SpellString = '\0'; - bVar1 = Object::exists(Obj); - if (bVar1) { - Object::getObjectType(local_2c); - bVar1 = ObjectType::getFlag((ObjectType *)local_2c,RUNE); - if (bVar1) { - local_2c[0] = (Object)Obj->ObjectID; - SpellNr_00 = FindSpell(local_2c); - if (SpellNr_00 == 0) { - Object::getObjectType(local_2c); - error("GetMagicItemDescription: Objekt %d hat keinen Zauberspruch.\n", - local_2c[0].ObjectID); - return; - } - GetSpellString(SpellNr_00,SpellString); - *MagicLevel = (uint)SpellList[SpellNr_00].RuneLevel; - return; - } - Text = &DAT_080fda00; - } - else { - Text = &DAT_080fd9c0; - } - error(Text); - return; -} - - - -void GetSpellbook(ulong CharacterID,char *Buffer) - -{ - bool bVar1; - bool bVar2; - TPlayer *this; - TPlayer **ppTVar3; - TPlayer **ppTVar4; - uint uVar5; - uint uVar6; - int i; - int SpellNumber; - int iVar7; - int Level; - uint uVar8; - char *Text; - bool first; - TPlayer *pl; - char Help [100]; - - this = GetPlayer(CharacterID); - if (this == (TPlayer *)0x0) { - Text = "GetSpellbook: Spieler existiert nicht.\n"; - } - else { - if (Buffer != (char *)0x0) { - uVar8 = 1; - *Buffer = '\0'; - do { - bVar1 = true; - SpellNumber = 1; - iVar7 = 0x24; - do { - if ((*(ushort *)(SpellList[0].Syllable + iVar7 + 0x10) == uVar8) && - (bVar2 = TPlayer::SpellKnown(this,SpellNumber), bVar2)) { - if (bVar1) { - sprintf((char *)&pl,"Spells for Level %d\n",uVar8); - strcat(Buffer,(char *)&pl); - bVar1 = false; - } - GetSpellString(SpellNumber,(char *)&pl); - ppTVar4 = &pl; - do { - ppTVar3 = ppTVar4; - uVar5 = (uint)((int)(*ppTVar3)[-0x127c].QuestValues + 0x68fU) & - ~(uint)*ppTVar3; - uVar6 = uVar5 & 0x80808080; - ppTVar4 = ppTVar3 + 1; - } while (uVar6 == 0); - bVar2 = (uVar5 & 0x8080) == 0; - if (bVar2) { - uVar6 = uVar6 >> 0x10; - } - if (bVar2) { - ppTVar4 = (TPlayer **)((int)ppTVar3 + 6); - } - if ((int)ppTVar4 + ((-3 - (uint)CARRY1((byte)uVar6,(byte)uVar6)) - (int)&pl) - < 2) { - error("GetSpellbook: Zauberspruch %d hat keine Zauberformel.\n", - SpellNumber); - } - else { - strcat(Buffer," "); - strcat(Buffer,(char *)&pl); - if (**(char **)(SpellList[0].Syllable + iVar7 + 0xc) != '\0') { - strcat(Buffer," - "); - strcat(Buffer,*(char **)(SpellList[0].Syllable + iVar7 + 0xc)); - if (SpellNumber == 0x50) { - builtin_strncpy(Help,"*Level",7); - pl = (TPlayer *)0x34203a20; - } - else if ((SpellNumber < 0x51) && - ((SpellNumber == 9 || (SpellNumber == 0xc)))) { - Help._2_2_ = SUB42(Help._0_4_,2) & 0xff00; - pl = (TPlayer *)0x76203a20; - Help[0] = 'a'; - Help[1] = 'r'; - } - else { - sprintf((char *)&pl," : %d", - *(undefined4 *)(SpellList[0].Syllable + iVar7 + 0x18)); - } - strcat(Buffer,(char *)&pl); - } - strcat(Buffer,"\n"); - } - } - SpellNumber = SpellNumber + 1; - iVar7 = iVar7 + 0x24; - } while (SpellNumber < 0x100); - if (!bVar1) { - strcat(Buffer,"\n"); - } - uVar8 = uVar8 + 1; - } while ((int)uVar8 < 0x3d); - return; - } - Text = &DAT_080fdac0; - } - error(Text); - return; -} - - - -int GetSpellLevel(int SpellNr) - -{ - uint uVar1; - - if (SpellNr - 1U < 0xff) { - uVar1 = (uint)SpellList[SpellNr].Level; - } - else { - error(&DAT_080fdb40,SpellNr); - uVar1 = 1; - } - return uVar1; -} - - - -int CheckForSpell(ulong CreatureID,char *Text) - -{ - int *piVar1; - bool bVar2; - int SpellNr; - int iVar3; - int iVar4; - size_t sVar5; - int iVar6; - int Next; - char (*pacVar7) [6]; - int syl; - uint in_stack_ffffe8a0; - uint uVar8; - int local_1750; - char local_174c [4]; - char SpellStr [11] [512]; - uchar Syllable [11]; - char *local_11c [4]; - undefined4 local_10c; - istringstream IS; - undefined4 local_104 [20]; - int local_b4; - undefined4 local_b0; - ios_base aiStack_ac [16]; - byte local_9c; - - local_1750 = 0; - if (Text != (char *)0x0) { - local_1750 = 1; - pacVar7 = SpellSyllable; - do { - pacVar7 = pacVar7 + 1; - iVar3 = stricmp(Text,*pacVar7,2); - if (iVar3 == 0) goto LAB_08089ed2; - local_1750 = local_1750 + 1; - } while (local_1750 < 6); - local_1750 = 0; - } -LAB_08089ed2: - iVar3 = 0; - if (local_1750 != 0) { - if (Text != (char *)0x0) { - strlen(Text); - } - uVar8 = in_stack_ffffe8a0 & 0xffffff00; - // try { // try from 08089f11 to 08089f15 has its CatchHandler @ 0808a436 - local_11c[0] = basic_string<>::_S_construct<>(); - // try { // try from 08089f2d to 08089f31 has its CatchHandler @ 0808a413 - std::ios_base::ios_base(aiStack_ac); - local_b0 = 0x8128950; - // try { // try from 08089f56 to 08089f5a has its CatchHandler @ 0808a40f - std::istream::istream((istream *)&local_10c,(streambuf *)(std::istringstream::VTT + 4)); - local_10c = 0x8128964; - local_b0 = 0x8128978; - // try { // try from 08089f89 to 08089f8d has its CatchHandler @ 0808a40b - std::stringbuf::stringbuf((stringbuf *)local_104,local_11c,8,uVar8); - // try { // try from 08089f95 to 08089f99 has its CatchHandler @ 0808a3a5 - std::ios::init((EVP_PKEY_CTX *)&local_b0); - LOCK(); - piVar1 = (int *)(local_11c[0] + -4); - iVar3 = *piVar1; - *piVar1 = *piVar1 + -1; - UNLOCK(); - if (iVar3 < 1) { - // try { // try from 0808a380 to 0808a384 has its CatchHandler @ 0808a429 - std::__default_alloc_template::deallocate - (local_11c[0] + -0xc,*(int *)(local_11c[0] + -8) + 0xd); - } - SpellStr[10][0x1fc] = (char)local_1750; - strcpy(local_174c,SpellSyllable[local_1750]); - // try { // try from 08089ff2 to 0808a0d9 has its CatchHandler @ 0808a5e0 - std::istream::get(); - std::istream::get(); - SpellStr[10][0x1fd] = '\0'; - SpellStr[0][0x1fc] = '\0'; - iVar3 = 1; - if ((local_9c & 2) == 0) { - while( true ) { - while( true ) { - iVar4 = std::istream::peek(); - bVar2 = isSpace(iVar4); - if (!bVar2) break; - std::istream::get(); - } - if (iVar4 == 0x22) { - // try { // try from 0808a32b to 0808a36d has its CatchHandler @ 0808a5e0 - std::istream::get(); - std::istream::get((char *)&local_10c,(int)(SpellStr[iVar3 + -1] + 0x1fc),-2); - std::istream::get(); - } - else { - std::istream::get((char *)&local_10c,(int)(SpellStr[iVar3 + -1] + 0x1fc),'d'); - sVar5 = strlen(SpellStr[iVar3 + -1] + 0x1fc); - if (sVar5 == 99) { - local_10c = 0x8128964; - local_b0 = 0x8128978; - local_104[0] = 0x8128a08; - // try { // try from 0808a31b to 0808a31f has its CatchHandler @ 0808a43a - std::string::_Rep::_M_dispose((allocator *)(local_b4 + -0xc)); - goto LAB_0808a2a8; - } - } - if (SpellStr[iVar3 + -1][0x1fc] == '\0') break; - iVar4 = 1; - pacVar7 = SpellSyllable; - do { - pacVar7 = pacVar7 + 1; - iVar6 = stricmp(SpellStr[iVar3 + -1] + 0x1fc,*pacVar7,-1); - if (iVar6 == 0) { - SpellStr[10][iVar3 + 0x1fc] = (char)iVar4; - } - iVar4 = iVar4 + 1; - } while (iVar4 < 0x34); - if (SpellStr[10][iVar3 + 0x1fc] == '\0') { - SpellStr[10][iVar3 + 0x1fc] = '\x06'; - } - SpellStr[10][iVar3 + 0x1fd] = '\0'; - SpellStr[iVar3][0x1fc] = '\0'; - if ((9 < iVar3 + 1) || (iVar3 = iVar3 + 1, (local_9c & 2) != 0)) break; - } - } - iVar3 = FindSpell((uchar *)(SpellStr[10] + 0x1fc)); - if (iVar3 == 0) { - local_10c = 0x8128964; - local_b0 = 0x8128978; - local_104[0] = 0x8128a08; - // try { // try from 0808a2a3 to 0808a2a7 has its CatchHandler @ 0808a440 - std::string::_Rep::_M_dispose((allocator *)(local_b4 + -0xc)); -LAB_0808a2a8: - std::streambuf::~streambuf((streambuf *)local_104); - std::istream::~istream((istream *)&local_10c); - std::ios::~ios((ios *)&local_b0); - iVar3 = 0; - } - else { - switch(local_1750) { - default: - // try { // try from 0808a20c to 0808a260 has its CatchHandler @ 0808a442 - error("CheckForSpell: Spruchklasse %d existiert nicht.\n",local_1750); - break; - case 1: - // try { // try from 0808a16f to 0808a173 has its CatchHandler @ 0808a442 - CharacterRightSpell(CreatureID,iVar3,(char (*) [512])local_174c); - break; - case 2: - RuneSpell(CreatureID,iVar3); - break; - case 3: - case 4: - CastSpell(CreatureID,iVar3,(char (*) [512])local_174c); - break; - case 5: - AccountRightSpell(CreatureID,iVar3,(char (*) [512])local_174c); - } - local_10c = 0x8128964; - local_b0 = 0x8128978; - local_104[0] = 0x8128a08; - // try { // try from 0808a1b1 to 0808a1b5 has its CatchHandler @ 0808a436 - std::string::_Rep::_M_dispose((allocator *)(local_b4 + -0xc)); - std::streambuf::~streambuf((streambuf *)local_104); - std::istream::~istream((istream *)&local_10c); - std::ios::~ios((ios *)&local_b0); - iVar3 = local_1750; - } - } - return iVar3; -} - - - -void DeleteRune(Object *Obj) - -{ - bool bVar1; - ulong uVar2; - undefined4 *puVar3; - Object local_1c [5]; - - // try { // try from 0808a5ff to 0808a6a0 has its CatchHandler @ 0808a6a1 - bVar1 = Object::exists(Obj); - if (bVar1) { - uVar2 = Object::getAttribute(Obj,CHARGES); - if (uVar2 < 2) { - local_1c[0] = (Object)Obj->ObjectID; - Delete(local_1c,-1); - } - else { - uVar2 = Object::getAttribute(Obj,CHARGES); - local_1c[0] = (Object)Obj->ObjectID; - Change(local_1c,CHARGES,uVar2 - 1); - } - return; - } - error(&DAT_080fdbc0); - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -void UseMagicItem(ulong CreatureID,Object *Obj,Object *Dest) - -{ - bool bVar1; - byte bVar2; - uchar uVar3; - TCreature *cr; - TPlayer *this; - int *SpellNr_00; - ulong uVar4; - int Damage_00; - undefined4 *puVar5; - int Damage; - TPlayer *pl; - char *pcVar6; - int *piVar7; - int *piVar8; - TCreature *z; - int EffectNr; - int AnimationNr; - ushort DmgType; - TCreature *local_60; - TCreature *dest; - int SpellNr; - int dx; - int dy; - int dz; - Object local_3c [4]; - Object local_2c; - Object Help; - - // try { // try from 0808a6de to 0808a8d4 has its CatchHandler @ 0808b318 - this = GetPlayer(CreatureID); - if (this == (TPlayer *)0x0) { - pcVar6 = "UseMagicItem: Kreatur existiert nicht.\n"; -LAB_0808b285: - error(pcVar6); -LAB_0808b22e: - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0xffffffff; -LAB_0808b0ba: - // WARNING: Subroutine does not return - __cxa_throw(puVar5,&RESULT::typeinfo,0); - } - bVar1 = Object::exists(Obj); - if (!bVar1) { - pcVar6 = &DAT_080fdcc0; - goto LAB_0808b285; - } - bVar1 = Object::exists(Dest); - if (!bVar1) { - Object::getObjectType(&local_2c); - pcVar6 = &DAT_080fdc80; -LAB_0808b229: - error(pcVar6,local_2c.ObjectID); - goto LAB_0808b22e; - } - bVar1 = CheckRight((this->super_TCreature).ID,NO_RUNES); - if (bVar1) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x1d; - goto LAB_0808b0ba; - } - local_2c = (Object)Obj->ObjectID; - SpellNr_00 = (int *)FindSpell(&local_2c); - if (SpellNr_00 == (int *)0x0) { - Object::getObjectType(&local_2c); - pcVar6 = &DAT_080fdc40; - goto LAB_0808b229; - } - Object::getObjectType(&local_2c); - if (local_2c.ObjectID == 99) { - local_2c = (Object)Dest->ObjectID; - local_60 = GetCreature(&local_2c); - } - else { - local_60 = (TCreature *)0x0; - } - Object::getContainer(local_3c); - GetFirstContainerObject(&local_2c); - if (local_2c.ObjectID != NONE.ObjectID) { - do { - dz = NONE.ObjectID; - local_3c[0].ObjectID = NONE.ObjectID; - Object::getObjectType((Object *)&dz); - if (dz == 99) { - if ((int)SpellNr_00 - 1U < 0xff) { - bVar2 = (byte)SpellList[(int)SpellNr_00].Flags & 1; - } - else { - error(&DAT_080fa6c0,SpellNr_00); - bVar2 = 0; - } - if ((bVar2 != 0) && - ((local_60 == (TCreature *)0x0 || - (uVar4 = Object::getCreatureID(&local_2c), uVar4 != (this->super_TCreature).ID)) - )) { - local_3c[0].ObjectID = local_2c.ObjectID; - Dest->ObjectID = (ulong)local_2c; - dz = local_2c.ObjectID; - local_60 = GetCreature((Object *)&dz); - } - if ((int)SpellNr_00 - 1U < 0xff) { - bVar2 = (byte)SpellList[(int)SpellNr_00].Flags & 1; - } - else { - error(&DAT_080fa6c0,SpellNr_00); - bVar2 = 0; - } - if ((bVar2 == 0) && - ((local_60 == (TCreature *)0x0 || - (uVar4 = Object::getCreatureID(&local_2c), uVar4 == (this->super_TCreature).ID)) - )) { - local_3c[0].ObjectID = local_2c.ObjectID; - Dest->ObjectID = (ulong)local_2c; - dz = local_2c.ObjectID; - local_60 = GetCreature((Object *)&dz); - } - } - Object::getNextObject(local_3c); - local_2c.ObjectID = local_3c[0].ObjectID; - } while (local_3c[0].ObjectID != NONE.ObjectID); - } - dz = NONE.ObjectID; - local_3c[0].ObjectID = NONE.ObjectID; - CheckRuneLevel(&this->super_TCreature,(int)SpellNr_00); - dz = Dest->ObjectID; - z = (TCreature *)&dy; - piVar8 = &dx; - piVar7 = &SpellNr; - GetObjectCoordinates((Object *)&dz,piVar7,piVar8,(int *)z); - if (ServerMilliseconds < (this->super_TCreature).EarliestSpellTime) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x31; - goto LAB_0808b0ba; - } - if ((int)SpellNr_00 - 1U < 0xff) { - bVar2 = (byte)SpellList[(int)SpellNr_00].Flags & 1; - } - else { - piVar7 = SpellNr_00; - error(&DAT_080fa6c0,SpellNr_00); - bVar2 = 0; - } - if (bVar2 != 0) { - piVar7 = (int *)0x43; - bVar1 = CheckRight((this->super_TCreature).ID,ATTACK_EVERYWHERE); - if (!bVar1) { - piVar8 = (int *)(this->super_TCreature).posz; - piVar7 = (int *)(this->super_TCreature).posy; - bVar1 = IsProtectionZone(); - if ((bVar1) || - (bVar1 = IsProtectionZone(), piVar7 = (int *)dx, piVar8 = (int *)dy, bVar1)) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4,piVar7,piVar8); - *puVar5 = 0x2f; - goto LAB_0808b0ba; - } - } - } - if ((int)SpellNr_00 - 1U < 0xff) { - bVar2 = (byte)SpellList[(int)SpellNr_00].Flags & 1; - } - else { - piVar7 = SpellNr_00; - error(&DAT_080fa6c0,SpellNr_00); - bVar2 = 0; - } - if (bVar2 != 0) { - // try { // try from 0808b080 to 0808b289 has its CatchHandler @ 0808b318 - Object::getContainer((Object *)&dz); - piVar7 = &dz; - Object::getObjectType(local_3c); - if (local_3c[0].ObjectID != 0) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4,piVar7); - *puVar5 = 4; - goto LAB_0808b0ba; - } - } - switch(SpellNr_00) { - case (int *)0x4: - if (local_60 != (TCreature *)0x0) { - // try { // try from 0808a978 to 0808b044 has its CatchHandler @ 0808b295 - z = (TCreature *)ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x46,0x1e); -LAB_0808a997: - piVar8 = (int *)0x0; - Heal(); - break; - } - goto LAB_0808aa1b; - case (int *)0x5: - if (local_60 != (TCreature *)0x0) { - z = (TCreature *)ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0xfa,0); - goto LAB_0808a997; - } - goto LAB_0808aa1b; - default: - error("UseMagicItem: Spell %d noch nicht implementiert.\n",SpellNr_00); - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0xffffffff; - goto LAB_0808aa2d; - case (int *)0x7: - Damage_00 = ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0xf,5); - dz = Dest->ObjectID; - DmgType = 8; - AnimationNr = 4; - EffectNr = 6; - goto LAB_0808aae3; - case (int *)0x8: - Damage_00 = ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x1e,10); - dz = Dest->ObjectID; - EffectNr = 6; - DmgType = 8; - goto LAB_0808ab2e; - case (int *)0xc: - if (local_60 != (TCreature *)0x0) { - Convince(&this->super_TCreature,local_60); - break; - } - goto LAB_0808aa1b; - case (int *)0xe: - dz = Dest->ObjectID; - z = (TCreature *)&dz; - piVar8 = (int *)0x0; - ObjectIllusion(); - break; - case (int *)0xf: - ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x14,5); - dz = Dest->ObjectID; - goto LAB_0808abfc; - case (int *)0x10: - ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x32,0xf); - dz = Dest->ObjectID; - goto LAB_0808abfc; - case (int *)0x11: - dz = Dest->ObjectID; - goto LAB_0808ac98; - case (int *)0x12: - ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x3c,0x28); - dz = Dest->ObjectID; -LAB_0808abfc: - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - MassCombat(); - break; - case (int *)0x15: - Damage_00 = ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x96,0x14); - dz = Dest->ObjectID; - EffectNr = 0x12; - DmgType = 1; - AnimationNr = 0xb; - goto LAB_0808aae3; - case (int *)0x19: - dz = Dest->ObjectID; - goto LAB_0808ad72; - case (int *)0x1a: - dz = Dest->ObjectID; - goto LAB_0808ad72; - case (int *)0x1b: - dz = Dest->ObjectID; - goto LAB_0808ad72; - case (int *)0x1c: - dz = Dest->ObjectID; - goto LAB_0808adf7; - case (int *)0x1e: - dz = Dest->ObjectID; - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - DeleteField(); - break; - case (int *)0x1f: - if (local_60 != (TCreature *)0x0) { - piVar8 = (int *)0x0; - NegatePoison(); - z = local_60; - break; - } - goto LAB_0808aa1b; - case (int *)0x20: - dz = Dest->ObjectID; - goto LAB_0808adf7; - case (int *)0x21: - dz = Dest->ObjectID; -LAB_0808adf7: - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - CreateFieldWall(); - break; - case (int *)0x32: - Damage_00 = ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x78,0x14); - dz = Dest->ObjectID; - EffectNr = 0x10; - DmgType = 0x40; -LAB_0808ab2e: - AnimationNr = 4; - goto LAB_0808aae3; - case (int *)0x36: - if (local_60 != (TCreature *)0x0) { - uVar3 = TPlayer::GetEffectiveProfession(this); - if (uVar3 == '\x04') { - z = (TCreature *)SpellList[(int)SpellNr_00].SoulPoints; - piVar8 = (int *)SpellList[(int)SpellNr_00].Mana; - MagicGoStrength(); - break; - } - puVar5 = (undefined4 *)__cxa_allocate_exception(4,piVar7); - *puVar5 = 0x1d; - goto LAB_0808aa2d; - } -LAB_0808aa1b: - puVar5 = (undefined4 *)__cxa_allocate_exception(4,piVar7,piVar8); - *puVar5 = 0x27; -LAB_0808aa2d: - // WARNING: Subroutine does not return - __cxa_throw(puVar5,&RESULT::typeinfo,0); - case (int *)0x37: - dz = Dest->ObjectID; - goto LAB_0808ac98; - case (int *)0x4d: - Damage_00 = ComputeDamage(&this->super_TCreature,(int)SpellNr_00,0x46,0x14); - dz = Dest->ObjectID; - EffectNr = 0x11; - DmgType = 0x20; - AnimationNr = 5; -LAB_0808aae3: - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - Combat(&this->super_TCreature,(Object *)&dz,0,0,Damage_00,EffectNr,AnimationNr,DmgType); - break; - case (int *)0x4e: - dz = Dest->ObjectID; - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - CleanupField(); - break; - case (int *)0x53: - dz = Dest->ObjectID; - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - RaiseDead(); - break; - case (int *)0x56: - dz = Dest->ObjectID; -LAB_0808ad72: - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - CreateField(); - break; - case (int *)0x5b: - dz = Dest->ObjectID; -LAB_0808ac98: - z = (TCreature *)0x0; - piVar8 = (int *)0x0; - MassCreateField(); - } - bVar1 = Object::exists(Obj); - if (bVar1) { - dz = Obj->ObjectID; - DeleteRune((Object *)&dz); - } - if ((int)SpellNr_00 - 1U < 0xff) { - bVar2 = (byte)SpellList[(int)SpellNr_00].Flags & 1; - } - else { - error(&DAT_080fa6c0,SpellNr_00,piVar8,z); - bVar2 = 0; - } - if (bVar2 != 0) { - TCreature::BlockLogout(&this->super_TCreature,0x3c,false); - } - return; -} - - - -void DrinkPotion(ulong CreatureID,Object *Obj) - -{ - bool bVar1; - TPlayer *pTVar2; - ulong uVar3; - undefined4 *puVar4; - TPlayer *pl; - char *Text; - Object local_2c [7]; - - // try { // try from 0808b348 to 0808b48e has its CatchHandler @ 0808b4b5 - pTVar2 = GetPlayer(CreatureID); - if (pTVar2 == (TPlayer *)0x0) { - Text = "DrinkPotion: Kreatur existiert nicht.\n"; - } - else { - bVar1 = Object::exists(Obj); - if (bVar1) { - Object::getObjectType(local_2c); - bVar1 = ObjectType::getFlag((ObjectType *)local_2c,LIQUIDCONTAINER); - if (bVar1) { - uVar3 = Object::getAttribute(Obj,CONTAINERLIQUIDTYPE); - if (uVar3 == 10) { - ComputeDamage((TCreature *)0x0,0,100,0x32); - RefreshMana(); - } - else { - if (uVar3 != 0xb) { - Text = &DAT_080fdd40; - goto LAB_0808b420; - } - ComputeDamage((TCreature *)0x0,0,0x32,0x19); - Heal(); - } - local_2c[0] = (Object)Obj->ObjectID; - Change(local_2c,CONTAINERLIQUIDTYPE,0); - return; - } - Text = &DAT_080fdd80; - } - else { - Text = &DAT_080fdde0; - } - } -LAB_0808b420: - error(Text); - puVar4 = (undefined4 *)__cxa_allocate_exception(4); - *puVar4 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar4,&RESULT::typeinfo,0); -} - -void CreateField(int x,int y,int z,int FieldType,ulong Owner,bool Peaceful) - -{ - bool bVar1; - undefined4 *puVar2; - ObjectType *this; - Object local_6c [4]; - ObjectType local_5c; - Object Help; - ObjectType local_4c; - Object Obj; - ObjectType local_3c [4]; - ObjectType local_2c; - ObjectType ObjectType; - - // try { // try from 0808db84 to 0808dc7d has its CatchHandler @ 0808dde0 - ::ObjectType::setTypeID(&local_2c,0); - switch(FieldType) { - default: - error(&DAT_080fdf20,FieldType); - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0xffffffff; - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); - case 1: - GetSpecialObject((SPECIALMEANING)&local_4c); - ::ObjectType::setTypeID(&local_2c,local_4c.TypeID); - this = local_3c; - goto LAB_0808dbd1; - case 2: - break; - case 3: - break; - case 4: - break; - case 5: - } - GetSpecialObject((SPECIALMEANING)local_3c); - ::ObjectType::setTypeID(&local_2c,local_3c[0].TypeID); - this = &local_4c; -LAB_0808dbd1: - ::ObjectType::setTypeID(this,local_2c.TypeID); - bVar1 = FieldPossible(); - if (bVar1) { - GetFirstObject(); - if (local_4c.TypeID != NONE.ObjectID) { - do { - local_5c.TypeID = local_4c.TypeID; - local_3c[0].TypeID = NONE.ObjectID; - Object::getNextObject(local_6c); - local_4c.TypeID = local_6c[0].ObjectID; - local_3c[0].TypeID = local_6c[0].ObjectID; - Object::getObjectType(local_6c); - bVar1 = ::ObjectType::getFlag((ObjectType *)local_6c,MAGICFIELD); - if (bVar1) { - local_6c[0].ObjectID = local_5c.TypeID; - // try { // try from 0808dcf9 to 0808dd64 has its CatchHandler @ 0808dde0 - Delete(local_6c,-1); - } - } while (local_4c.TypeID != NONE.ObjectID); - } - local_5c.TypeID = NONE.ObjectID; - local_3c[0].TypeID = NONE.ObjectID; - // try { // try from 0808dc9e to 0808dcdf has its CatchHandler @ 0808dda3 - ::ObjectType::setTypeID(&local_5c,local_2c.TypeID); - GetMapContainer((int)&local_4c,x,y); - Create(local_6c,&local_4c,(ulong)&local_5c); - } - return; -} - - - // WARNING: Unknown calling convention -- yet parameter storage is locked void _GLOBAL__I_SpellSyllable(void) diff --git a/src/crcombat.cc b/src/crcombat.cc index 2843cd3..e1bf72f 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -715,14 +715,13 @@ void TCombat::WandAttack(TCreature *Target){ int AttackStrength = WandType.getAttribute(WANDATTACKSTRENGTH); int AttackVariation = (int)WandType.getAttribute(WANDATTACKVARIATION); - // NOTE(fusion): Oof... try{ CheckMana(Master, ManaConsumption, 0, 0); - }catch(RESULT err){ - if(err == NOTENOUGHMANA){ + }catch(RESULT r){ + if(r == NOTENOUGHMANA){ throw OUTOFAMMO; }else{ - throw err; + throw r; } } @@ -836,7 +835,7 @@ void TCombat::DistanceAttack(TCreature *Target){ int Damage = ComputeDamage(Master, 0, EffectStrength, EffectStrength); TDamageImpact Impact(Master, DAMAGE_PHYSICAL, Damage, false); CircleShapeSpell(Master, DropX, DropY, DropZ, -1, - ANIMATION_NONE, 2, &Impact, EFFECT_BURST_ARROW); + ANIMATION_NONE, 2, &Impact, EFFECT_FIRE); } try{ @@ -845,10 +844,10 @@ void TCombat::DistanceAttack(TCreature *Target){ }else{ Move(0, this->Ammo, DropCon, 1, false, NONE); } - }catch(RESULT err){ - if(err != DESTROYED){ + }catch(RESULT r){ + if(r != DESTROYED){ error("TCombat::RangeAttack: Konnte Ammo nicht verschieben/löschen" - " (Exception %d, [%d,%d,%d].\n", err, DropX, DropY, DropZ); + " (Exception %d, [%d,%d,%d].\n", r, DropX, DropY, DropZ); } } diff --git a/src/creature.cc b/src/creature.cc index d1cb7bf..9eadc6e 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -165,9 +165,9 @@ int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){ ObjectType WearOutType = ObjType.getAttribute(WEAROUTTARGET); Change(Obj, WearOutType, 0); } - }catch(RESULT err){ + }catch(RESULT r){ error("TCreature::Damage: Exception %d beim Abnutzen von Objekt %d.\n", - err, ObjType.TypeID); + r, ObjType.TypeID); } } }else if(ObjType.getFlag(PROTECTION) && !ObjType.getFlag(CLOTHES)){ diff --git a/src/enums.hh b/src/enums.hh index 7b2fc68..21194dd 100644 --- a/src/enums.hh +++ b/src/enums.hh @@ -18,6 +18,7 @@ enum AnimationType: int { ANIMATION_THROWING_STAR = 8, ANIMATION_THROWING_KNIFE = 9, ANIMATION_SMALL_STONE = 10, + ANIMATION_DEATH = 11, ANIMATION_SNOWBALL = 13, ANIMATION_POWER_BOLT = 14, ANIMATION_POISON = 15, @@ -81,10 +82,9 @@ enum DamageType: int { DAMAGE_POISON = 0x0002, DAMAGE_FIRE = 0x0004, DAMAGE_ENERGY = 0x0008, - //DAMAGE_DEATH ? = 0x0010 - DAMAGE_POISON_PERIODIC = 0x0020, - DAMAGE_FIRE_PERIODIC = 0x0040, - DAMAGE_ENERGY_PERIODIC = 0x0080, + DAMAGE_POISON_PERIODIC = 0x0020, // Better name? + DAMAGE_FIRE_PERIODIC = 0x0040, // Better name? + DAMAGE_ENERGY_PERIODIC = 0x0080, // Better name? DAMAGE_LIFEDRAIN = 0x0100, DAMAGE_MANADRAIN = 0x0200, }; @@ -109,16 +109,19 @@ enum EffectType: int { EFFECT_MANA_HIT = 2, EFFECT_POFF = 3, EFFECT_BLOCK_HIT = 4, - EFFECT_BURST_ARROW = 7, + EFFECT_FIRE_EXPLOSION = 5, + EFFECT_FIRE_BURST = 6, // Not sure about this one. + EFFECT_FIRE = 7, EFFECT_POISON = 9, EFFECT_BONE_HIT = 10, - EFFECT_TELEPORT = 11, + EFFECT_ENERGY = 11, EFFECT_ENERGY_HIT = 12, EFFECT_MAGIC_BLUE = 13, EFFECT_MAGIC_RED = 14, EFFECT_MAGIC_GREEN = 15, EFFECT_FIRE_HIT = 16, EFFECT_POISON_HIT = 17, + EFFECT_DEATH = 18, }; enum FLAG: int { @@ -232,6 +235,8 @@ enum LiquidType: int { LIQUID_MUD = 4, LIQUID_BLOOD = 5, LIQUID_SLIME = 6, + LIQUID_MANA = 10, + LIQUID_LIFE = 11, LIQUID_LEMONADE = 12, }; diff --git a/src/magic.cc b/src/magic.cc index 3db1456..b3d70b1 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -7,6 +7,26 @@ #include "stubs.hh" #include +#include + +struct TCircle { + int x[32]; + int y[32]; + int Count; +}; + +struct TSpellList { + uint8 Syllable[10]; + uint8 RuneGr; + uint8 RuneNr; + const char *Comment; + uint16 Level; + uint16 RuneLevel; + uint16 Flags; + int Mana; + int SoulPoints; + int Amount; +}; static TSpellList SpellList[256]; static TCircle Circle[10]; @@ -593,44 +613,6 @@ void AngleShapeSpell(TCreature *Actor, int Angle, int Range, TImpact *Impact, in } } -// Spell Casting -// ============================================================================= -int GetDirection(int dx, int dy){ - // TODO(fusion): This function originally returned directions different from - // the ones used by creatures. I've converted it to use the same values which - // are also defined in `enums.hh` for simplicity. - int Result; - if(dx == 0){ - if(dy < 0){ - Result = DIRECTION_NORTH; - }else if(dy > 0){ - Result = DIRECTION_SOUTH; - }else{ - Result = DIRECTION_INVALID; - } - }else{ - // NOTE(fusion): This function uses the approximate tangent value, avoiding - // floating point calculations, for whatever reason. The tangent is unique - // and odd in the interval (-PI/2, +PI/2). We also need to recall that the - // Y-axis is inverted in Tibia, so we need to negate `dy`. - constexpr int Tangent_67_5 = 618; // => 618 / 256 ~ 2.41 ~ tan(67.5 deg) - constexpr int Tangent_22_5 = 106; // => 106 / 256 ~ 0.41 ~ tan(22.5 deg) - int Tangent = (-dy * 256) / dx; // => (dy * 256) / dx ~ (dy / dx) * 256 - if(Tangent >= Tangent_67_5){ - Result = DIRECTION_NORTH; - }else if(Tangent >= Tangent_22_5){ - Result = (dx < 0) ? DIRECTION_NORTHWEST : DIRECTION_NORTHEAST; - }else if(Tangent >= -Tangent_22_5){ - Result = (dx < 0) ? DIRECTION_WEST : DIRECTION_EAST; - }else if(Tangent >= -Tangent_67_5){ - Result = (dx < 0) ? DIRECTION_SOUTHWEST : DIRECTION_SOUTHEAST; - }else{ - Result = DIRECTION_SOUTH; - } - } - return Result; -} - void CheckSpellbook(TCreature *Actor, int SpellNr){ if(Actor == NULL){ error("CheckSpellbook: Übergebene Kreatur existiert nicht.\n"); @@ -908,6 +890,42 @@ void Combat(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, INT_MAX, Animation, 0, &Impact, Effect); } +int GetDirection(int dx, int dy){ + // TODO(fusion): This function originally returned directions different from + // the ones used by creatures. I've converted it to use the same values which + // are also defined in `enums.hh` for simplicity. + int Result; + if(dx == 0){ + if(dy < 0){ + Result = DIRECTION_NORTH; + }else if(dy > 0){ + Result = DIRECTION_SOUTH; + }else{ + Result = DIRECTION_INVALID; + } + }else{ + // NOTE(fusion): This function uses the approximate tangent value, avoiding + // floating point calculations, for whatever reason. The tangent is unique + // and odd in the interval (-PI/2, +PI/2). We also need to recall that the + // Y-axis is inverted in Tibia, so we need to negate `dy`. + constexpr int Tangent_67_5 = 618; // => 618 / 256 ~ 2.41 ~ tan(67.5 deg) + constexpr int Tangent_22_5 = 106; // => 106 / 256 ~ 0.41 ~ tan(22.5 deg) + int Tangent = (-dy * 256) / dx; // => (dy * 256) / dx ~ (dy / dx) * 256 + if(Tangent >= Tangent_67_5){ + Result = DIRECTION_NORTH; + }else if(Tangent >= Tangent_22_5){ + Result = (dx < 0) ? DIRECTION_NORTHWEST : DIRECTION_NORTHEAST; + }else if(Tangent >= -Tangent_22_5){ + Result = (dx < 0) ? DIRECTION_WEST : DIRECTION_EAST; + }else if(Tangent >= -Tangent_67_5){ + Result = (dx < 0) ? DIRECTION_SOUTHWEST : DIRECTION_SOUTHEAST; + }else{ + Result = DIRECTION_SOUTH; + } + } + return Result; +} + // Spell Functions // ============================================================================= void KillAllMonsters(TCreature *Actor, int Effect, int Radius){ @@ -966,6 +984,72 @@ void KillAllMonsters(TCreature *Actor, int Effect, int Radius){ } } +void CreateField(int x, int y, int z, int FieldType, uint32 Owner, bool Peaceful){ + if(!FieldPossible(x, y, z, FieldType)){ + return; + } + + SPECIALMEANING Meaning; + switch(FieldType){ + case FIELD_TYPE_FIRE:{ + if(Peaceful){ + Meaning = MAGICFIELD_FIRE_HARMLESS; + }else{ + Meaning = MAGICFIELD_FIRE_DANGEROUS; + } + break; + } + + case FIELD_TYPE_POISON:{ + if(Peaceful){ + Meaning = MAGICFIELD_POISON_HARMLESS; + }else{ + Meaning = MAGICFIELD_POISON_DANGEROUS; + } + break; + } + + case FIELD_TYPE_ENERGY:{ + if(Peaceful){ + Meaning = MAGICFIELD_ENERGY_HARMLESS; + }else{ + Meaning = MAGICFIELD_ENERGY_DANGEROUS; + } + break; + } + + case FIELD_TYPE_MAGICWALL:{ + Meaning = MAGICFIELD_MAGICWALL; + break; + } + + case FIELD_TYPE_WILDGROWTH:{ + Meaning = MAGICFIELD_RUSHWOOD; + break; + } + + default:{ + error("CreateField: Ungültiger Feldtyp %d.\n", FieldType); + throw ERROR; + } + } + + // NOTE(fusion): Delete other magic fields? + Object Obj = GetFirstObject(x, y, z); + while(Obj != NONE){ + Object Next = Obj.getNextObject(); + if(Obj.getObjectType().getFlag(MAGICFIELD)){ + Delete(Obj, -1); + } + Obj = Next; + } + + // NOTE(fusion): Create field, at last. + Create(GetMapContainer(x, y, z), + GetSpecialObject(Meaning), + Owner); +} + void CreateField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int FieldType){ if(Actor == NULL){ error("CreateField: Ungültige Kreatur übergeben.\n"); @@ -993,7 +1077,7 @@ void CreateField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints CheckMana(Actor, ManaPoints, SoulPoints, Delay); int Animation = ANIMATION_ENERGY; - if(FieldType == 1){ + if(FieldType == FIELD_TYPE_FIRE){ Animation = ANIMATION_FIRE; } Missile(Actor->CrObject, Target, Animation); @@ -1001,8 +1085,9 @@ void CreateField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints bool Peaceful = (WorldType == NON_PVP && Actor->IsPeaceful()); CreateField(TargetX, TargetY, TargetZ, FieldType, Actor->ID, Peaceful); - // TODO(fusion): Probably damaging fields? - if(FieldType == 1 || FieldType == 2 || FieldType == 3){ + if(FieldType == FIELD_TYPE_FIRE + || FieldType == FIELD_TYPE_POISON + || FieldType == FIELD_TYPE_ENERGY){ Actor->BlockLogout(60, true); } } @@ -1058,7 +1143,7 @@ void MassCreateField(TCreature *Actor, Object Target, if(Actor->posx != TargetX || Actor->posy != TargetY || Actor->posz != TargetZ){ int Animation = ANIMATION_ENERGY; - if(FieldType == 1){ + if(FieldType == FIELD_TYPE_FIRE){ Animation = ANIMATION_FIRE; } Missile(Actor->CrObject, Target, Animation); @@ -1089,7 +1174,9 @@ void MassCreateField(TCreature *Actor, Object Target, } } - if(FieldType == 1 || FieldType == 2 || FieldType == 3){ + if(FieldType == FIELD_TYPE_FIRE + || FieldType == FIELD_TYPE_POISON + || FieldType == FIELD_TYPE_ENERGY){ Actor->BlockLogout(60, true); } } @@ -1121,7 +1208,7 @@ void CreateFieldWall(TCreature *Actor, Object Target, if(ActorX != TargetX || ActorY != TargetY || ActorZ != TargetZ){ int Animation = ANIMATION_ENERGY; - if(FieldType == 1){ + if(FieldType == FIELD_TYPE_FIRE){ Animation = ANIMATION_FIRE; } Missile(Actor->CrObject, Target, Animation); @@ -1216,7 +1303,9 @@ void CreateFieldWall(TCreature *Actor, Object Target, } } - if(FieldType == 1 || FieldType == 2 || FieldType == 3){ + if(FieldType == FIELD_TYPE_FIRE + || FieldType == FIELD_TYPE_POISON + || FieldType == FIELD_TYPE_ENERGY){ Actor->BlockLogout(60, true); } } @@ -1321,7 +1410,7 @@ void CleanupField(TCreature *Actor){ CleanupField(Actor, Target, 0, 0); } -void Teleport(TCreature *Actor, char *Param){ +void Teleport(TCreature *Actor, const char *Param){ if(Actor == NULL){ error("Teleport: Ungültige Kreatur übergeben.\n"); throw ERROR; @@ -1414,7 +1503,7 @@ void Teleport(TCreature *Actor, char *Param){ } Object Dest = GetMapContainer(DestX, DestY, DestZ); Move(0, Actor->CrObject, Dest, -1, false, NONE); - GraphicalEffect(DestX, DestY, DestZ, EFFECT_TELEPORT); + GraphicalEffect(DestX, DestY, DestZ, EFFECT_ENERGY); }else if(MDGoStrength != Actor->Skills[SKILL_GO_STRENGTH]->MDAct){ Actor->Skills[SKILL_GO_STRENGTH]->SetMDAct(MDGoStrength); AnnounceChangedCreature(Actor->ID, 4); // CREATURE_GO_STRENGTH_CHANGED ? @@ -1455,7 +1544,7 @@ void TeleportToCreature(TCreature *Actor, const char *Name){ } if(Actor == Player){ - GraphicalEffect(Actor->CrObject, EFFECT_TELEPORT); + GraphicalEffect(Actor->CrObject, EFFECT_ENERGY); return; } @@ -1475,7 +1564,7 @@ void TeleportToCreature(TCreature *Actor, const char *Name){ Object Dest = GetMapContainer(DestX, DestY, DestZ); Move(0, Actor->CrObject, Dest, -1, false, NONE); - GraphicalEffect(DestX, DestY, DestZ, EFFECT_TELEPORT); + GraphicalEffect(DestX, DestY, DestZ, EFFECT_ENERGY); Log("banish", "%s teleportiert sich zu %s.\n", Actor->Name, Player->Name); } @@ -1523,7 +1612,7 @@ void TeleportPlayerToMe(TCreature *Actor, const char *Name){ Object Dest = GetMapContainer(DestX, DestY, DestZ); Move(0, Player->CrObject, Dest, -1, false, NONE); - GraphicalEffect(DestX, DestY, DestZ, EFFECT_TELEPORT); + GraphicalEffect(DestX, DestY, DestZ, EFFECT_ENERGY); } void MagicRope(TCreature *Actor, int ManaPoints, int SoulPoints){ @@ -1543,7 +1632,7 @@ void MagicRope(TCreature *Actor, int ManaPoints, int SoulPoints){ Object Dest = GetMapContainer(OrigX, OrigY + 1, OrigZ - 1); Move(0, Actor->CrObject, Dest, -1, false, NONE); - GraphicalEffect(OrigX, OrigY, OrigZ, EFFECT_TELEPORT); + GraphicalEffect(OrigX, OrigY, OrigZ, EFFECT_ENERGY); } void MagicClimbing(TCreature *Actor, int ManaPoints, int SoulPoints, const char *Param){ @@ -1594,7 +1683,7 @@ void MagicClimbing(TCreature *Actor, int ManaPoints, int SoulPoints, const char Object Dest = GetMapContainer(DestX, DestY, DestZ); Move(0, Actor->CrObject, Dest, -1, false, NONE); - GraphicalEffect(OrigX, OrigY, OrigZ, EFFECT_TELEPORT); + GraphicalEffect(OrigX, OrigY, OrigZ, EFFECT_ENERGY); } void MagicClimbing(TCreature *Actor, const char *Param){ @@ -1825,7 +1914,7 @@ void SummonCreature(TCreature *Actor, int ManaPoints, int Race, bool God){ GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); } -void SummonCreature(TCreature *Actor, int Mana, const char *RaceName, bool God){ +void SummonCreature(TCreature *Actor, int ManaPoints, const char *RaceName, bool God){ if(Actor == NULL){ error("SummonCreature: Ungültige Kreatur übergeben.\n"); throw ERROR; @@ -1841,7 +1930,7 @@ void SummonCreature(TCreature *Actor, int Mana, const char *RaceName, bool God){ throw CREATURENOTEXISTING; } - SummonCreature(Actor, Mana, Race, God); + SummonCreature(Actor, ManaPoints, Race, God); } void StartMonsterraid(TCreature *Actor, const char *RaidName){ @@ -2025,7 +2114,6 @@ void Heal(TCreature *Actor, int ManaPoints, int SoulPoints, int Amount){ } } - void MassHeal(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Amount, int Radius){ if(Actor == NULL){ error("MassHeal: Ungültige Kreatur übergeben.\n"); @@ -2098,7 +2186,7 @@ void MassHeal(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, i } } -void HealFriend(TCreature *Actor, char *TargetName, int ManaPoints, int SoulPoints, int Amount){ +void HealFriend(TCreature *Actor, const char *TargetName, int ManaPoints, int SoulPoints, int Amount){ if(Actor == NULL){ error("HealFriend: Ungültige Kreatur übergeben.\n"); throw ERROR; @@ -2352,7 +2440,7 @@ void CancelInvisibility(TCreature *Actor, Object Target, int ManaPoints, int Sou } } -void CreatureIllusion(TCreature *Actor, const char *RaceName, int ManaPoints, int SoulPoints, int Duration){ +void CreatureIllusion(TCreature *Actor, int ManaPoints, int SoulPoints, const char *RaceName, int Duration){ if(Actor == NULL){ error("CreatureIllusion: Ungültige Kreatur übergeben.\n"); throw ERROR; @@ -2381,7 +2469,7 @@ void CreatureIllusion(TCreature *Actor, const char *RaceName, int ManaPoints, in GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_BLUE); } -void ObjectIllusion(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Duration){ +void ObjectIllusion(TCreature *Actor, int ManaPoints, int SoulPoints, Object Target, int Duration){ if(Actor == NULL){ error("ObjectIllusion: Ungültige Kreatur übergeben.\n"); throw ERROR; @@ -2570,7 +2658,7 @@ void Challenge(TCreature *Actor, int ManaPoints, int SoulPoints, int Radius){ } } -void FindPerson(TCreature *Actor, const char *TargetName, int ManaPoints, int SoulPoints){ +void FindPerson(TCreature *Actor, int ManaPoints, int SoulPoints, const char *TargetName){ // TODO(fusion): And we're back. if(Actor == NULL){ error("FindPerson: Ungültige Kreatur übergeben.\n"); @@ -3220,13 +3308,1040 @@ void HomeTeleport(TCreature *Actor, const char *Name){ Object Dest = GetMapContainer(Player->startx, Player->starty, Player->startz); GraphicalEffect(Player->posx, Player->posy, Player->posz, EFFECT_POFF); Move(0, Player->CrObject, Dest, -1, false, NONE); - GraphicalEffect(Player->posx, Player->posy, Player->posz, EFFECT_TELEPORT); + GraphicalEffect(Player->posx, Player->posy, Player->posz, EFFECT_ENERGY); SendMessage(Actor->Connection, TALK_INFO_MESSAGE, "Player %s has been moved to the temple.", Player->Name); Log("banish", "%s teleportiert %s zum Tempel.\n", Actor->Name, Player->Name); } +// Spell Casting +// ============================================================================= +static void SpellFailed(uint32 CreatureID){ + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("SpellFailed: Kreatur existiert nicht.\n"); + return; + } + + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_POFF); +} + +static void CharacterRightSpell(uint32 CreatureID, int SpellNr, const char (*SpellStr)[512]){ + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("CharacterRightSpell: Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(Actor->Type != PLAYER){ + return; + } + + switch(SpellNr) { + case 34: CreateThing(Actor, SpellStr[2], SpellStr[3]); break; + case 35: CreateThing(Actor, SpellStr[2], NULL); break; + case 37: Teleport(Actor, SpellStr[2]); break; + case 40: CreateKnowledge(Actor, SpellStr[3], NULL); break; + case 41: ChangeData(Actor, SpellStr[2]); break; + case 46: CreateKnowledge(Actor, SpellStr[3], SpellStr[4]); break; + case 47: TeleportToCreature(Actor, SpellStr[3]); break; + case 52: TeleportPlayerToMe(Actor, SpellStr[3]); break; + case 53: SummonCreature(Actor, 0, SpellStr[3], true); break; + case 58: GetPosition(Actor); break; + case 63: CreateMoney(Actor, SpellStr[3]); break; + case 64: ChangeProfession(Actor, SpellStr[3]); break; + case 71: EditGuests(Actor); break; + case 72: EditSubowners(Actor); break; + case 73: KickGuest(Actor, SpellStr[3]); break; + case 74: EditNameDoor(Actor); break; + case 96: GetQuestValue(Actor, SpellStr[3]); break; + case 97: SetQuestValue(Actor, SpellStr[3], SpellStr[4]); break; + case 98: CleanupField(Actor); break; + case 99: MagicClimbing(Actor, SpellStr[3]); break; + case 100: ClearQuestValues(Actor); break; + case 101: KillAllMonsters(Actor, EFFECT_DEATH, 2); break; + case 102: StartMonsterraid(Actor, SpellStr[4]); break; + } +} + +static void AccountRightSpell(uint32 CreatureID, int SpellNr, const char (*SpellStr)[512]){ + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("AccountRightSpell: Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(Actor->Type != PLAYER){ + return; + } + + switch(SpellNr) { + case 57: BanishAccount(Actor, SpellStr[3], atoi(SpellStr[4]), SpellStr[5]); break; + case 60: HomeTeleport(Actor, SpellStr[2]); break; + case 61: DeleteAccount(Actor, SpellStr[4], SpellStr[5]); break; + case 62: SetNameRule(Actor, SpellStr[2]); break; + case 65: Notation(Actor, SpellStr[2], SpellStr[3]); break; + case 66: NameLock(Actor, SpellStr[3]); break; + case 67: KickPlayer(Actor, SpellStr[2]); break; + case 68: DeleteCharacter(Actor, SpellStr[4], SpellStr[5]); break; + case 69: IPBanishment(Actor, SpellStr[3], SpellStr[4]); break; + case 70: BanishCharacter(Actor, SpellStr[3], atoi(SpellStr[4]), SpellStr[5]); break; + } +} + +static void CastSpell(uint32 CreatureID, int SpellNr, const char (*SpellStr)[512]){ + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("CastSpell: Kreatur existiert nicht.\n"); + throw ERROR; + } + + CheckSpellbook(Actor, SpellNr); + CheckAccount(Actor, SpellNr); + CheckLevel(Actor, SpellNr); + CheckRing(Actor, SpellNr); + + if(Actor->EarliestSpellTime > ServerMilliseconds){ + throw EXHAUSTED; + } + + if(IsAggressiveSpell(SpellNr) && Actor->Type == PLAYER + && !CheckRight(Actor->ID, ATTACK_EVERYWHERE) + && IsProtectionZone(Actor->posx, Actor->posy, Actor->posz)){ + throw PROTECTIONZONE; + } + + int ManaPoints = SpellList[SpellNr].Mana; + int SoulPoints = SpellList[SpellNr].SoulPoints; + switch(SpellNr) { + case 1:{ + int Amount = ComputeDamage(Actor, SpellNr, 20, 10); + Heal(Actor, ManaPoints, SoulPoints, Amount); + break; + } + + case 2:{ + int Amount = ComputeDamage(Actor, SpellNr, 40, 20); + Heal(Actor, ManaPoints, SoulPoints, Amount); + break; + } + + case 3:{ + int Amount = ComputeDamage(Actor, SpellNr, 250, 50); + Heal(Actor, ManaPoints, SoulPoints, Amount); + break; + } + + case 6:{ + MagicGoStrength(Actor, Actor, ManaPoints, SoulPoints, 30, 3); + break; + } + + case 9:{ + SummonCreature(Actor, ManaPoints, SpellStr[3], false); + break; + } + + case 10:{ + Enlight(Actor, ManaPoints, SoulPoints, 6, 500); + break; + } + + case 11:{ + Enlight(Actor, ManaPoints, SoulPoints, 8, 1000); + break; + } + + case 13:{ + int Damage = ComputeDamage(Actor, SpellNr, 150, 50); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_ENERGY, 5, 30, DAMAGE_ENERGY); + break; + } + + case 19:{ + int Damage = ComputeDamage(Actor, SpellNr, 30, 10); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_FIRE, 4, 45, DAMAGE_FIRE); + break; + } + + case 20:{ + FindPerson(Actor, ManaPoints, SoulPoints, SpellStr[2]); + break; + } + + case 22:{ + int Damage = ComputeDamage(Actor, SpellNr, 60, 20); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_FIRE_BURST, 5, 0, DAMAGE_ENERGY); + break; + } + + case 23:{ + int Damage = ComputeDamage(Actor, SpellNr, 120, 80); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_FIRE_BURST, 8, 0, DAMAGE_ENERGY); + break; + } + + case 24:{ + int Damage = ComputeDamage(Actor, SpellNr, 250, 50); + MassCombat(Actor, Actor->CrObject, ManaPoints, SoulPoints, Damage, + EFFECT_FIRE_EXPLOSION, 6, DAMAGE_PHYSICAL, ANIMATION_FIRE); + break; + } + + case 29:{ + NegatePoison(Actor, Actor, ManaPoints, SoulPoints); + break; + } + + case 38:{ + CreatureIllusion(Actor, ManaPoints, SoulPoints, SpellStr[4], 200); + break; + } + + case 39:{ + MagicGoStrength(Actor, Actor, ManaPoints, SoulPoints, 70, 2); + break; + } + + case 42:{ + CreateFood(Actor, ManaPoints, SoulPoints); + break; + } + + case 44:{ + Shielding(Actor, ManaPoints, SoulPoints, 200); + break; + } + + case 45:{ + Invisibility(Actor, ManaPoints, SoulPoints, 200); + break; + } + + case 48:{ + CreateArrows(Actor, ManaPoints, SoulPoints, 1, 5); + break; + } + + case 49:{ + CreateArrows(Actor, ManaPoints, SoulPoints, 2, 3); + break; + } + + case 51:{ + CreateArrows(Actor, ManaPoints, SoulPoints, 0, 10); + break; + } + + case 56:{ + int Damage = ComputeDamage(Actor, SpellNr, 200, 50); + MassCombat(Actor, Actor->CrObject, ManaPoints, SoulPoints, Damage, + EFFECT_POISON, 8, DAMAGE_POISON_PERIODIC, ANIMATION_NONE); + break; + } + + case 75:{ + Enlight(Actor, ManaPoints, SoulPoints, 9, 2000); + break; + } + + case 76:{ + MagicRope(Actor, ManaPoints, SoulPoints); + break; + } + + case 79:{ + CreateArrows(Actor, ManaPoints, SoulPoints, 3, 5); + break; + } + + case 80:{ + int Level = Actor->Skills[SKILL_LEVEL]->Get(); + int Damage = (Level * ComputeDamage(Actor, SpellNr, 80, 20)) / 25; + MassCombat(Actor, Actor->CrObject, Level * 4, 0, Damage, + EFFECT_BONE_HIT, 2, DAMAGE_PHYSICAL, ANIMATION_NONE); + break; + } + + case 81:{ + MagicClimbing(Actor, ManaPoints, SoulPoints, SpellStr[3]); + break; + } + + case 82:{ + int Amount = ComputeDamage(Actor, SpellNr, 200, 40); + MassHeal(Actor, Actor->CrObject, ManaPoints, SoulPoints, Amount, 4); + break; + } + + case 84:{ + int Amount = ComputeDamage(Actor,SpellNr,120,40); + HealFriend(Actor, SpellStr[3], ManaPoints, SoulPoints, Amount); + break; + } + + case 85:{ + MassRaiseDead(Actor, Actor->CrObject, ManaPoints, SoulPoints, 4); + break; + } + + case 87:{ + int Damage = ComputeDamage(Actor, SpellNr, 45, 10); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_DEATH, 1, 0, DAMAGE_PHYSICAL); + break; + } + + case 88:{ + int Damage = ComputeDamage(Actor, SpellNr, 45, 10); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_ENERGY, 1, 0, DAMAGE_ENERGY); + break; + } + + case 89:{ + int Damage = ComputeDamage(Actor, SpellNr, 45, 10); + AngleCombat(Actor, ManaPoints, SoulPoints, Damage, + EFFECT_FIRE, 1, 0, DAMAGE_FIRE); + break; + } + + case 90:{ + CancelInvisibility(Actor, Actor->CrObject, ManaPoints, SoulPoints, 4); + break; + } + + case 92:{ + ObjectType OldType = GetNewObjectType(90, 25); + ObjectType NewType = GetNewObjectType(90, 57); + EnchantObject(Actor, ManaPoints, SoulPoints, OldType, NewType); + break; + } + + case 93:{ + Challenge(Actor, ManaPoints, SoulPoints, 2); + break; + } + + case 94:{ + CreateField(Actor, ManaPoints, SoulPoints, FIELD_TYPE_WILDGROWTH); + break; + } + + case 95:{ + CreateArrows(Actor, ManaPoints, SoulPoints, 4, 1); + break; + } + } + + if(IsAggressiveSpell(SpellNr)){ + Actor->BlockLogout(60, false); + } +} + +static void RuneSpell(uint32 CreatureID, int SpellNr){ + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("RuneSpell: Kreatur existiert nicht.\n"); + throw ERROR; + } + + uint8 RuneGr = SpellList[SpellNr].RuneGr; + uint8 RuneNr = SpellList[SpellNr].RuneNr; + int ManaPoints = SpellList[SpellNr].Mana; + int SoulPoints = SpellList[SpellNr].SoulPoints; + int Amount = SpellList[SpellNr].Amount; + + if(RuneGr == 0){ + error("RuneSpell: Spell %d ist Runenspruch, hat aber keine Rune.\n", SpellNr); + throw ERROR; + } + + CheckSpellbook(Actor, SpellNr); + CheckAccount(Actor, SpellNr); + CheckLevel(Actor, SpellNr); + CheckRing(Actor, SpellNr); + if(Actor->EarliestSpellTime > ServerMilliseconds){ + throw EXHAUSTED; + } + + bool RuneCreated = false; + ObjectType BlankType = GetSpecialObject(RUNE_BLANK); + + Object RightHand = GetBodyObject(Actor->ID, 5); // RIGHTHAND + if(RightHand.exists() && RightHand.getObjectType() == BlankType){ + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + ObjectType RuneType = GetNewObjectType(RuneGr, RuneNr); + Change(RightHand, RuneType, Amount); + RuneCreated = true; + } + + Object LeftHand = GetBodyObject(Actor->ID, 6); // LEFTHAND + if(LeftHand.exists() && LeftHand.getObjectType() == BlankType){ + // TODO(fusion): Ughh... I'm not sure why we're trying to cast the spell + // twice but we need to make so errors from the second cast are only + // relevant if there wasn't a first cast. + try{ + CheckMana(Actor, ManaPoints, SoulPoints, 1000); + ObjectType RuneType = GetNewObjectType(RuneGr, RuneNr); + Change(LeftHand, RuneType, Amount); + RuneCreated = true; + }catch(...){ + if(!RuneCreated){ + throw; + } + } + } + + if(!RuneCreated){ + throw MAGICITEM; + } + + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_MAGIC_RED); +} + +static int TypeOfSpell(const char *Text){ + // NOTE(fusion): We're checking `Text` against the first 5 spell syllables + // which are "al", "ad", "ex", "ut", "om". They determine the type of the + // spell. See `CheckForSpell`. + int SpellType = 0; + if(Text != NULL){ + for(int SyllableNr = 1; + SyllableNr < 6; + SyllableNr += 1){ + if(stricmp(Text, SpellSyllable[SyllableNr], 2) == 0){ + SpellType = SyllableNr; + break; + } + } + } + return SpellType; +} + +static int FindSpell(const uint8 *Syllable){ + int BestMatch = 0; + int MinParams = 100; + for(int SpellNr = 0; + SpellNr < NARRAY(SpellList); + SpellNr += 1){ + int Params = 0; + TSpellList *Spell = &SpellList[SpellNr]; + for(int i = 0; + i < NARRAY(Spell->Syllable); + i += 1){ + // NOTE(fusion): SpellSyllable[6] is "para" which refers to a spell + // parameter. + if(Spell->Syllable[i] == 6){ + if(Syllable[i] == 0) + break; + Params += 1; + }else if(Spell->Syllable[i] != Syllable[i]){ + break; + } + + if(Spell->Syllable[i] == 0){ + if(Params < MinParams){ + MinParams = Params; + BestMatch = SpellNr; + } + break; + } + } + } + + return BestMatch; +} + +static int FindSpell(Object Obj){ + if(!Obj.exists()){ + error("FindSpell: Übergebenes Objekt existiert nicht.\n"); + return 0; + } + + ObjectType ObjType = Obj.getObjectType(); + if(!ObjType.getFlag(RUNE)){ + error("FindSpell: Übergebenes Objekt ist nicht magisch.\n"); + return 0; + } + + int Result = 0; + for(int SpellNr = 0; + SpellNr < NARRAY(SpellList); + SpellNr += 1){ + TSpellList *Spell = &SpellList[SpellNr]; + ObjectType RuneType = GetNewObjectType(Spell->RuneGr, Spell->RuneNr); + if(ObjType == RuneType){ + Result = SpellNr; + break; + } + } + return Result; +} + +static void GetSpellString(int SpellNr, char *Text){ + Text[0] = 0; + + if(SpellNr < 1 || SpellNr >= NARRAY(SpellList)){ + error("GetSpellString: Ungültige Zauberspruchnummer %d.\n", SpellNr); + return; + } + + TSpellList *Spell = &SpellList[SpellNr]; + for(int i = 0; i < NARRAY(Spell->Syllable); i += 1){ + const char *Syllable = SpellSyllable[Spell->Syllable[i]]; + if(Syllable[0] == 0){ + break; + } + + // TODO(fusion): Review. I think we don't add a space between the first + // and second syllable and it's probably correct. + if(i >= 2){ + strcat(Text, " "); + } + + strcat(Text, Syllable); + } +} + +void GetMagicItemDescription(Object Obj, char *SpellString, int *MagicLevel){ + SpellString[0] = 0; + *MagicLevel = 0; + + if(!Obj.exists()){ + error("GetMagicItemDescription: Übergebenes Objekt existiert nicht.\n"); + return; + } + + ObjectType ObjType = Obj.getObjectType(); + if(!ObjType.getFlag(RUNE)){ + error("GetMagicItemDescription: Übergebenes Objekt ist nicht magisch.\n"); + return; + } + + int SpellNr = FindSpell(Obj); + if(SpellNr == 0){ + error("GetMagicItemDescription: Objekt %d hat keinen Zauberspruch.\n", ObjType.TypeID); + return; + } + + GetSpellString(SpellNr, SpellString); + *MagicLevel = (int)SpellList[SpellNr].RuneLevel; +} + +void GetSpellbook(uint32 CharacterID, char *Buffer){ + TPlayer *Player = GetPlayer(CharacterID); + if(Player == NULL){ + error("GetSpellbook: Spieler existiert nicht.\n"); + return; + } + + if(Buffer == NULL){ + error("GetSpellbook: Übergebener Puffer existiert nicht.\n"); + return; + } + + // TODO(fusion): We're iterating over all spells for each level. This is bad + // because we could have a list of spell numbers sorted by level and achieve + // the same thing. We also had a hard coded max level but since we're already + // iterating so many times over the spell list, what's another one? + // Also, the overuse of `strcat` doesn't help. + + int MaxLevel = 1; + for(int SpellNr = 0; + SpellNr < NARRAY(SpellList); + SpellNr += 1){ + int Level = (int)SpellList[SpellNr].Level; + if(Level > MaxLevel){ + Level = MaxLevel; + } + } + + char Help[256]; + for(int Level = 1; Level <= MaxLevel; Level += 1){ + bool First = true; + for(int SpellNr = 0; + SpellNr < NARRAY(SpellList); + SpellNr += 1){ + TSpellList *Spell = &SpellList[SpellNr]; + if((int)Spell->Level != Level || !Player->SpellKnown(SpellNr)){ + continue; + } + + if(First){ + sprintf(Help, "Spells for Level %d\n", Level); + strcat(Buffer, Help); + First = false; + } + + GetSpellString(SpellNr, Help); + if(Help[0] == 0){ + error("GetSpellbook: Zauberspruch %d hat keine Zauberformel.\n", SpellNr); + continue; + } + + strcat(Buffer, " "); + strcat(Buffer, Help); + if(Spell->Comment != NULL && Spell->Comment[0] != 0){ + strcat(Buffer, " - "); + strcat(Buffer, Spell->Comment); + if(SpellNr == 9 || SpellNr == 12){ + strcpy(Help, ": var"); + }else if(SpellNr == 80){ + strcpy(Help, ": 4*Level"); + }else{ + sprintf(Help, ": %d", Spell->Mana); + } + strcat(Buffer, Help); + } + strcat(Buffer, "\n"); + } + + if(!First){ + strcat(Buffer, "\n"); + } + } +} + +int GetSpellLevel(int SpellNr){ + if(SpellNr < 1 || SpellNr >= NARRAY(SpellList)){ + error("GetSpellLevel: Ungültige Spruchnummer %d.\n", SpellNr); + return 1; + } + + return (int)SpellList[SpellNr].Level; +} + +int CheckForSpell(uint32 CreatureID, const char *Text){ + // IMPORTANT(fusion): The first syllable also determines the type of the spell + // which is why we can quickly rule out a spell cast if it is unknown. + // It is also handled separately in the parsing loop below to allow for the + // second syllable to be glued together (or not). + int SpellType = TypeOfSpell(Text); + if(SpellType == 0){ + return 0; + } + + // TODO(fusion): Keeping syllables in text form doesn't make sense. `SpellStr` + // should ideally only contain parameters. + + // TODO(fusion): Did we really need to use `stringstream` here? It allocates + // an internal `std::string` from the input string which is just wasteful, + // specially for the minimal processing we're doing here. + + int SyllableCount = 1; + uint8 Syllable[10] = { (uint8)SpellType }; + char SpellStr[10][512] = {}; + strcpy(SpellStr[0], SpellSyllable[SpellType]); + + std::istringstream IS(Text); + IS.get(); + IS.get(); + while(!IS.eof()){ + while(isSpace(IS.peek())){ + IS.get(); + } + + int Index = SyllableCount; + if(IS.peek() == '"'){ + IS.get(); + IS.get(SpellStr[Index], sizeof(SpellStr[0]), '"'); + IS.get(); + }else{ + IS.get(SpellStr[Index], sizeof(SpellStr[0]), ' '); + } + + // TODO(fusion): This could be a problem if there is a "" parameter? + if(SpellStr[Index][0] == 0){ + break; + } + + for(int SyllableNr = 0; + SyllableNr < NARRAY(SpellSyllable); + SyllableNr += 1){ + if(stricmp(SpellStr[Index], SpellSyllable[SyllableNr]) == 0){ + Syllable[Index] = (uint8)SyllableNr; + break; + } + } + + // NOTE(fusion): SpellSyllable[6] is "para" which refers to a spell + // parameter. This is setting up `Syllable` to be used by `FindSpell`. + if(Syllable[Index] == 0){ + Syllable[Index] = 6; + } + + SyllableCount += 1; + } + + int SpellNr = FindSpell(Syllable); + if(SpellNr != 0){ + try{ + switch(SpellType){ + case 1: CharacterRightSpell(CreatureID, SpellNr, SpellStr); break; + case 2: RuneSpell(CreatureID, SpellNr); break; + case 3: CastSpell(CreatureID, SpellNr, SpellStr); break; + case 4: CastSpell(CreatureID, SpellNr, SpellStr); break; + case 5: AccountRightSpell(CreatureID, SpellNr, SpellStr); break; + default:{ + error("CheckForSpell: Spruchklasse %d existiert nicht.\n", SpellType); + break; + } + } + }catch(RESULT r){ + // TODO(fusion): `SpellFailed` is inlined in here but I think it's + // cleaner to keep it this way. + TCreature *Actor = GetCreature(CreatureID); + if(Actor == NULL){ + error("SpellFailed: Kreatur existiert nicht.\n"); + }else{ + if(r != ERROR){ + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_POFF); + } + + if(Actor->Type == PLAYER){ + SendResult(Actor->Connection, r); + } + } + + return -SpellType; + } + } + + return SpellType; +} + +static void DeleteRune(Object Obj){ + if(!Obj.exists()){ + error("DeleteRune: Übergebenes Objekt existiert nicht.\n"); + throw ERROR; + } + + // TODO(fusion): Should probably check if object is a rune? + uint32 Charges = Obj.getAttribute(CHARGES); + if(Charges > 1){ + Change(Obj, CHARGES, Charges - 1); + }else{ + Delete(Obj, -1); + } +} + +void UseMagicItem(uint32 CreatureID, Object Obj, Object Dest){ + TPlayer *Actor = GetPlayer(CreatureID); + if(Actor == NULL){ + error("UseMagicItem: Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(!Obj.exists()){ + error("UseMagicItem: Übergebenes Objekt existiert nicht.\n"); + throw ERROR; + } + + if(!Dest.exists()){ + error("UseMagicItem: Übergebenes Ziel existiert nicht (Objekt %d).\n", + Obj.getObjectType().TypeID); + throw ERROR; + } + + if(CheckRight(Actor->ID, NO_RUNES)){ + throw NOTUSABLE; + } + + int SpellNr = FindSpell(Obj); + if(SpellNr == 0){ + error("UseMagicItem: Für Objekt %d existiert kein Spruch.\n", + Obj.getObjectType().TypeID); + throw ERROR; + } + + + // NOTE(fusion): This target picking logic is probably to avoid picking + // obviously wrong candidates when there are multiple creatures on a single + // field. + TCreature *Target = NULL; + bool Aggressive = IsAggressiveSpell(SpellNr); + { + if(Dest.getObjectType().isCreatureContainer()){ + Target = GetCreature(Dest); + } + + Object Other = GetFirstContainerObject(Dest.getContainer()); + while(Other != NONE){ + if(Other.getObjectType().isCreatureContainer()){ + uint32 OtherID = Other.getCreatureID(); + if(Target == NULL + || (Aggressive && OtherID != Actor->ID) + || (!Aggressive && OtherID == Actor->ID)){ + Target = GetCreature(OtherID); + Dest = Other; + } + } + Other = Other.getNextObject(); + } + } + + // NOTE(fusion): Rune check. + CheckRuneLevel(Actor, SpellNr); + + if(Actor->EarliestSpellTime > ServerMilliseconds){ + throw EXHAUSTED; + } + + if(Aggressive){ + int DestX, DestY, DestZ; + GetObjectCoordinates(Dest, &DestX, &DestY, &DestZ); + if(!CheckRight(Actor->ID, ATTACK_EVERYWHERE) + && (IsProtectionZone(Actor->posx, Actor->posy, Actor->posz) + || IsProtectionZone(DestX, DestY, DestZ))){ + throw PROTECTIONZONE; + } + + if(Dest.getContainer().getObjectType().isMapContainer()){ + throw NOROOM; + } + } + + try{ + switch(SpellNr){ + case 4:{ + if(Target == NULL){ + throw NOCREATURE; + } + + int Amount = ComputeDamage(Actor, SpellNr, 70, 30); + Heal(Target, -1, 0, Amount); // -1 ? + break; + } + + case 5:{ + if(Target == NULL){ + throw NOCREATURE; + } + + int Amount = ComputeDamage(Actor, SpellNr, 250, 30); + Heal(Target, -1, 0, Amount); // -1 ? + break; + } + + case 7:{ + int Damage = ComputeDamage(Actor, SpellNr, 15, 5); + Combat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE_BURST, + ANIMATION_FIRE, DAMAGE_ENERGY); + break; + } + + case 8:{ + int Damage = ComputeDamage(Actor, SpellNr, 30, 10); + Combat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE_BURST, + ANIMATION_FIRE, DAMAGE_ENERGY); + break; + } + + case 12:{ + if(Target == NULL){ + throw NOCREATURE; + } + + Convince(Actor, Target); + break; + } + + case 14:{ + ObjectIllusion(Actor, 0, 0, Dest, 200); + break; + } + + case 15:{ + int Damage = ComputeDamage(Actor, SpellNr, 20, 5); + MassCombat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE, + 3, DAMAGE_FIRE, ANIMATION_FIRE); + break; + } + + case 16:{ + int Damage = ComputeDamage(Actor, SpellNr, 50, 15); + MassCombat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE, + 4, DAMAGE_FIRE, ANIMATION_FIRE); + break; + } + + case 17:{ + MassCreateField(Actor, Dest, 0, 0, FIELD_TYPE_FIRE, 3); + break; + } + + case 18:{ + int Damage = ComputeDamage(Actor, SpellNr, 60, 40); + MassCombat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE_EXPLOSION, + 1, DAMAGE_PHYSICAL, ANIMATION_FIRE); + break; + } + + case 21:{ + int Damage = ComputeDamage(Actor, SpellNr, 150, 20); + Combat(Actor, Dest, 0, 0, Damage, EFFECT_DEATH, + ANIMATION_DEATH, DAMAGE_PHYSICAL); + break; + } + + case 25:{ + CreateField(Actor, Dest, 0, 0, FIELD_TYPE_FIRE); + break; + } + + case 26:{ + CreateField(Actor, Dest, 0, 0, FIELD_TYPE_POISON); + break; + } + + case 27:{ + CreateField(Actor, Dest, 0, 0, FIELD_TYPE_ENERGY); + break; + } + + case 28:{ + CreateFieldWall(Actor, Dest, 0, 0, FIELD_TYPE_FIRE, 2); + break; + } + + case 30:{ + DeleteField(Actor, Dest, 0, 0); + break; + } + + case 31:{ + if(Target == NULL){ + throw NOCREATURE; + } + + NegatePoison(Actor, Target, 0, 0); + break; + } + + case 32:{ + CreateFieldWall(Actor, Dest, 0, 0, FIELD_TYPE_POISON, 2); + break; + } + + case 33:{ + CreateFieldWall(Actor, Dest, 0, 0, FIELD_TYPE_ENERGY, 3); + break; + } + + case 50:{ + int Damage = ComputeDamage(Actor, SpellNr, 120, 20); + Combat(Actor, Dest, 0, 0, Damage, EFFECT_FIRE_HIT, + ANIMATION_FIRE, DAMAGE_FIRE_PERIODIC); + break; + } + + case 54:{ + if(Target == NULL){ + throw NOCREATURE; + } + + if(Actor->GetEffectiveProfession() != PROFESSION_DRUID){ + throw NOTUSABLE; + } + + int ManaPoints = SpellList[SpellNr].Mana; + int SoulPoints = SpellList[SpellNr].SoulPoints; + MagicGoStrength(Actor, Target, ManaPoints, SoulPoints, -101, 1); + break; + } + + case 55:{ + MassCreateField(Actor, Dest, 0, 0, FIELD_TYPE_ENERGY, 3); + break; + } + + case 77:{ + int Damage = ComputeDamage(Actor, SpellNr, 70, 20); + Combat(Actor, Dest, 0, 0, Damage, EFFECT_POISON_HIT, + ANIMATION_ENERGY, DAMAGE_POISON_PERIODIC); + break; + } + + case 78:{ + CleanupField(Actor, Dest, 0, 0); + break; + } + + case 83:{ + RaiseDead(Actor, Dest, 0, 0); + break; + } + + case 86:{ + CreateField(Actor, Dest, 0, 0, FIELD_TYPE_MAGICWALL); + break; + } + + case 91:{ + MassCreateField(Actor, Dest, 0, 0, FIELD_TYPE_POISON, 3); + break; + } + + default:{ + error("UseMagicItem: Spell %d noch nicht implementiert.\n", SpellNr); + throw ERROR; + } + } + }catch(RESULT r){ + // TODO(fusion): Same as with `CheckForSpell` except that `Actor` is + // already in scope. + if(r != ERROR){ + GraphicalEffect(Actor->posx, Actor->posy, Actor->posz, EFFECT_POFF); + } + SendResult(Actor->Connection, r); + return; + } + + DeleteRune(Obj); + + if(Aggressive){ + Actor->BlockLogout(60, false); + } +} + +void DrinkPotion(uint32 CreatureID, Object Obj){ + TPlayer *Player = GetPlayer(CreatureID); + if(Player == NULL){ + error("DrinkPotion: Kreatur existiert nicht.\n"); + throw ERROR; + } + + if(!Obj.exists()){ + error("DrinkPotion: Übergebenes Objekt existiert nicht.\n"); + throw ERROR; + } + + ObjectType ObjType = Obj.getObjectType(); + if(!ObjType.getFlag(LIQUIDCONTAINER)){ + error("DrinkPotion: Übergebenes Objekt ist kein Flüssigkeitscontainer.\n"); + throw ERROR; + } + + int LiquidType = (int)Obj.getAttribute(CONTAINERLIQUIDTYPE); + if(LiquidType == LIQUID_MANA){ + int Amount = ComputeDamage(NULL, 0, 100, 50); + RefreshMana(Player, 0, 0, Amount); + }else if(LiquidType == LIQUID_LIFE){ + int Amount = ComputeDamage(NULL, 0, 50, 25); + Heal(Player, 0, 0, Amount); + }else{ + error("DrinkPotion: Objekt enthält keinen Zaubertrank.\n"); + throw ERROR; + } + + Change(Obj, CONTAINERLIQUIDTYPE, LIQUID_NONE); +} + // Magic Init Functions // ============================================================================= static void InitCircles(void){ diff --git a/src/magic.hh b/src/magic.hh index 0525ad2..e1dc354 100644 --- a/src/magic.hh +++ b/src/magic.hh @@ -4,6 +4,14 @@ #include "common.hh" #include "creature.hh" +enum : int { + FIELD_TYPE_FIRE = 1, + FIELD_TYPE_POISON = 2, + FIELD_TYPE_ENERGY = 3, + FIELD_TYPE_MAGICWALL = 4, + FIELD_TYPE_WILDGROWTH = 5, +}; + struct TImpact{ // VIRTUAL FUNCTIONS // ========================================================================= @@ -89,25 +97,105 @@ struct TSummonImpact: TImpact { int Maximum; }; -struct TCircle { - int x[32]; - int y[32]; - int Count; -}; - -struct TSpellList { - uint8 Syllable[10]; - uint8 RuneGr; - uint8 RuneNr; - const char *Comment; - uint16 Level; - uint16 RuneLevel; - uint16 Flags; - int Mana; - int SoulPoints; - int Amount; -}; +void ActorShapeSpell(TCreature *Actor, TImpact *Impact, int Effect); +void VictimShapeSpell(TCreature *Actor, TCreature *Victim, + int Range, int Animation, TImpact *Impact, int Effect); +void OriginShapeSpell(TCreature *Actor, int Radius, TImpact *Impact, int Effect); +void CircleShapeSpell(TCreature *Actor, int DestX, int DestY, int DestZ, + int Range, int Animation, int Radius, TImpact *Impact, int Effect); +void DestinationShapeSpell(TCreature *Actor, TCreature *Victim, + int Range, int Animation, int Radius, TImpact *Impact, int Effect); +void AngleShapeSpell(TCreature *Actor, int Angle, int Range, TImpact *Impact, int Effect); +void CheckSpellbook(TCreature *Actor, int SpellNr); +void CheckAccount(TCreature *Actor, int SpellNr); +void CheckLevel(TCreature *Actor, int SpellNr); +void CheckRuneLevel(TCreature *Actor, int SpellNr); +void CheckMagicItem(TCreature *Actor, ObjectType Type); +void CheckRing(TCreature *Actor, int SpellNr); +void CheckAffectedPlayers(TCreature *Actor, int x, int y, int z); +void CheckMana(TCreature *Actor, int ManaPoints, int SoulPoints, int Delay); +int ComputeDamage(TCreature *Actor, int SpellNr, int Damage, int Variation); +bool IsAggressiveSpell(int SpellNr); +void MassCombat(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, + int Damage, int Effect, int Radius, int DamageType, int Animation); +void AngleCombat(TCreature *Actor, int ManaPoints, int SoulPoints, + int Damage, int Effect, int Range, int Angle, int DamageType); +void Combat(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, + int Damage, int Effect, int Animation, int DamageType); +int GetDirection(int dx, int dy); // TODO(fusion): Move this one elsewhere? Maybe `info.cc`. +void KillAllMonsters(TCreature *Actor, int Effect, int Radius); +void CreateField(int x, int y, int z, int FieldType, uint32 Owner, bool Peaceful); +void CreateField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int FieldType); +void CreateField(TCreature *Actor, int ManaPoints, int SoulPoints, int FieldType); +void MassCreateField(TCreature *Actor, Object Target, + int ManaPoints, int SoulPoints, int FieldType, int Radius); +void CreateFieldWall(TCreature *Actor, Object Target, + int ManaPoints, int SoulPoints, int FieldType, int Width); +void DeleteField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints); +void CleanupField(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints); +void CleanupField(TCreature *Actor); +void Teleport(TCreature *Actor, const char *Param); +void TeleportToCreature(TCreature *Actor, const char *Name); +void TeleportPlayerToMe(TCreature *Actor, const char *Name); +void MagicRope(TCreature *Actor, int ManaPoints, int SoulPoints); +void MagicClimbing(TCreature *Actor, int ManaPoints, int SoulPoints, const char *Param); +void MagicClimbing(TCreature *Actor, const char *Param); +void CreateThing(TCreature *Actor, const char *Param1, const char *Param2); +void CreateMoney(TCreature *Actor, const char *Param); +void CreateFood(TCreature *Actor, int ManaPoints, int SoulPoints); +void CreateArrows(TCreature *Actor, int ManaPoints, int SoulPoints, int ArrowType, int Count); +void SummonCreature(TCreature *Actor, int ManaPoints, int Race, bool God); +void SummonCreature(TCreature *Actor, int ManaPoints, const char *RaceName, bool God); +void StartMonsterraid(TCreature *Actor, const char *RaidName); +void RaiseDead(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints); +void MassRaiseDead(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Radius); +void Heal(TCreature *Actor, int ManaPoints, int SoulPoints, int Amount); +void MassHeal(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Amount, int Radius); +void HealFriend(TCreature *Actor, const char *TargetName, int ManaPoints, int SoulPoints, int Amount); +void RefreshMana(TCreature *Actor, int ManaPoints, int SoulPoints, int Amount); +void MagicGoStrength(TCreature *Actor, TCreature *Target, int ManaPoints, int SoulPoints, int Percent, int Duration); +void Shielding(TCreature *Actor, int ManaPoints, int SoulPoints, int Duration); +void NegatePoison(TCreature *Actor, TCreature *Target, int ManaPoints, int SoulPoints); +void Enlight(TCreature *Actor, int ManaPoints, int SoulPoints, int Radius, int Duration); +void Invisibility(TCreature *Actor, int ManaPoints, int SoulPoints, int Duration); +void CancelInvisibility(TCreature *Actor, Object Target, int ManaPoints, int SoulPoints, int Radius); +void CreatureIllusion(TCreature *Actor, int ManaPoints, int SoulPoints, const char *RaceName, int Duration); +void ObjectIllusion(TCreature *Actor, int ManaPoints, int SoulPoints, Object Target, int Duration); +void ChangeData(TCreature *Actor, const char *Param); +void EnchantObject(TCreature *Actor, int ManaPoints, int SoulPoints, ObjectType OldType, ObjectType NewType); +void Convince(TCreature *Actor, TCreature *Target); +void Challenge(TCreature *Actor, int ManaPoints, int SoulPoints, int Radius); +void FindPerson(TCreature *Actor, int ManaPoints, int SoulPoints, const char *TargetName); +void GetPosition(TCreature *Actor); +void GetQuestValue(TCreature *Actor, const char *Param); +void SetQuestValue(TCreature *Actor, const char *Param1, const char *Param2); +void ClearQuestValues(TCreature *Actor); +void CreateKnowledge(TCreature *Actor, const char *Param1, const char *Param2); +void ChangeProfession(TCreature *Actor, const char *Param); +void EditGuests(TCreature *Actor); +void EditSubowners(TCreature *Actor); +void EditNameDoor(TCreature *Actor); +void KickGuest(TCreature *Actor, const char *GuestName); +void Notation(TCreature *Actor, const char *Name, const char *Comment); +void NameLock(TCreature *Actor, const char *Name); +void BanishAccount(TCreature *Actor, const char *Name, int Duration, const char *Reason); +void DeleteAccount(TCreature *Actor, const char *Name, const char *Reason); +void BanishCharacter(TCreature *Actor, const char *Name, int Duration, const char *Reason); +void DeleteCharacter(TCreature *Actor, const char *Name, const char *Reason); +void IPBanishment(TCreature *Actor, const char *Name, const char *Reason); +void SetNameRule(TCreature *Actor, const char *Name); +void KickPlayer(TCreature *Actor, const char *Name); +void HomeTeleport(TCreature *Actor, const char *Name); + +// TODO(fusion): These are unsafe like strcpy. +void GetMagicItemDescription(Object Obj, char *SpellString, int *MagicLevel); +void GetSpellbook(uint32 CharacterID, char *Buffer); + +int GetSpellLevel(int SpellNr); + +int CheckForSpell(uint32 CreatureID, const char *Text); +void UseMagicItem(uint32 CreatureID, Object Obj, Object Dest); +void DrinkPotion(uint32 CreatureID, Object Obj); -void CheckMana(TCreature *Creature, int ManaPoints, int SoulPoints, int Delay); #endif //TIBIA_MAGIC_HH_ diff --git a/src/main.cc b/src/main.cc index d3a0cbd..3925b04 100644 --- a/src/main.cc +++ b/src/main.cc @@ -541,8 +541,8 @@ int main(int argc, char **argv){ // once we identify all throw statements and how to roughly handle them. try{ LaunchGame(); - }catch(RESULT result){ - error("main: Nicht abgefangene Exception %d.\n", result); + }catch(RESULT r){ + error("main: Nicht abgefangene Exception %d.\n", r); }catch(const char *str){ error("main: Nicht abgefangene Exception \"%s\".\n", str); }catch(const std::exception &e){ diff --git a/src/stubs.hh b/src/stubs.hh index 6013a17..a1b451a 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -22,15 +22,12 @@ extern void Change(Object Obj, ObjectType NewType, uint32 Value); extern void CharacterDeathOrder(TCreature *Creature, int OldLevel, uint32 Offender, const char *Remark, bool Unjustified); extern bool CheckRight(uint32 CreatureID, RIGHT Right); -extern void CircleShapeSpell(TCreature *Actor, int DestX, int DestY, int DestZ, - int Range, int Animation, int Radius, TImpact *Impact, int Effect); extern void CleanHouseField(int x, int y, int z); -extern int ComputeDamage(TCreature *Creature, int SpellNr, int Damage, int Variation); extern void ConvinceMonster(TCreature *Master, TCreature *Slave); extern void ChallengeMonster(TCreature *Challenger, TCreature *Monster); extern int CountInventoryObjects(uint32 CreatureID, ObjectType Type, uint32 Value); +extern Object Create(Object Con, ObjectType Type, uint32 Value); extern Object CreateAtCreature(uint32 CreatureID, ObjectType Type, uint32 Value); -extern void CreateField(int x, int y, int z, int FieldType, uint32 Owner, bool Peaceful); extern void CreateMonster(int Race, int x, int y, int z, int Home, uint32 Master, bool ShowEffect); extern void CreatePlayerList(bool Online); extern void CreatePool(Object Con, ObjectType Type, uint32 Value); @@ -92,6 +89,7 @@ extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ... extern void SendPlayerData(TConnection *Connection); extern void SendPlayerSkills(TConnection *Connection); extern void SendPlayerState(TConnection *Connection, uint8 State); +extern void SendResult(TConnection *Connection, RESULT r); extern void ShowGuestList(uint16 HouseID, TPlayer *Player, char *Buffer); extern void ShowSubownerList(uint16 HouseID, TPlayer *Player, char *Buffer); extern void ShowNameDoor(Object Door, TPlayer *Player, char *Buffer); -- cgit v1.2.3