diff options
| -rw-r--r-- | TODO.md | 3 | ||||
| -rw-r--r-- | reference/game.c | 1473 | ||||
| -rw-r--r-- | src/crcombat.cc | 15 | ||||
| -rw-r--r-- | src/creature.cc | 4 | ||||
| -rw-r--r-- | src/enums.hh | 17 | ||||
| -rw-r--r-- | src/magic.cc | 1235 | ||||
| -rw-r--r-- | src/magic.hh | 126 | ||||
| -rw-r--r-- | src/main.cc | 4 | ||||
| -rw-r--r-- | src/stubs.hh | 6 |
9 files changed, 1309 insertions, 1574 deletions
@@ -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<true,0>::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 <fstream> +#include <sstream> + +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); |
