{ Summary: Various spell related functions ## $Id: magic.pas,v 1.7 2004/04/10 22:24:03 druid Exp $ } unit magic; interface uses skills, chars; function findFunc(const s : string) : SPEC_FUNC; procedure magic_timer(ch, victim : GCharacter; sn : GSkill); implementation uses SysUtils, constants, area, dtypes, commands, mudsystem, player, util, fight; // TODO: Weird function URANGE(5, 50+, 5) always returns 5 imho - Nemesis function saving_throw(level, save : integer; vict: GCharacter) : boolean; var chance : integer; begin chance := 50 + (vict.level - level - save) * 5; chance := URANGE(5, chance, 5); Result := number_percent <= chance; end; // Spell - Acid Arrow (inflicts damage and poison) procedure spell_acid_arrow(ch, victim : GCharacter; sn : GSkill); var af : GAffect; begin if (saving_throw(ch.level, victim.save_poison, victim)) then begin act(AT_REPORT,'$N resisted the effects of your spell!', false, ch, nil, victim, TO_CHAR); damage(ch, victim, 40, cardinal(sn)); end else begin af := GAffect.Create(); af.name := sn.name; af.wear_msg := 'The poison slowly wears off.'; af.duration := (ch.level div 8); setLength(af.modifiers, 1); af.modifiers[0].apply_type := APPLY_AFFECT; af.modifiers[0].modifier := AFF_POISON; af.applyTo(victim); damage(ch, victim, 55, cardinal(sn)); end; end; // Spell - Burning Hands (damage) procedure spell_burning_hands(ch,victim:GCharacter; sn : GSkill); begin damage(ch, victim, 45, cardinal(sn)); end; // Spell - Lightning (damage) procedure spell_lightning(ch, victim : GCharacter; sn : GSkill); begin act(AT_SPELL,'Your hands burst into lightning!', false, ch, nil, nil, TO_CHAR); act(AT_SPELL,'Your ears pop as $n releases $s lightning!', false, ch, nil, nil, TO_ROOM); damage(ch, victim, 110, cardinal(sn)); end; // Spell - Magic Missile (damage) procedure spell_magic_missile(ch, victim : GCharacter; sn : GSkill); begin damage(ch, victim, 35, cardinal(sn)); end; // Spell - Poison (adds poison) procedure spell_poison(ch, victim : GCharacter; sn : GSkill); var af : GAffect; begin if saving_throw(ch.level, victim.save_poison, victim) then begin act(AT_REPORT,'Your spell failed!', false, ch, nil, victim, TO_CHAR); act(AT_REPORT,'You resisted the effects of $n''s poison!', false, ch, nil, victim, TO_VICT); end else begin af := GAffect.Create(); af.name := sn.name; af.wear_msg := 'The poison slowly wears off.'; af.duration := (ch.level div 8); setLength(af.modifiers, 1); af.modifiers[0].apply_type := APPLY_AFFECT; af.modifiers[0].modifier := AFF_POISON; af.applyTo(victim); act(AT_SPELL,'You have succesfully poisoned $N!', false, ch, nil, victim, TO_CHAR); act(AT_SPELL,'You are poisoned!', false, ch, nil, victim, TO_VICT); act(AT_SPELL,'$N has been poisoned!', false, ch, nil, victim, TO_NOTVICT); end; end; // Spell - Vortex (damage) procedure spell_vortex(ch, victim : GCharacter; sn : GSkill); var dam : integer; begin dam := rolldice(4,6); inc(dam, ch.int div 4); damage(ch, victim, dam, cardinal(sn)); end; // Spell - Winds procedure spell_winds(ch, victim : GCharacter; sn : GSkill); var dam : integer; begin dam := rolldice(4,10); inc(dam, ch.int div 3); act(AT_SPELL,'You call upon the elements and release your fury!', false, ch, nil, nil, TO_CHAR); act(AT_SPELL,'$n calls upon the elements and releases $s fury!', false, ch, nil, nil, TO_ROOM); damage(ch, victim, dam, cardinal(sn)); end; // Spell - Recall procedure spell_recall(ch, victim : GCharacter; sn : GSkill); begin if (ch.room.flags.isBitSet(ROOM_NORECALL)) then begin act(AT_SPELL, 'Your recall spell failed.', false, ch, nil, nil, TO_CHAR); exit; end; ch.fromRoom(); if (ch.IS_EVIL) then ch.toRoom(findRoom(ROOM_VNUM_EVIL_PORTAL)) else ch.toRoom(findRoom(ROOM_VNUM_GOOD_PORTAL)); act(AT_REPORT,'You $B$7implore$A$7 the gods for safety.', false, ch, nil, nil, TO_CHAR); act(AT_REPORT,'$n $B$7implores$A$7 the gods for a safe haven.', false, ch, nil, nil, TO_ROOM); end; // Spell - Summon (summon a pc/npc into the room) procedure spell_summon(ch, victim : GCharacter; sn : GSkill); begin if (ch = victim) then begin act(AT_SPELL,'You attempt to summon yourself into the room.', false, ch, nil, victim, TO_CHAR); act(AT_SPELL,'Silly, you''re already here!', false, ch, nil, victim, TO_CHAR); act(AT_SPELL,'$n attempts to summon $N. Duh.', false, victim, nil, ch, TO_ROOM); exit; end; if (ch.room.flags.isBitSet(ROOM_NOSUMMON)) then begin act(AT_SPELL, 'Your summon spell failed.', false, ch, nil, nil, TO_CHAR); end else if (victim.state = STATE_IDLE) then begin act(AT_SPELL,'You summon $N into the room.', false, ch, nil, victim, TO_CHAR); act(AT_SPELL,'$n is summoned out of here!', false, victim, nil, nil, TO_ROOM); victim.fromRoom(); victim.toRoom(ch.room); act(AT_SPELL,'$n has summoned $N!', false, ch, nil, victim, TO_ROOM); if (victim.position <> POS_STANDING) then interpret(victim, 'stand'); end else act(AT_REPORT,'$N is not in a normal position to be summoned.', false, ch, nil, victim, TO_CHAR); end; // Spell - Refresh (regenerates moves) procedure spell_refresh(ch, victim : GCharacter; sn : GSkill); var ref : integer; begin ref := (ch.wis div 2) + 20 + rolldice(5,10); victim.mv := UMax(victim.mv + ref, victim.max_mv); act(AT_SPELL,'You feel refreshed.', false, victim, nil, nil, TO_CHAR); act(AT_SPELL,'$n looks refreshed.', false, victim, nil, nil, TO_ROOM); end; // Spell - Identify (identify objects) procedure spell_identify(ch, victim : GCharacter; sn : GSkill); var obj : GObject; s : string; liq : integer; const ac_types : array[ARMOR_HAC..ARMOR_LAC] of string = ('HAC','BAC','AAC','LAC'); begin obj := GObject(victim); with obj do begin case item_type of ITEM_WEAPON:s:='weapon'; ITEM_ARMOR:s:='armor'; ITEM_FOOD:s:='food'; ITEM_DRINK:s:='drink'; ITEM_LIGHT:s:='light'; ITEM_TRASH:s:='trash'; ITEM_MONEY:s:='money'; ITEM_GEM:s:='gem'; ITEM_CORPSE:s:='corpse'; ITEM_FOUNTAIN:s:='fountain'; ITEM_CONTAINER:s:='container'; ITEM_BLOOD:s:='blood'; ITEM_PORTAL:s:='portal'; ITEM_KEY:s:='key' else s:='unknown object'; end; act(AT_REPORT,'$p$7 is some sort of $B$4'+s+'$A$7.'#13#10,false,ch,obj,nil,TO_CHAR); if (wear_location1 = '') and (wear_location2 = '') then s := 'NONE' else if (wear_location1 = '') and (wear_location2 <> '') then s := wear_location2 else if (wear_location1 <> '') and (wear_location2 = '') then s := wear_location1 else s := uppercase(wear_location1 + ', ' + wear_location2); act(AT_REPORT,'Wearing positions $B$7'+s+'$A$7, weight $B$2'+inttostr(weight)+'$A$7 ounce(s).',false,ch,obj,nil,TO_CHAR); s:=''; if IS_SET(flags,OBJ_GLOW) then s:=s+'GLOWING '; if IS_SET(flags,OBJ_HUM) then s:=s+'HUMMING '; if IS_SET(flags,OBJ_ANTI_GOOD) then s:=s+'ANTI_GOOD '; if IS_SET(flags,OBJ_ANTI_EVIL) then s:=s+'ANTI_EVIL '; if IS_SET(flags,OBJ_LOYAL) then s:=s+'LOYAL '; if IS_SET(flags,OBJ_NOREMOVE) then s:=s+'NOREMOVE '; if IS_SET(flags,OBJ_NODROP) then s:=s+'NODROP '; if IS_SET(flags,OBJ_CLANOBJECT) then s:=s+'CLANOBJECT '; if IS_SET(flags,OBJ_MISSILE) then s:=s+'MISSILE '; if IS_SET(flags,OBJ_NOSAC) then s:=s+'NOSAC '; if length(s)>1 then begin delete(s,length(s),1); act(AT_REPORT,'Flags: [$B$7'+s+'$A$7]',false,ch,obj,nil,TO_CHAR); end; case item_type of ITEM_WEAPON:act(AT_REPORT,'Damage roll $B$3'+inttostr(value[2])+'d'+ inttostr(value[3])+'$A$7, type $B$7'+attack_table[value[4],1]+'$A$7.',false,ch,obj,nil,TO_CHAR); ITEM_ARMOR:act(AT_REPORT,'Armor $B$7'+ac_types[value[2]]+'$A$7, $B$3'+ inttostr(value[3])+'$A$7 AC.',false,ch,obj,nil,TO_CHAR); ITEM_DRINK, ITEM_FOUNTAIN:begin liq:=value[3]; act(AT_REPORT,'Liquid "'+liq_types[liq].name+'", affects '+ inttostr(liq_types[liq].affect[1])+' '+inttostr(liq_types[liq].affect[2])+ ' '+inttostr(liq_types[liq].affect[3])+' '+inttostr(liq_types[liq].affect[4])+ '.',false,ch,obj,nil,TO_CHAR); end; ITEM_GEM:act(AT_REPORT,'Spell level: '+inttostr(value[2])+', charged '+ 'mana: '+inttostr(value[3])+'.',false,ch,obj,nil,TO_CHAR); end; end; end; procedure spell_affect(ch, caster : GCharacter; sn : GSkill); var iterator : GIterator; aff, aff_find : GAffect; begin iterator := sn.affects.iterator(); while (iterator.hasNext()) do begin aff := GAffect(iterator.next()); aff_find := findAffect(ch, aff.name); // prolong existing affect if it already exists if (aff_find <> nil) then aff_find.duration := aff.duration else aff.applyTo(ch); end; iterator.Free(); end; // Handles default spellcasting procedure spell_generic(ch, victim : GCharacter; sn : GSkill); var vict, check : GCharacter; node, node_next : GListNode; dam : integer; begin with sn do begin check := nil; vict := nil; case target of TARGET_OFF_ATTACK, TARGET_DEF_SINGLE, TARGET_DEF_WORLD:begin node := victim.node_world; vict := victim; if (not ch.IS_SAME_ALIGN(vict)) then begin ch.sendBuffer('They are not here.'#13#10); exit; end; end; TARGET_OFF_AREA:begin { check fighting } if (ch.fighting <> nil) then check := ch.fighting else check := victim; node := ch.room.chars.head; while (node <> nil) do begin vict := GCharacter(node.element); if (vict <> ch) then begin if (vict.IS_NPC or vict.IS_SAME_ALIGN(check)) then break; end; node := node.next; end; end; TARGET_DEF_SELF:begin node := ch.node_room; vict := ch; end; TARGET_DEF_AREA:begin node := ch.room.chars.head; while (node <> nil) do begin vict := GCharacter(node.element); if (ch.IS_SAME_ALIGN(vict)) and (not vict.IS_NPC) then break; node := node.next; end; end; else begin bugreport('spell generic', 'magic.pas', 'illegal target ' + inttostr(target)); exit; end; end; if (vict = nil) then begin ch.sendBuffer('They are not here.'#13#10); exit; end; if (length(start_char) > 0) then act(AT_SPELL,start_char,false,ch,nil,vict,TO_CHAR); if (length(start_vict) > 0) then act(AT_SPELL,start_vict,false,ch,nil,vict,TO_VICT); if (length(start_room) > 0) then act(AT_SPELL,start_room,false,ch,nil,vict,TO_ROOM); repeat node_next := node.next; if (length(hit_vict) > 0) and (ch <> vict) and (not vict.CHAR_DIED) then begin act(AT_SPELL,hit_vict,false,ch,nil,vict,TO_VICT); if (length(hit_room)>0) then begin act(AT_SPELL,hit_room,false,ch,nil,vict,TO_NOTVICT); act(AT_SPELL,hit_room,false,ch,nil,vict,TO_CHAR); end; end else if (length(hit_room)>0) then act(AT_SPELL,hit_room,false,ch,nil,vict,TO_ROOM); if (ch = vict) and (not ch.CHAR_DIED) then begin if (length(hit_vict) > 0) then act(AT_SPELL,hit_vict,false,ch,nil,ch,TO_CHAR) else if (length(hit_char) > 0) then act(AT_SPELL,hit_char,false,ch,nil,ch,TO_CHAR); end else if (length(hit_char) > 0) then act(AT_SPELL,hit_char,false,ch,nil,vict,TO_CHAR); if (target <= TARGET_OFF_AREA) then if (dicenum>0) and (dicesize>0) then begin dam:=rolldice(dicenum,dicesize)+diceadd; damage(ch,vict,dam, cardinal(sn)); end; if (not vict.CHAR_DIED) and (affects.size() > 0) then spell_affect(vict,ch,sn); case target of TARGET_OFF_ATTACK, TARGET_DEF_SINGLE, TARGET_DEF_WORLD: vict := nil; TARGET_OFF_AREA:begin while (node <> nil) do begin node := node_next; if (node = nil) then begin vict := nil; break; end; vict := GCharacter(node.element); if (vict <> ch) then begin if (vict.IS_NPC or vict.IS_SAME_ALIGN(check)) then break; end; node_next := node.next; end; end; TARGET_DEF_SELF: vict := nil; TARGET_DEF_AREA:begin while (node <> nil) do begin node := node_next; if (node = nil) then begin vict := nil; break; end; vict := GCharacter(node.element); if (ch.IS_SAME_ALIGN(vict)) and (not vict.IS_NPC) then break; node_next := node.next; end; end; else begin bugreport('spell generic', 'magic.pas', 'illegal target ' + inttostr(target)); exit; end; end; until (vict = nil); end; end; procedure spell_dummy(ch, victim : GCharacter; sn : GSkill); begin end; // Find spell function function findFunc(const s : string) : SPEC_FUNC; begin findFunc := spell_dummy; if (s = 'spell_acid_arrow') then Result := spell_acid_arrow else if s='spell_burning_hands' then findFunc:=spell_burning_hands else if s='spell_generic' then findFunc:=spell_generic else if s='spell_identify' then findFunc:=spell_identify else if s='spell_lightning' then findFunc:=spell_lightning else if s='spell_magic_missile' then findFunc:=spell_magic_missile else if s='spell_poison' then findFunc:=spell_poison else if s='spell_summon' then findFunc:=spell_summon else if s='spell_vortex' then findFunc:=spell_vortex else if s='spell_winds' then findFunc:=spell_winds else if s='spell_recall' then findFunc:=spell_recall else if s='spell_refresh' then findFunc:=spell_refresh else bugreport('spell', 'magic.pas', 'spell ' + s + ' not found'); end; // Verbose spell procedure say_spell(ch : GCharacter; const name : string); const syl_table:array[1..49,1..2] of string=( (' ',' '), ('ar','abra'), ('au','kada'), ('bless','fido'), ('blind','nose'), ('bur','mosa'), ('cu','judi'), ('de','oculo'), ('en','unso'), ('light','dies'), ('lo','hi'), ('mor','zak'), ('move','sido'), ('ness','lacri'), ('ning','illa'), ('per','duda'), ('ra','gru'), ('re','candus'), ('son','sabru'), ('tect','infra'), ('tri','cula'), ('ven','nofo'), ('a','a'), ('b','b'), ( 'c', 'q' ), ( 'd', 'e' ), ( 'e', 'z' ), ( 'f', 'y' ), ( 'g', 'o' ), ( 'h', 'p' ), ( 'i', 'u' ), ( 'j', 'y' ), ( 'k', 't' ), ( 'l', 'r' ), ( 'm', 'w' ), ( 'n', 'i' ), ( 'o', 'a' ), ( 'p', 's' ), ( 'q', 'd' ), ( 'r', 'f' ), ( 's', 'g' ), ( 't', 'h' ), ( 'u', 'j' ), ( 'v', 'z' ), ( 'w', 'x' ), ( 'x', 'n' ), ( 'y', 'l' ), ( 'z', 'k' ), ( '', '' )); var buf, s : string; p : integer; len, syl : integer; begin buf := ''; p := 1; repeat len := 1; s := copy(name, p, length(name) - p + 1); for syl := 1 to 49 do begin if (pos(syl_table[syl,1], lowercase(s)) = 1) then begin len := length(syl_table[syl,1]); buf := buf + syl_table[syl,2]; break; end; end; inc(p, len); until (p > length(name)); act(AT_PURPLE,'You utter the words '''+buf+'''',false,ch,nil,nil,TO_CHAR); act(AT_PURPLE,'$n utter the words '''+buf+'''',false,ch,nil,nil,TO_ROOM); end; procedure magic_timer(ch, victim : GCharacter; sn : GSkill); var func : SPEC_FUNC; begin if (sn = nil) then exit; func := sn.func; if ((sn.target in [TARGET_DEF_WORLD, TARGET_OBJECT]) or (victim.room=ch.room)) and (not victim.CHAR_DIED) then begin if (skill_success(ch, sn)) or (ch.IS_IMMORT) or (ch.IS_NPC) then { immo's don't fail :) } begin if (sn.target <= TARGET_OFF_AREA) then begin if (not victim.CHAR_DIED) then begin ch.state := STATE_FIGHTING; ch.position := POS_STANDING; ch.fighting := victim; if (victim.state <> STATE_FIGHTING) then begin victim.fighting := ch; victim.state := STATE_FIGHTING; victim.position := POS_STANDING; end; end; end; say_spell(ch, sn.name); improve_skill(ch, sn); if (assigned(func)) then func(ch, victim, sn); if (not ch.IS_IMMORT) and (not ch.IS_NPC) then begin ch.cast_timer := 1; ch.mana := ch.mana - sn.min_mana; end; if (ch.fighting <> nil) then ch.state := STATE_FIGHTING else ch.state := STATE_IDLE; end else begin ch.mana := ch.mana - sn.min_mana div 2; if (sn.target < TARGET_OFF_AREA) then begin ch.state := STATE_FIGHTING; ch.position := POS_STANDING; ch.fighting := victim; if (victim.state <> STATE_FIGHTING) then begin victim.fighting := ch; victim.state := STATE_FIGHTING; victim.position := POS_STANDING; end; end; act(AT_REPORT, 'You have lost your concentration.',false,ch,nil,nil,TO_CHAR); if (ch.fighting <> nil) then ch.state := STATE_FIGHTING else ch.state := STATE_IDLE; end; end else begin act(AT_REPORT,'They are not here.',false,ch,nil,nil,TO_CHAR); ch.state := STATE_IDLE; end; ch.emptyBuffer(); end; end.