/
clans/
include/CVS/
manual/CVS/
races/CVS/
system/CVS/
text/
text/CVS/
todo/
todo/CVS/
units/CVS/
unit skills;

interface

uses
    SysUtils,
    chars,
    dtypes,
    conns,
    util,
    constants;

type
    SPEC_FUNC = procedure(ch, victim : GCharacter; sn : integer);

    GSkill = class
      func : SPEC_FUNC;
      affect : GAffect;

      prereqs : GDLinkedList;

      name : string;
      skill_type:integer;
      min_mana:integer;
      min_lvl:integer;
      beats:integer;
      target:integer;
      sn : 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;
    end;

var
   skill_table : array[0..MAX_SKILLS-1] of GSkill;
   num_skills : integer;

{ gsn's }
var
   gsn_slashing_weapons : integer;
   gsn_second_attack : integer;
   gsn_third_attack : integer;
   gsn_fourth_attack : integer;
   gsn_fifth_attack : integer;
   gsn_enhanced_damage : integer;
   gsn_dual_wield : integer;
   gsn_slashing : integer;
   gsn_piercing : integer;
   gsn_concussion : integer;
   gsn_whipping : integer;
   gsn_kick : integer;
   gsn_bash : integer;
   gsn_poison : integer;
   gsn_sneak : integer;
   gsn_swim : integer;
   gsn_searching : integer;
   gsn_backstab : integer;
   gsn_circle : integer;
   gsn_rescue : integer;
   gsn_dodge : integer;
   gsn_track : integer;
   gsn_peek : integer;
   gsn_hide : integer;


procedure load_skills;
procedure done_skills;

function findSkill(s : string) : integer;
function findSkillPlayer(ch : GCharacter; s : string) : integer;

procedure improve_skill(ch : GCharacter; sn : integer);
function skill_success(ch : GCharacter; sn : integer) : boolean;

function findAffect(ch:GCharacter;sn:integer) : GAffect;
procedure doAffect(ch:GCharacter;affect:GAffect);
procedure removeAffect(ch : GCharacter; aff : GAffect);
function removeAffectSkill(ch:GCharacter;gsn:integer):boolean;
function removeAffectFlag(ch:GCharacter;flag:integer):boolean;
procedure update_affects;


implementation

uses
    strip,
    magic,
    fight,
    mudsystem;


function findSkill(s : string) : integer;
var h, r : integer;
begin
  s := uppercase(s);
  r := -1;

  for h := 0 to num_skills - 1 do
   if (s = uppercase(skill_table[h].name)) or (pos(s,uppercase(skill_table[h].name)) <> 0) then
    begin
    r := h;
    break;
    end;

  findSkill := r;
end;

function findSkillPlayer(ch : GCharacter; s : string) : integer;
var h, r : integer;
begin
  s := uppercase(s);
  r := -1;

  for h := 0 to num_skills - 1 do
   if (s = uppercase(skill_table[h].name)) or (pos(s,uppercase(skill_table[h].name)) <> 0) then
    begin
    r := h;
    break;
    end;

  if (r <> -1) and (ch.learned[r] = 0) then
    findSkillPlayer := -1
  else
    findSkillPlayer := r;
end;

function assign_gsn(name : string) : integer;
var
   gsn : integer;
begin
  gsn := findSkill(name);

  if (gsn = -1) 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; s : integer; format:string);
begin
  skill.affect := GAffect.Create;
  with skill.affect do
    begin
    sn:=s;
    aff_type:=upcase(format[1]);
    format:=striprbeg(format,' ');
    modifier:=strtoint(stripl(format,' '));
    format:=striprbeg(format,' ');
    duration:=strtoint(stripl(format,' '));
    format:=striprbeg(format,' ');
    aff_flag:=strtoint(stripl(format,' '));
    end;
end;

