// $Id: magic.pas,v 1.11 2001/07/12 16:37:01 druid Exp $
unit magic;
interface
uses
skills,
chars;
function findFunc(s:string) : SPEC_FUNC;
procedure magic_timer(ch, victim : GCharacter; sn : GSkill);
implementation
uses
SysUtils,
constants,
area,
dtypes,
mudthread,
mudsystem,
conns,
util,
fight;
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);
saving_throw:=number_percent<=chance;
end;
procedure spell_acid_arrow(ch, victim : GCharacter; sn : GSkill);
var af : GAffect;
begin
af := nil;
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.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;
procedure spell_burning_hands(ch,victim:GCharacter; sn : GSkill);
begin
damage(ch,victim,45, cardinal(sn));
end;
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;
procedure spell_magic_missile(ch,victim:GCharacter;sn:GSkill);
begin
damage(ch,victim,35,cardinal(sn));
end;
procedure spell_poison(ch,victim:GCharacter; sn : GSkill);
var af:GAffect;
begin
af := nil;
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.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;
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;
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;
procedure spell_recall(ch,victim:GCharacter;sn:GSkill);
begin
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;
procedure spell_summon(ch,victim:GCharacter;sn:GSkill);
begin
// Jago 18May2001 : check ch <> vict
// note: could make the spell not castable on self, but this is more fun ;)
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 victim.position<>POS_FIGHTING 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;
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;
procedure spell_identify(ch,victim:GCharacter;sn:GSkill);
var obj : GObject;
s:string;
liq:integer;
const wearpos:array[WEAR_RFINGER..WEAR_EYES] of string=
('FINGER','FINGER','NECK','NECK','BODY','HEAD','LEGS',
'FEET','HANDS','ARMS','SHIELD','ABOUT','WAIST',
'WRIST','WRIST','FLOATING','HAND','HAND','SHOULDER',
'SHOULDER','FACE','EAR','EAR','ANKLE','ANKLE','EYES');
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);
s:='';
if wear1<>0 then
s:=wearpos[wear1];
if wear2<>0 then
begin
if s<>'' then
s:=s+' '+wearpos[wear2]
else
s:=wearpos[wear2];
end;
if (wear1=0) and (wear2=0) then
s:='NONE';
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
node : GListNode;
aff : GAffect;
begin
node := sn.affects.head;
while (node <> nil) do
begin
aff := node.element;
removeAffectName(ch, aff.name^);
aff.applyTo(ch);
node := node.next;
end;
end;
procedure spell_generic(ch,victim:GCharacter; sn : GSkill);
var vict,check:GCharacter;
node : 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;
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 := 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 := 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),
'The specified target is unknown.');
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
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.getSize() > 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 := node.element;
if (vict <> ch) then
begin
if (vict.IS_NPC or vict.IS_SAME_ALIGN(check)) then
break;
end;
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 := node.element;
if (ch.IS_SAME_ALIGN(vict)) and (not vict.IS_NPC) then
break;
end;
end;
else
begin
bugreport('spell generic', 'magic.pas', 'illegal target ' + inttostr(target),
'The specified target is unknown.');
exit;
end;
end;
until (vict = nil);
end;
end;
procedure spell_dummy(ch,victim:GCharacter; sn : GSkill);
begin
end;
function findFunc(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',
'The specified spell could not be found.');
end;
procedure say_spell(ch:GCharacter; 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;
a, len, syl : integer;
begin
buf := '';
a := 1;
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], 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
if (not victim.CHAR_DIED) then
begin
ch.position := POS_FIGHTING;
ch.fighting := victim;
if (victim.position < POS_FIGHTING) then
begin
victim.fighting := ch;
victim.position := POS_FIGHTING;
end;
end
else
ch.position := POS_STANDING;
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.position := POS_FIGHTING
else
ch.position := POS_STANDING;
end
else
begin
ch.mana := ch.mana - sn.min_mana div 2;
if (sn.target < TARGET_OFF_AREA) then
begin
ch.position := POS_FIGHTING;
ch.fighting := victim;
if (victim.position < POS_FIGHTING) then
begin
victim.fighting := ch;
victim.position := POS_FIGHTING;
end;
end
else
ch.position := POS_STANDING;
act(AT_REPORT, 'You have lost your concentration.',false,ch,nil,nil,TO_CHAR);
if (ch.fighting <> nil) then
ch.position := POS_FIGHTING
else
ch.position := POS_STANDING;
end;
end
else
begin
act(AT_REPORT,'They are not here.',false,ch,nil,nil,TO_CHAR);
ch.position := POS_STANDING;
end;
ch.emptyBuffer;
end;
end.