// $Id: skills.pas,v 1.15 2001/07/12 16:37:02 druid Exp $ unit skills; interface uses SysUtils, chars, dtypes, conns, util, constants; type GSkill = class; SPEC_FUNC = procedure(ch, victim : GCharacter; sn : GSkill); GModifier = record apply_type : GApplyTypes; modifier : longint; end; GAffect = class // skill : GSkill; wear_msg : string; name : PString; duration : longint; modifiers : array of GModifier; 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 : PString; skill_type:integer; min_mana:integer; min_lvl:integer; beats:integer; target:integer; dicenum,dicesize,diceadd:integer; dam_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; name : string) : GAffect; procedure removeAffect(ch : GCharacter; aff : GAffect); function removeAffectName(ch:GCharacter; name : string):boolean; function removeAffectFlag(ch:GCharacter; flag : integer):boolean; procedure update_affects; implementation uses strip, fsys, magic, fight, update, mudsystem; function findSkill(s : string) : GSkill; var node : GListNode; sk : GSkill; begin s := trim(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^)) = 1) 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 load_skills; var af : GFileReader; s,g,a:string; num : integer; sk, skill : GSkill; aff : GAffect; modif, len : integer; begin try af := GFileReader.Create('system\skills.dat'); except 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 s := af.readLine(); until (uppercase(s) = '#SKILL') or (af.eof()); if (af.eof()) then break; skill := GSkill.Create; skill.id := num; with skill do repeat g := uppercase(left(af.readToken(), ':')); if (g = 'TYPE') then begin s := uppercase(af.readToken()); 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 := hash_string(af.readLine()) else if g='ROUNDS' then beats := af.readInteger() else if g='MINLEVEL' then min_lvl := af.readInteger() else if g='MANA' then min_mana := af.readInteger() else if g='TARGET' then target := af.readInteger() else if g='FUNCTION' then func := findFunc(af.readToken()) else if g='STARTCHAR' then start_char := af.readLine() else if g='STARTVICT' then start_vict := af.readLine() else if g='STARTROOM' then start_room := af.readLine() else if g='HITCHAR' then hit_char := af.readLine() else if g='HITVICT' then hit_vict := af.readLine() else if g='HITROOM' then hit_room := af.readLine() else if g='MISSCHAR' then miss_char := af.readLine() else if g='MISSVICT' then miss_vict := af.readLine() else if g='MISSROOM' then miss_room := af.readLine() else if g='DAMMSG' then dam_msg := af.readLine() else if g='DICE' then begin a:=uppercase(af.readLine()); 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 begin aff := GAffect.Create(); aff.name := hash_string(af.readToken()); aff.wear_msg := af.readToken(); aff.duration := af.readInteger(); num := 1; while (not af.eol) and (af.readToken() = '{') do begin setLength(aff.modifiers, num); aff.modifiers[num - 1].apply_type := findApply(af.readToken); s := af.readToken(); modif := cardinal(findSkill(s)); if (modif = 0) then modif := strtointdef(s, 0); aff.modifiers[num - 1].modifier := modif; s := af.readToken(); inc(num); end; aff.node := affects.insertLast(aff); end else if g='PREREQ' then begin a := af.readToken(); 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 g='#END'; skill_table.insertLast(skill); inc(num); until (af.eof()); af.Free; 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.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; a : integer; begin for a := 0 to length(modifiers) - 1 do begin modif := modifiers[a].modifier; if (not add) then begin case (modifiers[a].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_STRIPNAME: exit; end; modif := -modif; end; case (modifiers[a].apply_type) of APPLY_STR: ch.str := UMin(ch.str + modif, 100); APPLY_DEX: ch.dex := UMin(ch.dex + modif, 100); APPLY_INT: ch.int := UMin(ch.int + modif, 100); APPLY_WIS: ch.wis := UMin(ch.wis + modif, 100); APPLY_CON: ch.con := UMin(ch.con + modif, 100); APPLY_HP: ch.hp := UMin(ch.hp + modif, ch.max_hp); APPLY_MAX_HP: ch.max_hp := UMin(ch.max_hp + modif, 15000); APPLY_MV: ch.mv := UMin(ch.mv + modif, ch.max_mv); APPLY_MAX_MV: ch.max_mv := UMin(ch.max_mv + modif, 15000); APPLY_MANA: ch.mana := UMin(ch.mana + modif, ch.max_mana); APPLY_MAX_MANA: ch.max_mana := UMin(ch.max_mana + modif, 15000); APPLY_AC: begin inc(ch.ac, modif); ch.calcAC; end; APPLY_APB: ch.apb := ch.apb + modif; APPLY_AFFECT: SET_BIT(ch.aff_flags, modif); APPLY_REMOVE: REMOVE_BIT(ch.aff_flags, modif); APPLY_STRIPNAME: removeAffectName(ch, PString(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; end; procedure GAffect.applyTo(ch : GCharacter); var aff : GAffect; begin if (duration > 0) then begin aff := GAffect.Create; aff.name := Self.name; aff.wear_msg := Self.wear_msg; aff.duration := Self.duration; aff.modifiers := Self.modifiers; if (findAffect(ch, Self.name^) = nil) then // not yet affected 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_STRIPNAME') then Result := APPLY_STRIPNAME 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_STRIPNAME : Result := 'apply_stripname'; 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; name : string) : GAffect; var node : GListNode; aff : GAffect; begin Result := nil; node := ch.affects.head; while (node <> nil) do begin aff := node.element; if (aff.name^ = name) then begin Result := 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 removeAffectName(ch : GCharacter; name : string) : boolean; var aff : GAffect; begin aff := findAffect(ch, name); 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, taff : GAffect; a : integer; begin removeAffectFlag := false; aff := nil; node := ch.affects.head; while (node <> nil) do begin taff := node.element; for a := 0 to length(taff.modifiers) - 1 do begin if (taff.modifiers[a].apply_type = APPLY_AFFECT) and (taff.modifiers[a].modifier = flag) then begin aff := node.element; break; end; 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.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.