procedure load_skills;
var f:textfile;
    s,g,a:string;
    h:integer;
    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;

  FillChar(skill_table, sizeof(skill_table), 0);
  num_skills := 0;

  repeat
    repeat
      readln(f,s);
    until (uppercase(s) = '#SKILL') or (eof(f));

    if (eof(f)) then
      break;

    skill := GSkill.Create;

    skill.affect := nil;
    skill.prereqs := GDLinkedList.Create;
    skill.sn := num_skills;

    with skill do
      repeat
      readln(f, s);

      g := uppercase(stripl(s,':'));

      if (g = 'TYPE') then
        begin
        s := uppercase(striprbeg(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 := striprbeg(s,' ')
      else
      if g='ROUNDS' then
        beats:=strtoint(striprbeg(s,' '))
      else
      if g='MINLEVEL' then
        min_lvl:=strtoint(striprbeg(s,' '))
      else
      if g='MANA' then
        min_mana:=strtoint(striprbeg(s,' '))
      else
      if g='TARGET' then
        target:=strtoint(striprbeg(s,' '))
      else
      if g='FUNCTION' then
        func := findFunc(striprbeg(s,' '))
      else
      if g='STARTCHAR' then
        start_char := striprbeg(s,' ')
      else
      if g='STARTVICT' then
        start_vict := striprbeg(s,' ')
      else
      if g='STARTROOM' then
        start_room := striprbeg(s,' ')
      else
      if g='HITCHAR' then
        hit_char := striprbeg(s,' ')
      else
      if g='HITVICT' then
        hit_vict := striprbeg(s,' ')
      else
      if g='HITROOM' then
        hit_room := striprbeg(s,' ')
      else
      if g='MISSCHAR' then
        miss_char := striprbeg(s,' ')
      else
      if g='MISSVICT' then
        miss_vict := striprbeg(s,' ')
      else
      if g='MISSROOM' then
        miss_room := striprbeg(s,' ')
      else
      if g='DAMMSG' then
        dam_msg := striprbeg(s,' ')
      else
      if g='WEAROFF' then
        wear_msg := striprbeg(s,' ')
      else
      if g='DICE' then
        begin
        a:=uppercase(striprbeg(s,' '));
        dicenum:=strtoint(stripl(a,'D'));
        a:=striprbeg(a,'D');
        dicesize:=strtoint(stripl(a,'+'));
        a:=striprbeg(a,'+');
        diceadd:=strtoint(stripl(a,' '));
        end
      else
      if g='AFFECTS' then
        process_affect(skill, num_skills, striprbeg(s,' '))
      else
      if g='PREREQ' then
        begin
        a := striprbeg(s, ' ');
        h := findSkill(a);

        if (h >= 0) then
          prereqs.insertLast(skill_table[h])
        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[num_skills] := skill;

    inc(num_skills);
  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 done_skills;
var
   a : integer;
begin
  for a := 0 to num_skills - 1 do
    begin
    skill_table[a].Free;
    end;
end;

procedure improve_skill(ch : GCharacter; sn : integer);
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 '+skill_table[sn].name+'!]',false,ch,nil,nil,TO_CHAR);
    ch.learned[sn] := UMAX(ch.learned[sn]+1,100);
    end;
end;

function skill_success(ch : GCharacter; sn : integer) : boolean;
begin
  skill_success := (number_percent <= ch.learned[sn]);
end;

function findAffect(ch:GCharacter;sn:integer) : GAffect;
var
   node : GListNode;
   aff : GAffect;
begin
  findAffect := nil;

  node := ch.affects.head;

  while (node <> nil) do
    begin
    aff := node.element;

    if (aff.sn = sn) then
      begin
      findAffect := aff;
      exit;
      end;

    node := node.next;
    end;
end;

procedure doAffect(ch:GCharacter;affect:GAffect);
var
   aff : GAffect;
   node : GListNode;
begin
  if (affect = nil) then
    exit;
    
  with ch do
  { first check if the damn affect doesn't exist already }
    begin
    aff := findAffect(ch,affect.sn);

    if (aff <> nil) then
      begin
      aff.duration := affect.duration; {reset duration }
      exit;
      end;

    aff := GAffect.Create;
    aff.sn := affect.sn;
    aff.aff_type := affect.aff_type;
    aff.aff_flag := affect.aff_flag;
    aff.duration := affect.duration;
    aff.modifier := affect.modifier;

    if (aff.duration > 0) then
      begin
      SET_BIT(ch.aff_flags, affect.aff_flag);
      aff.node := ch.affects.insertLast(aff);
      end;

    with aff do
      case aff_type of
          'H':inc(ch.point.max_hp, modifier);
          'M':inc(ch.point.max_mv, modifier);
          'N':inc(ch.point.max_mana, modifier);
          'S':inc(ch.ability.str, modifier);
          'C':inc(ch.ability.con, modifier);
          'D':inc(ch.ability.dex, modifier);
          'I':inc(ch.ability.int, modifier);
          'W':inc(ch.ability.wis, modifier);
          'F':ch.startFlying;
          'A':begin
              inc(ch.point.ac_mod,modifier);

              ch.calcAC;
              end;
      end;
    end;
end;

procedure removeAffect(ch : GCharacter; aff : GAffect);
begin
  with aff do
    begin
    REMOVE_BIT(ch.aff_flags,aff_flag);

    case aff_type of
      'H':dec(ch.point.max_hp, modifier);
      'M':dec(ch.point.max_mv, modifier);
      'N':dec(ch.point.max_mana, modifier);
      'S':dec(ch.ability.str, modifier);
      'C':dec(ch.ability.con, modifier);
      'D':dec(ch.ability.dex, modifier);
      'I':dec(ch.ability.int, modifier);
      'W':dec(ch.ability.wis, modifier);
      'F':ch.stopFlying;
      'A':begin
          dec(ch.point.ac_mod,modifier);
          ch.calcAc;
          end;
    end;
    end;

  ch.affects.remove(aff.node);

  aff.Free;
end;

function removeAffectSkill(ch:GCharacter;gsn:integer):boolean;
var
   aff : GAffect;
begin
  aff := findAffect(ch,gsn);

  if (aff = nil) then
    begin
    bugreport('removeAffectSkill', 'skills.pas', 'skill number null, ch ' + ch.name^,
              'An attempt was made to remove an unexisting affect.');
    exit;
    end;

  removeAffect(ch, aff);

  removeAffectSkill := 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).aff_flag = 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 i : integer;
    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,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, skill_table[aff.sn].wear_msg,false,ch,nil,nil,TO_CHAR);
        removeAffect(ch, aff);
        end;

      node_aff := node_aff.next;
      end;

    node := node.next;
    end;
end;

end.