/* * magic.cpp * Additional spell-casting routines. * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ #include "mud.h" #include "magic.h" // Spells with -1 mp will not check mp and will leave it up to // the spell function to do this check. // The order of this array is important! "splno" must equal the // spot of the struct in the array. The mud will check this on // startup and will crash if it's not properly defined struct { const char *splstr; int splno; int (*splfn)(SpellFn); int mp; SchoolOfMagic school; } spllist[] = { // Name Spell Num Spell Function Mana School of Magic { "vigor", S_VIGOR, splVigor, 2, HEALING }, { "zephyr", S_ZEPHYR, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "infravision", S_INFRAVISION, splInfravision, -1, TRANSMUTATION }, { "slow-poison", S_SLOW_POISON, splSlowPoison, 8, HEALING }, { "bless", S_BLESS, splBless, 10, ABJURATION }, { "protection", S_PROTECTION, splProtection, 10, ABJURATION }, { "fireball", S_FIREBALL, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "invisibility", S_INVISIBILITY, splInvisibility, 15, ILLUSION }, { "restore", S_RESTORE, splRestore, -1, HEALING }, { "detect-invisibility",S_DETECT_INVISIBILITY, splDetectInvisibility, 10, DIVINATION }, { "detect-magic", S_DETECT_MAGIC, splDetectMagic, 10, DIVINATION }, { "teleport", S_TELEPORT, splTeleport, -1, TRANSLOCATION }, { "stun", S_STUN, splStun, 10, ENCHANTMENT }, { "cyclone", S_CYCLONE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "atrophy", S_ATROPHY, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "enchant", S_ENCHANT, splEnchant, 25, ENCHANTMENT }, { "word-of-recall", S_WORD_OF_RECALL, splWordOfRecall, 25, TRANSLOCATION }, { "summon", S_SUMMON, splSummon, 28, TRANSLOCATION }, { "mend-wounds", S_MEND_WOUNDS, splMendWounds, 4, HEALING }, { "heal", S_HEAL, splHeal, 20, HEALING }, { "track", S_TRACK, splTrack, 13, TRANSLOCATION }, { "levitate", S_LEVITATE, splLevitate, 10, TRANSMUTATION }, { "heat-protection", S_HEAT_PROTECTION, splHeatProtection, 12, ABJURATION }, { "fly", S_FLY, splFly, 15, TRANSMUTATION }, { "resist-magic", S_RESIST_MAGIC, splResistMagic, 25, ABJURATION }, { "whirlwind", S_WHIRLWIND, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "rumble", S_RUMBLE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "burn", S_BURN, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "blister", S_BLISTER, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "dustgust", S_DUSTGUST, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "waterbolt", S_WATERBOLT, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "crush", S_CRUSH, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "shatterstone", S_ENGULF, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "burstflame", S_BURSTFLAME, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "steamblast", S_STEAMBLAST, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "engulf", S_SHATTERSTONE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "immolate", S_IMMOLATE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "bloodboil", S_BLOODBOIL, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "tempest", S_TEMPEST, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "earth-tremor", S_EARTH_TREMOR, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "flamefill", S_FLAMEFILL, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "know-aura", S_KNOW_AURA, splKnowAura, 6, DIVINATION }, { "remove-curse", S_REMOVE_CURSE, splRemoveCurse, 18, ABJURATION }, { "resist-cold", S_RESIST_COLD, splResistCold, 13, ABJURATION }, { "breathe-water", S_BREATHE_WATER, splBreatheWater, 12, TRANSMUTATION }, { "earth-shield", S_STONE_SHIELD, splEarthShield, 12, ABJURATION }, { "clairvoyance", S_CLAIRVOYANCE, splClairvoyance, 15, DIVINATION }, { "drain-exp", S_DRAIN_EXP, drain_exp, -1, NECROMANCY }, { "cure-disease", S_CURE_DISEASE, splCureDisease, 12, HEALING }, { "cure-blindness", S_CURE_BLINDNESS, splCureBlindness, 15, HEALING }, { "fear", S_FEAR, splFear, 15, ENCHANTMENT }, { "room-vigor", S_ROOM_VIGOR, splRoomVigor, 8, HEALING }, { "transport", S_TRANSPORT, splTransport, -1, TRANSLOCATION }, { "blind", S_BLINDNESS, splBlind, 20, TRANSMUTATION }, { "silence", S_SILENCE, splSilence, -1, ENCHANTMENT }, { "fortune", S_FORTUNE, splFortune, 12, DIVINATION }, { "curse", S_CURSE, splCurse, 25, ENCHANTMENT }, { "earthquake", S_EARTHQUAKE, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "tsunami", S_FLOOD, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "inferno", S_FIRESTORM, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "hurricane", S_HURRICANE, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "conjure", S_CONJURE, conjure, -1, CONJURATION }, { "plane-shift", S_PLANE_SHIFT, splPlaneShift, -1, TRANSLOCATION }, { "judgement", S_JUDGEMENT, splJudgement, -1, NO_SCHOOL }, { "resist-water", S_RESIST_WATER, splResistWater, 13, ABJURATION }, { "resist-fire", S_RESIST_FIRE, splResistFire, 13, ABJURATION }, { "resist-air", S_RESIST_AIR, splResistAir, 13, ABJURATION }, { "resist-earth", S_RESIST_EARTH, splResistEarth, 13, ABJURATION }, { "reflect-magic", S_REFLECT_MAGIC, splReflectMagic, 30, ABJURATION }, { "animate-dead", S_ANIMIATE_DEAD, animate_dead, -1, NECROMANCY }, { "annul-magic", S_ANNUL_MAGIC, splAnnulMagic, 30, ABJURATION }, { "true-sight", S_TRUE_SIGHT, splTrueSight, 18, DIVINATION }, { "remove-fear", S_REMOVE_FEAR, splRemoveFear, 10, ABJURATION }, { "remove-silence", S_REMOVE_SILENCE, splRemoveSilence, 15, ABJURATION }, { "camouflage", S_CAMOUFLAGE, splCamouflage, 15, ILLUSION }, { "ethereal-travel", S_ETHREAL_TRAVEL, splEtherealTravel, 31, TRANSLOCATION }, { "drain-shield", S_DRAIN_SHIELD, splDrainShield, 15, ABJURATION }, { "dispel-evil", S_DISPEL_EVIL, splDispelEvil, 25, ABJURATION }, { "dispel-good", S_DISPEL_GOOD, splDispelGood, 25, ABJURATION }, { "undead-ward", S_UNDEAD_WARD, splUndeadWard, 25, ABJURATION }, { "zap", S_ZAP, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "lightning-bolt", S_LIGHTNING_BOLT, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "shockbolt", S_SHOCKBOLT, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "electrocution", S_ELECTROCUTE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "thunderbolt", S_THUNDERBOLT, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "chain-lightning", S_CHAIN_LIGHTNING, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "chill", S_CHILL, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "frostbite", S_FROSTBITE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "sleet", S_SLEET, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "cold-cone", S_COLD_CONE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "iceblade", S_ICEBLADE, (int (*)(SpellFn))splOffensive, -1, EVOCATION }, { "blizzard", S_BLIZZARD, (int (*)(SpellFn))splMultiOffensive,-1, EVOCATION }, { "resist-electric", S_RESIST_ELEC, splResistLightning, 13, ABJURATION }, { "warmth", S_WARMTH, splWarmth, 12, ABJURATION }, { "cure-poison", S_CURE_POISON, splCurePoison, 10, HEALING }, { "haste", S_HASTE, splHaste, 30, TRANSMUTATION }, { "slow", S_SLOW, splSlow, 30, TRANSMUTATION }, { "comprehend-languages",S_COMPREHEND_LANGUAGES,splComprehendLanguages, 20, DIVINATION }, { "stone-to-flesh", S_STONE_TO_FLESH, splStoneToFlesh, 20, TRANSMUTATION }, { "scare", S_SCARE, splScare, 25, ENCHANTMENT }, { "hold-person", S_HOLD_PERSON, splHoldPerson, 20, ENCHANTMENT }, { "entangle", S_ENTANGLE, splEntangle, 20, TRANSMUTATION }, { "anchor", S_DIMENSIONAL_ANCHOR, splDimensionalAnchor, -1, TRANSLOCATION }, { "strength", S_STRENGTH, splStrength, 30, TRANSMUTATION }, { "enfeeblement", S_ENFEEBLEMENT, splEnfeeblement, 30, TRANSMUTATION }, { "armor", S_ARMOR, splArmor, -1, ABJURATION }, { "stoneskin", S_STONESKIN, splStoneskin, -1, ABJURATION }, { "free-action", S_FREE_ACTION, splFreeAction, 15, ABJURATION }, { "rejuvenate", S_REJUVENATE, splRejuvenate, -1, HEALING }, { "disintegrate", S_DISINTEGRATE, splDisintegrate, 50, TRANSMUTATION }, { "resurrect", S_RESURRECT, splResurrect, -1, HEALING }, { "courage", S_COURAGE, splCourage, 20, ENCHANTMENT }, { "bloodfusion", S_BLOODFUSION, splBloodfusion, -1, HEALING }, { "magic-missile", S_MAGIC_MISSILE, splMagicMissile, -1, EVOCATION }, { "knock", S_KNOCK, splKnock, -1, TRANSMUTATION }, { "blink", S_BLINK, splBlink, 20, TRANSLOCATION }, { "harm", S_HARM, splHarm, 30, NECROMANCY }, { "bind", S_BIND, splBind, 25, TRANSLOCATION }, { "darkness", S_DARKNESS, splDarkness, -1, EVOCATION }, { "tongues", S_TONGUES, splTongues, 25, DIVINATION }, { "enlarge", S_ENLARGE, splEnlarge, 15, TRANSMUTATION }, { "reduce", S_REDUCE, splReduce, 15, TRANSMUTATION }, { "insight", S_INSIGHT, splInsight, 30, TRANSMUTATION }, { "feeblemind", S_FEEBLEMIND, splFeeblemind, 30, TRANSMUTATION }, { "prayer", S_PRAYER, splPrayer, 30, TRANSMUTATION }, { "damnation", S_DAMNATION, splDamnation, 30, TRANSMUTATION }, { "fortitude", S_FORTITUDE, splFortitude, 30, TRANSMUTATION }, { "weakness", S_WEAKNESS, splWeakness, 30, TRANSMUTATION }, { "pass-without-trace", S_PASS_WITHOUT_TRACE, splPassWithoutTrace, 8, TRANSMUTATION }, { "farsight", S_FARSIGHT, splFarsight, 10, DIVINATION }, { "wind-protection", S_WIND_PROTECTION, splWindProtection, 12, ABJURATION }, { "static-field", S_STATIC_FIELD, splStaticField, 12, ABJURATION }, { "portal", S_PORTAL, splPortal, 75, TRANSLOCATION }, // anything after this line has been coded for the new magic system and needs player-testing { "detect-hidden", S_DETECT_HIDDEN, splDetectHidden, 5, DIVINATION }, { "illusion", S_ILLUSION, splIllusion, 25, ILLUSION }, { "deafness", S_DEAFNESS, splDeafness, 30, TRANSMUTATION }, { "radiation", S_RADIATION, splRadiation, 6, ABJURATION }, { "fiery-retribution", S_FIERY_RETRIBUTION, splFieryRetribution, 12, ABJURATION }, { "aura-of-flame", S_AURA_OF_FLAME, splAuraOfFlame, 18, ABJURATION }, { "barrier-of-combustion",S_BARRIER_OF_COMBUSTION,splBarrierOfCombustion, 24, ABJURATION }, { "bounce-magic", S_BOUNCE_MAGIC, splBounceMagic, 10, ABJURATION }, { "rebound-magic", S_REBOUND_MAGIC, splReboundMagic, 20, ABJURATION }, { "dense-fog", S_DENSE_FOG, splDenseFog, 10, CONJURATION }, { "wall-of-fire", S_WALL_OF_FIRE, splWallOfFire, 30, CONJURATION }, { "wall-of-force", S_WALL_OF_FORCE, splWallOfForce, 50, CONJURATION }, { "wall-of-thorns", S_WALL_OF_THORNS, splWallOfThorns, 30, CONJURATION }, { "greater-invisibility",S_GREATER_INVIS, splGreaterInvisibility, 30, ILLUSION }, { "hallow", S_HALLOW, splHallow, 20, NO_SCHOOL }, { "unhallow", S_UNHALLOW, splUnhallow, 20, NO_SCHOOL }, { "toxic-cloud", S_TOXIC_CLOUD, splToxicCloud, 20, CONJURATION }, { "blur", S_BLUR, splBlur, 10, ILLUSION }, { "illusory-wall", S_ILLUSORY_WALL, splIllusoryWall, 24, ILLUSION }, { "globe-of-silence", S_GLOBE_OF_SILENCE, splGlobeOfSilence, 45, ENCHANTMENT }, { "cancel-magic", S_CANCEL_MAGIC, splCancelMagic, 15, ABJURATION }, { "dispel-magic", S_DISPEL_MAGIC, splDispelMagic, 20, ABJURATION }, { "sap-life", S_SAP_LIFE, splNecroDrain, -1, NECROMANCY }, { "lifetap", S_LIFETAP, splNecroDrain, -1, NECROMANCY }, { "lifedraw", S_LIFEDRAW, splNecroDrain, -1, NECROMANCY }, { "draw-spirit", S_DRAW_SPIRIT, splNecroDrain, -1, NECROMANCY }, { "siphon-life", S_SIPHON_LIFE, splNecroDrain, -1, NECROMANCY }, { "spirit-strike", S_SPIRIT_STRIKE, splNecroDrain, -1, NECROMANCY }, { "soulsteal", S_SOULSTEAL, splNecroDrain, -1, NECROMANCY }, { "touch-of-kesh", S_TOUCH_OF_KESH, splNecroDrain, -1, NECROMANCY }, { "@", -1, 0, 0, NO_SCHOOL } }; int spllist_size = sizeof(spllist)/sizeof(*spllist); //********************************************************************** // saveSpells //********************************************************************** // this creates spells.xml, which the Web Editor requests when it wants // to know what spells it can use bool Config::saveSpells() const { xmlDocPtr xmlDoc; xmlNodePtr rootNode, curNode; char filename[80]; xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Spells", NULL); xmlDocSetRootElement(xmlDoc, rootNode); for(int i=0 ; i < spllist_size-1; i++) { curNode = xml::newStringChild(rootNode, "Spell", spllist[i].splstr); xml::newNumProp(curNode, "Id", i+1); } sprintf(filename, "%s/spells.xml", CONFPATH); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(true); } //********************************************************************** // initSpellList //********************************************************************** // Make sure someone hasn't messed up the spell table :) void initSpellList() { for(int i=0 ; i<spllist_size; i++) { if(spllist[i].splno != i) { if(spllist[i].splno != -1) { printf("Error: Spell list mismatch: %d != %d\n", i, spllist[i].splno); abort(); } } } } //********************************************************************* // get_spell_name //********************************************************************* const char *get_spell_name(int nIndex) { // do bounds checking ASSERTLOG(nIndex >= 0); ASSERTLOG(nIndex < spllist_size); nIndex = MAX(0, MIN(nIndex, spllist_size)); return( spllist[nIndex].splstr ); } //********************************************************************* // get_spell_num() //********************************************************************* int get_spell_num(int nIndex) { // do bounds checking ASSERTLOG(nIndex >= 0); ASSERTLOG(nIndex < spllist_size); nIndex = MAX(0, MIN(nIndex, spllist_size)); return(spllist[nIndex].splno); } //********************************************************************* // get_spell_lvl //********************************************************************* // this also serves as a list of offensive spells to cast during combat // (for pets - see Monster::castSpell) int get_spell_lvl(int sflag) { int slvl=0; ASSERTLOG(sflag >= 0 ); ASSERTLOG(sflag < spllist_size ); switch(sflag) { case S_RUMBLE: case S_ZEPHYR: case S_BURN: case S_BLISTER: case S_ZAP: case S_CHILL: case S_SAP_LIFE: case S_LIFETAP: slvl = 1; break; case S_CRUSH: case S_DUSTGUST: case S_FIREBALL: case S_WATERBOLT: case S_LIGHTNING_BOLT: case S_FROSTBITE: case S_LIFEDRAW: slvl = 2; break; case S_ENGULF: case S_WHIRLWIND: case S_BURSTFLAME: case S_STEAMBLAST: case S_SHOCKBOLT: case S_SLEET: case S_DRAW_SPIRIT: slvl = 3; break; case S_SHATTERSTONE: case S_CYCLONE: case S_IMMOLATE: case S_BLOODBOIL: case S_ELECTROCUTE: case S_COLD_CONE: case S_SIPHON_LIFE: slvl = 4; break; case S_EARTH_TREMOR: case S_TEMPEST: case S_FLAMEFILL: case S_ATROPHY: case S_THUNDERBOLT: case S_ICEBLADE: case S_SPIRIT_STRIKE: slvl = 5; break; case S_EARTHQUAKE: case S_HURRICANE: case S_FIRESTORM: case S_FLOOD: case S_CHAIN_LIGHTNING: case S_BLIZZARD: case S_SOULSTEAL: case S_TOUCH_OF_KESH: slvl = 6; break; default: slvl = 0; break; } return(slvl); } //********************************************************************* // get_spell_function //********************************************************************* SpellRet get_spell_function(int nIndex) { // do bounds checking ASSERTLOG(nIndex >= 0); ASSERTLOG(nIndex < spllist_size); nIndex = MAX(0, MIN(nIndex, spllist_size)); return( spllist[nIndex].splfn ); } //********************************************************************* // get_spell_school //********************************************************************* SchoolOfMagic get_spell_school(int nIndex) { // do bounds checking ASSERTLOG(nIndex >= 0); ASSERTLOG(nIndex < spllist_size); nIndex = MAX(0, MIN(nIndex, spllist_size)); return( spllist[nIndex].school ); } //********************************************************************* // get_spell_list_size //********************************************************************* int get_spell_list_size() { return(spllist_size); } //********************************************************************* // getSpellMp //********************************************************************* // Parameters: <splNo> int getSpellMp(int spellNum) { // do bounds checking ASSERTLOG(spellNum >= 0); ASSERTLOG(spellNum < spllist_size); return(spllist[spellNum].mp); } //********************************************************************* // checkMp //********************************************************************* // Parameters: <target> The player casting // <reqMp> The required amount of mp // Return value: <true> - Has enough mp // <false> - Doesn't have enough mp bool Creature::checkMp(int reqMp) { if(getClass() != LICH && mp.getCur() < reqMp) { if(!isPet()) print("You need %d magic points to cast that spell.\n", reqMp); else print("%M needs %d magic points to cast that spell.\n", this, reqMp); return(false); } else if(getClass() == LICH && hp.getCur() <= reqMp) { if(!isPet()) print("Sure, and kill yourself in the process?\n"); else print("Sure, and have %N kill %sself in the process?\n", this, himHer()); return(false); } return(true); } //********************************************************************* // subMp //********************************************************************* // Parameters: <target> The player casting // <reqMp> The amount of mp to deduct void Creature::subMp(int reqMp) { if(getClass() == LICH) { hp.decrease(reqMp); } else { mp.decrease(reqMp); } } //********************************************************************* // splLevitate //********************************************************************* bool checkRefusingMagic(Creature* player, Creature* target, bool healing, bool print) { if(player->isStaff() || !target->isPlayer()) return(false); // linkdead players always want healing if(!healing && target->flagIsSet(P_LINKDEAD)) { if(print) player->print("%M doesn't want that cast on them right now.\n", target); return(0); } if(target->getPlayer()->isRefusing(player->name)) { if(print) player->print("%M is refusing your magical services.\n", target); return(true); } return(false); }