// $Id: skills.pas,v 1.11 2001/04/16 17:34:52 xenon Exp $ unit skills; interface uses SysUtils, chars, dtypes, conns, util, constants; type GSkill = class; SPEC_FUNC = procedure(ch, victim : GCharacter; sn : GSkill); GAffect = class skill : GSkill; apply_type : GApplyTypes; modifier : longint; duration : longint; node : GListNode; procedure modify(ch : GCharacter; add : boolean); procedure applyTo(ch : GCharacter); end; GSkill = class id : integer; func : SPEC_FUNC; affects : GDLinkedList; prereqs : GDLinkedList; name : string; skill_type:integer; min_mana:integer; min_lvl:integer; beats:integer; target:integer; dicenum,dicesize,diceadd:integer; dam_msg,wear_msg:string; start_char,start_vict,start_room:string; hit_char,hit_vict,hit_room:string; miss_char,miss_vict,miss_room:string; die_char,die_vict,die_room:string; imm_char,imm_vict,imm_room:string; constructor Create; destructor Destroy; override; end; var skill_table : GDLinkedList; { gsn's } var gsn_slashing_weapons : GSkill; gsn_second_attack : GSkill; gsn_third_attack : GSkill; gsn_fourth_attack : GSkill; gsn_fifth_attack : GSkill; gsn_enhanced_damage : GSkill; gsn_dual_wield : GSkill; gsn_slashing : GSkill; gsn_piercing : GSkill; gsn_concussion : GSkill; gsn_whipping : GSkill; gsn_kick : GSkill; gsn_bash : GSkill; gsn_poison : GSkill; gsn_sneak : GSkill; gsn_swim : GSkill; gsn_searching : GSkill; gsn_backstab : GSkill; gsn_circle : GSkill; gsn_rescue : GSkill; gsn_dodge : GSkill; gsn_track : GSkill; gsn_peek : GSkill; gsn_hide : GSkill; procedure load_skills; function findSkill(s : string) : GSkill; function findSkillPlayer(ch : GCharacter; s : string) : GSkill; procedure improve_skill(ch : GCharacter; sn : GSkill); function skill_success(ch : GCharacter; sn : GSkill) : boolean; function findApply(s : string) : GApplyTypes; function printApply(apply : GApplyTypes) : string; function findAffect(ch : GCharacter; sn : GSkill) : GAffect; procedure removeAffect(ch : GCharacter; aff : GAffect); function removeAffectSkill(ch:GCharacter; sn : GSkill):boolean; function removeAffectFlag(ch:GCharacter;flag:integer):boolean; procedure update_affects; implementation uses strip, magic, fight, update, mudsystem; function findSkill(s : string) : GSkill; var node : GListNode; sk : GSkill; begin s := uppercase(s); Result := nil; node := skill_table.head; while (node <> nil) do begin sk := node.element; if (s = uppercase(sk.name)) or (pos(s, uppercase(sk.name)) > 0) then begin Result := sk; break; end; node := node.next; end; end; function findSkillPlayer(ch : GCharacter; s : string) : GSkill; var sk : GSkill; begin sk := findSkill(s); Result := nil; if (ch.LEARNED(sk) > 0) then Result := sk; end; function assign_gsn(name : string) : GSkill; var gsn : GSkill; begin gsn := findSkill(name); if (gsn = nil) then bugreport('assign_gsn', 'skills.pas', 'skill '''+name+''' not found', 'The specified skill could not be found.'); assign_gsn := gsn; end; procedure process_affect(skill : GSkill; format : string); var aff : GAffect; begin aff := GAffect.Create; aff.skill := skill; with aff do begin apply_type := findApply(left(format, ' ')); format := right(format, ' '); modifier := cardinal(findSkill(left(format, ' '))); if (modifier = 0) then modifier := strtointdef(left(format, ' '), 0); format := right(format, ' '); duration := strtointdef(left(format, ' '), 0); end; aff.node := skill.affects.insertLast(aff); end; procedure load_skills; var f:textfile; s,g,a:string; num : integer; sk, skill : GSkill; begin assignfile(f, 'system\skills.dat'); {$I-} reset(f); {$I+} if (IOResult <> 0) then begin bugreport('load_skills', 'skills.pas', 'could not open system\skills.dat', 'The system file skills.dat could not be opened.'); exit; end; num := 0; repeat repeat readln(f,s); until (uppercase(s) = '#SKILL') or (eof(f)); if (eof(f)) then break; skill := GSkill.Create; skill.id := num; with skill do repeat readln(f, s); g := uppercase(left(s,':')); if (g = 'TYPE') then begin s := uppercase(right(s,' ')); if (s = 'SPELL') then skill_type := SKILL_SPELL else if (s = 'SKILL') then skill_type := SKILL_SKILL else if (s = 'WEAPON') then skill_type := SKILL_WEAPON; end else if (g = 'NAME') then name := right(s,' ') else if g='ROUNDS' then beats:=strtoint(right(s,' ')) else if g='MINLEVEL' then min_lvl:=strtoint(right(s,' ')) else if g='MANA' then min_mana:=strtoint(right(s,' ')) else if g='TARGET' then target:=strtoint(right(s,' ')) else if g='FUNCTION' then func := findFunc(right(s,' ')) else if g='STARTCHAR' then start_char := right(s,' ') else if g='STARTVICT' then start_vict := right(s,' ') else if g='STARTROOM' then start_room := right(s,' ') else if g='HITCHAR' then hit_char := right(s,' ') else if g='HITVICT' then hit_vict := right(s,' ') else if g='HITROOM' then hit_room := right(s,' ') else if g='MISSCHAR' then miss_char := right(s,' ') else if g='MISSVICT' then miss_vict := right(s,' ') else if g='MISSROOM' then miss_room := right(s,' ') else if g='DAMMSG' then dam_msg := right(s,' ') else if g='WEAROFF' then wear_msg := right(s,' ') else if g='DICE' then begin a:=uppercase(right(s,' ')); dicenum:=strtoint(left(a,'D')); a:=right(a,'D'); dicesize:=strtoint(left(a,'+')); a:=right(a,'+'); diceadd:=strtoint(left(a,' ')); end else if g='AFFECTS' then process_affect(skill, right(s,' ')) else if g='PREREQ' then begin a := right(s, ' '); sk := findSkill(a); if (sk <> nil) then prereqs.insertLast(sk) else bugreport('load_skills', 'skills.pas', 'Could not find prereq skill ' + a, 'The specified skill could not be found.'); end; until uppercase(s)='#END'; skill_table.insertLast(skill); inc(num); until eof(f); closefile(f); gsn_second_attack := assign_gsn('second attack'); gsn_third_attack := assign_gsn('third attack'); gsn_fourth_attack := assign_gsn('fourth attack'); gsn_fifth_attack := assign_gsn('fifth attack'); gsn_enhanced_damage := assign_gsn('enhanced damage'); gsn_dual_wield := assign_gsn('dual wield'); gsn_slashing := assign_gsn('slashing weapons'); gsn_piercing := assign_gsn('piercing weapons'); gsn_concussion := assign_gsn('concussion weapons'); gsn_whipping := assign_gsn('whipping weapons'); gsn_kick := assign_gsn('kick'); gsn_bash := assign_gsn('bash'); gsn_poison := assign_gsn('poison'); gsn_sneak := assign_gsn('sneaking'); gsn_swim := assign_gsn('swim'); gsn_searching := assign_gsn('searching'); gsn_backstab := assign_gsn('backstab'); gsn_circle := assign_gsn('circle'); gsn_rescue := assign_gsn('rescue'); gsn_dodge := assign_gsn('dodge'); gsn_track := assign_gsn('track'); gsn_peek := assign_gsn('peek'); gsn_hide := assign_gsn('hide'); end; procedure improve_skill(ch : GCharacter; sn : GSkill); var chance, percent : integer; begin if (ch.LEARNED(sn) = 100) then exit; chance := ch.LEARNED(sn) - (ch.ability.wis div 5); percent := number_percent; if (percent <= chance div 3) then begin act(AT_WHITE, '[You have become better at ' + sn.name + '!]',false,ch,nil,nil,TO_CHAR); ch.SET_LEARNED(UMin(ch.LEARNED(sn) + 1, 100), sn); end; end; function skill_success(ch : GCharacter; sn : GSkill) : boolean; begin skill_success := (number_percent <= ch.LEARNED(sn)); end; procedure GAffect.modify(ch : GCharacter; add : boolean); var modif : integer; begin modif := modifier; if (not add) then begin case apply_type of APPLY_AFFECT: begin REMOVE_BIT(ch.aff_flags, modif); exit; end; APPLY_REMOVE: begin SET_BIT(ch.aff_flags, modif); exit; end; APPLY_STRIPSPELL: exit; end; modif := -modif; end; case apply_type of APPLY_STR: inc(ch.ability.str, modif); APPLY_DEX: inc(ch.ability.dex, modif); APPLY_INT: inc(ch.ability.int, modif); APPLY_WIS: inc(ch.ability.wis, modif); APPLY_CON: inc(ch.ability.con, modif); APPLY_HP: ch.point.hp := UMin(ch.point.hp + modif, ch.point.max_hp); APPLY_MAX_HP: inc(ch.point.max_hp, modif); APPLY_MV: ch.point.mv := UMin(ch.point.mv + modif, ch.point.max_mv); APPLY_MAX_MV: inc(ch.point.max_mv, modif); APPLY_MANA: ch.point.mana := UMin(ch.point.mana + modif, ch.point.max_mana); APPLY_MAX_MANA: inc(ch.point.max_mana, modif); APPLY_AC: begin inc(ch.point.ac, modif); ch.calcAC; end; APPLY_APB: inc(ch.point.apb, modif); APPLY_AFFECT: SET_BIT(ch.aff_flags, modif); APPLY_REMOVE: REMOVE_BIT(ch.aff_flags, modif); APPLY_STRIPSPELL: removeAffectSkill(ch, GSkill(pointer(modif))); APPLY_FULL: gain_condition(ch, COND_FULL, modif); APPLY_THIRST: gain_condition(ch, COND_THIRST, modif); APPLY_DRUNK: gain_condition(ch, COND_DRUNK, modif); APPLY_CAFFEINE: gain_condition(ch, COND_CAFFEINE, modif); end; end; procedure GAffect.applyTo(ch : GCharacter); var aff : GAffect; begin if (duration > 0) then begin aff := GAffect.Create; aff.skill := Self.skill; aff.apply_type := Self.apply_type; aff.duration := Self.duration; aff.modifier := Self.modifier; aff.node := ch.affects.insertLast(aff); aff.modify(ch, true); end else modify(ch, true); end; function findApply(s : string) : GApplyTypes; begin s := uppercase(s); if (s = 'APPLY_STR') then Result := APPLY_STR else if (s = 'APPLY_DEX') then Result := APPLY_DEX else if (s = 'APPLY_INT') then Result := APPLY_INT else if (s = 'APPLY_WIS') then Result := APPLY_WIS else if (s = 'APPLY_CON') then Result := APPLY_CON else if (s = 'APPLY_HP') then Result := APPLY_HP else if (s = 'APPLY_MAX_HP') then Result := APPLY_MAX_HP else if (s = 'APPLY_MV') then Result := APPLY_MV else if (s = 'APPLY_MAX_MV') then Result := APPLY_MAX_MV else if (s = 'APPLY_MANA') then Result := APPLY_MANA else if (s = 'APPLY_MAX_MANA') then Result := APPLY_MAX_MANA else if (s = 'APPLY_AFFECT') then Result := APPLY_AFFECT else if (s = 'APPLY_REMOVE') then Result := APPLY_REMOVE else if (s = 'APPLY_AC') then Result := APPLY_AC else if (s = 'APPLY_APB') then Result := APPLY_APB else if (s = 'APPLY_STRIPSPELL') then Result := APPLY_STRIPSPELL else if (s = 'APPLY_FULL') then Result := APPLY_FULL else if (s = 'APPLY_THIRST') then Result := APPLY_THIRST else if (s = 'APPLY_DRUNK') then Result := APPLY_DRUNK else if (s = 'APPLY_CAFFEINE') then Result := APPLY_CAFFEINE else begin bugreport('findApply', 'skills.pas', 'Illegal apply type "' + s + '"', ''); Result := APPLY_NONE; end; end; function printApply(apply : GApplyTypes) : string; begin case apply of APPLY_STR: Result := 'apply_str'; APPLY_CON: Result := 'apply_con'; APPLY_INT: Result := 'apply_int'; APPLY_WIS: Result := 'apply_wis'; APPLY_DEX: Result := 'apply_dex'; APPLY_AC : Result := 'apply_ac'; APPLY_APB : Result := 'apply_apb'; APPLY_STRIPSPELL : Result := 'apply_stripspell'; APPLY_AFFECT : Result := 'apply_affect'; APPLY_REMOVE : Result := 'apply_remove'; APPLY_FULL : Result := 'apply_full'; APPLY_THIRST : Result := 'apply_thirst'; APPLY_DRUNK : Result := 'apply_drunk'; APPLY_CAFFEINE : Result := 'apply_caffeine'; APPLY_HP : Result := 'apply_hp'; APPLY_MAX_HP : Result := 'apply_max_hp'; APPLY_MV : Result := 'apply_mv'; APPLY_MAX_MV : Result := 'apply_max_mv'; APPLY_MANA : Result := 'apply_mana'; APPLY_MAX_MANA : Result := 'apply_max_mana'; else Result := 'apply_none'; end; end; function findAffect(ch : GCharacter; sn : GSkill) : GAffect; var node : GListNode; aff : GAffect; begin findAffect := nil; node := ch.affects.head; while (node <> nil) do begin aff := node.element; if (aff.skill = sn) then begin findAffect := aff; exit; end; node := node.next; end; end; procedure removeAffect(ch : GCharacter; aff : GAffect); begin aff.modify(ch, false); ch.affects.remove(aff.node); aff.Free; end; function removeAffectSkill(ch:GCharacter; sn : GSkill):boolean; var aff : GAffect; begin aff := findAffect(ch, sn); if (aff = nil) then begin Result := false; exit; end; removeAffect(ch, aff); Result := true; end; function removeAffectFlag(ch:GCharacter;flag:integer):boolean; var node : GListNode; aff : GAffect; begin removeAffectFlag := false; aff := nil; node := ch.affects.head; while (node <> nil) do begin if (GAffect(node.element).apply_type = APPLY_AFFECT) and (GAffect(node.element).modifier = flag) then begin aff := node.element; break; end; node := node.next; end; if (aff = nil) then exit; removeAffect(ch, aff); removeAffectFlag := true; end; procedure update_affects; var ch : GCharacter; node, node_aff : GListNode; aff : GAffect; begin node := char_list.head; while (node <> nil) do begin ch := node.element; if IS_SET(ch.aff_flags,AFF_POISON) then begin act(AT_REPORT,'You shiver and suffer.',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n shivers and suffers.',false,ch,nil,nil,TO_ROOM); ch.mental_state:=URANGE(20,ch.mental_state+4,100); damage(ch,ch,6, cardinal(gsn_poison)); end; { if IS_SET(ch.aff_flags,AFF_COLD) then begin i:=random(5); case i of 1:begin act(AT_REPORT,'You sneeze loudly... do you need a tissue?',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n sneezes loudly... maybe $e needs a tissue?',false,ch,nil,nil,TO_ROOM); end; 2:begin act(AT_REPORT,'You sniff a bit.',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n sniffs a bit.',false,ch,nil,nil,TO_ROOM); end; 3:begin act(AT_REPORT,'You don''t feel very well.',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n doesn''t look very well.',false,ch,nil,nil,TO_ROOM); ch.mental_state:=URANGE(20,ch.mental_state+1,100); end; end; end; } node_aff := ch.affects.head; while (node_aff <> nil) do begin aff := node_aff.element; dec(aff.duration); if (aff.duration = 0) then begin act(AT_REPORT, aff.skill.wear_msg, false,ch,nil,nil,TO_CHAR); removeAffect(ch, aff); end; node_aff := node_aff.next; end; node := node.next; end; end; { GSkill } constructor GSkill.Create; begin inherited Create; affects := GDLinkedList.Create; prereqs := GDLinkedList.Create; end; destructor GSkill.Destroy; begin affects.clean; affects.Free; prereqs.smallClean; prereqs.Free; inherited Destroy; end; begin skill_table := GDLinkedList.Create; end.