// $Id: chars.pas,v 1.43 2001/07/12 16:37:01 druid Exp $ unit chars; interface uses SysUtils, Math, Winsock2, constants, strip, area, race, md5, ansiio, fsys, util, clan, dtypes, gvm, bulletinboard; type GCharacter = class; GTrophy = record name : string; level, times : integer; end; GAlias = class alias : string; expand : string; node : GListNode; end; THistoryElement = // channelhistory stuff class time : TDateTime; contents : PString; constructor Create(txt : string); destructor Destroy(); override; end; TChannel = class channelname : string; history : GDLinkedList; ignored : boolean; constructor Create(txt : string); destructor Destroy(); override; end; GLearned = class skill : pointer; perc : integer; constructor Create(perc_ : integer; skill_ : pointer); end; {$M+} GCharacter = class node_world, node_room : GListNode; objects : GDLinkedList; reply, master, leader : GCharacter; fighting , hunting : GCharacter; snooped_by : GCharacter; private _level : integer; _str, _con, _dex, _int, _wis : integer; _hp, _max_hp : integer; _mv, _max_mv : integer; _mana, _max_mana : integer; _apb : integer; public ac_mod : integer; { AC modifier (spells?) } natural_ac : integer; { Natural AC (race based for PC's) } hac, bac, aac, lac, ac : integer; { head, body, arm, leg and overall ac } hitroll : integer; { the hit roll } damnumdie, damsizedie : integer; save_poison, save_cold, save_para, { saving throws } save_breath, save_spell :integer; tracking : string; logging : boolean; position : integer; gold : longint; { Gold carried } mental_state : integer; room : GRoom; substate : integer; trust : integer; kills : integer; wait : integer; skills_learned : GDLinkedList; cast_timer, bash_timer, bashing : integer; in_command : boolean; name, short, long : PString; sex : integer; race : GRace; alignment : integer; carried_weight : integer; { weight of items carried } weight, height : integer; { weight/height of (N)PC } last_cmd : pointer; affects : GDLinkedList; aff_flags : cardinal; clan : GClan; { joined a clan? } conn : pointer; published procedure sendPrompt; virtual; procedure sendBuffer(s : string); virtual; procedure sendPager(txt : string); virtual; procedure emptyBuffer; virtual; function ansiColor(color : integer) : string; virtual; function getTrust : integer; function CHAR_DIED : boolean; function IS_IMMORT : boolean; virtual; function IS_NPC : boolean; virtual; function IS_LEARNER : boolean; virtual; function IS_AWAKE : boolean; virtual; function IS_INVIS : boolean; virtual; function IS_HIDDEN : boolean; virtual; function IS_WIZINVIS : boolean; virtual; function IS_GOOD : boolean; virtual; function IS_EVIL : boolean; virtual; function IS_SAME_ALIGN(vict : GCharacter) : boolean; virtual; function IS_FLYING : boolean; virtual; function IS_BANKER : boolean; virtual; function IS_SHOPKEEPER : boolean; virtual; function IS_OUTSIDE : boolean; virtual; function IS_AFFECT(affect : integer) : boolean; virtual; function IS_DRUNK : boolean; virtual; function IS_WEARING(item_type : integer) : boolean; virtual; function IS_HOLYWALK : boolean; virtual; function IS_HOLYLIGHT : boolean; virtual; function IS_AFK : boolean; virtual; function IS_KEYLOCKED : boolean; virtual; function IS_EDITING : boolean; virtual; function CAN_FLY : boolean; virtual; function CAN_SEE(target : TObject) : boolean; virtual; function LEARNED(skill : pointer) : integer; procedure SET_LEARNED(perc : integer; skill : pointer); procedure extract(pull : boolean); procedure fromRoom; procedure toRoom(to_room : GRoom); function getEQ(location : integer) : GObject; function getWield(item_type : integer) : GObject; function getDualWield : GObject; procedure affectObject(obj : GObject; remove: boolean); function equip(obj : GObject) : boolean; procedure die; virtual; procedure setWait(ticks : integer); function calcxp2lvl : cardinal; procedure calcAC; procedure startFlying; procedure stopFlying; function findInventory(s : string) : GObject; function findEquipment(s : string) : GObject; constructor Create; destructor Destroy; override; // properties function getName() : string; property level : integer read _level write _level; property str : integer read _str write _str; property con : integer read _str write _str; property dex : integer read _str write _str; property int : integer read _str write _str; property wis : integer read _str write _str; property hp : integer read _hp write _hp; property max_hp : integer read _max_hp write _max_hp; property mv : integer read _mv write _mv; property max_mv : integer read _max_mv write _max_mv; property mana : integer read _mana write _mana; property max_mana : integer read _max_mana write _max_mana; property apb : integer read _apb write _apb; property pname : string read getName; end; GNPC = class(GCharacter) public npc_index : GNPCIndex; act_flags : cardinal; context : GContext; published function IS_IMMORT : boolean; override; function IS_NPC : boolean; override; function IS_LEARNER : boolean; override; function IS_WIZINVIS : boolean; override; function IS_BANKER : boolean; override; function IS_SHOPKEEPER : boolean; override; procedure die; override; end; GPlayer = class(GCharacter) public edit_buffer : string; edit_dest : pointer; pagerlen : integer; title : string; { Title of PC } hometown : integer; { Hometown (vnum of portal) } age : longint; { Age in hours (irl) } cfg_flags, flags : cardinal; { config flags and misc. flags } deaths : integer; bankgold : longint; { Gold in bank } xptot, xptogo : longint; { Experience earned total and needed to level } fightxp : longint; rank : string; clanleader : boolean; { is clanleader? } password : string; md5_password : MD5Digest; prompt : string; remorts : integer; { remorts done } condition : array[COND_DRUNK..COND_MAX-1] of integer; area: GArea; area_fname : string; r_lo, r_hi, m_lo, m_hi, o_lo, o_hi : integer; wiz_level : integer; { level of wizinvis } bg_status, bg_points : integer; bg_room : pointer; war_points, quest_points : integer; snooping : GCharacter; trophy : array[1..15] of GTrophy; trophysize: integer; switched : GCharacter; logon_first : TDateTime; logon_now : TDateTime; played : TDateTime; wimpy : integer; aliases : GDLinkedList; pracs : integer; max_skills, max_spells : integer; bamfin, bamfout : string; taunt : string; channels : GDLinkedList; // profession:PROF_DATA; ld_timer : integer; active_board : integer; boards : array[BOARD1..BOARD_MAX-1] of integer; subject : string; constructor Create; destructor Destroy; override; published function ansiColor(color : integer) : string; override; function IS_IMMORT : boolean; override; function IS_WIZINVIS : boolean; override; function IS_HOLYWALK : boolean; override; function IS_HOLYLIGHT : boolean; override; function IS_AFK : boolean; override; function IS_KEYLOCKED : boolean; override; function IS_EDITING : boolean; override; function getUsedSkillslots() : integer; // returns nr. of skillslots occupied function getUsedSpellslots() : integer; // returns nr. of spellslots occupied function load(fn : string) : boolean; function save(fn : string) : boolean; function getAge : integer; function getPlayed : integer; procedure die; override; procedure calcRank; procedure quit; procedure sendPrompt; override; procedure sendBuffer(s : string); override; procedure sendPager(txt : string); override; procedure emptyBuffer; override; procedure startEditing(text : string); procedure stopEditing; procedure editBuffer(text : string); procedure sendEdit(text : string); end; {$M-} GExtractedCharacter = class node : GListNode; ch : GCharacter; pull : boolean; end; var char_list : GDLinkedList; extracted_chars : GDLinkedList; function findCharWorld(ch : GCharacter; name : string) : GCharacter; function findPlayerWorld(ch : GCharacter; name : string) : GPlayer; procedure cleanChars; implementation uses conns, skills, mudsystem, mudthread, Channels; constructor THistoryElement.Create(txt : string); begin inherited Create(); time := Now(); contents := hash_string(txt); end; destructor THistoryElement.Destroy(); begin unhash_string(contents); inherited Destroy(); end; constructor TChannel.Create(txt : string); begin inherited Create(); channelname := txt; history := GDLinkedList.Create(); ignored := false; end; destructor TChannel.Destroy(); var node : GListNode; he : THistoryElement; begin node := history.head; while (node <> nil) do begin he := node.element; history.remove(node); he.Free(); node := node.next; end; history.clean(); history.Free(); inherited Destroy(); end; constructor GCharacter.Create; begin inherited Create; objects := GDLinkedList.Create; affects := GDLinkedList.Create; reply := nil; master := nil; snooped_by := nil; leader := Self; tracking := ''; end; destructor GCharacter.Destroy; var obj : GObject; node : GListNode; tc : TChannel; begin affects.clean; affects.Free; if (objects.tail <> nil) then repeat obj := objects.tail.element; obj.extract; until (objects.tail = nil); objects.Free; hunting := nil; unhash_string(name); unhash_string(short); unhash_string(long); inherited Destroy; end; procedure GCharacter.extract(pull : boolean); { set pull to false if you wish for character to stay alive, e.g. in portal or so. don't set to false for NPCs - Grimlord } var ext : GExtractedCharacter; begin if (CHAR_DIED) then begin bugreport('extract_char', 'area.pas', 'ch already extracted', 'Heavy desyncing occured: attempt to extract a character twice.'); exit; end; if (room <> nil) then fromRoom; ext := GExtractedCharacter.Create; ext.ch := Self; ext.pull := pull; ext.node := extracted_chars.insertLast(ext); if (not pull) then begin if (IS_EVIL) then toRoom(findRoom(ROOM_VNUM_EVIL_PORTAL)) else toRoom(findRoom(ROOM_VNUM_GOOD_PORTAL)); end else begin if (conn <> nil) then GConnection(conn).ch := nil; char_list.remove(node_world); end; end; function GCharacter.getName() : string; begin if (name <> nil) then Result := name^ else Result := ''; end; function GCharacter.getTrust : integer; var ch : GCharacter; begin { if (conn <> nil) and (GConnection(conn).original <> nil) then ch := GConnection(conn).original else } ch := Self; if (ch.trust <> 0) then begin getTrust := ch.trust; exit; end; if (ch.IS_NPC) then getTrust := UMax(ch.level, 500) else getTrust := ch.level; end; function GCharacter.CHAR_DIED : boolean; var ext : GExtractedCharacter; node : GListNode; begin CHAR_DIED := false; if (Self = nil) then begin CHAR_DIED := true; exit; end; node := extracted_chars.head; while (node <> nil) do begin ext := node.element; if (ext.ch = Self) then begin CHAR_DIED := true; exit; end; node := node.next; end; end; procedure GCharacter.sendPrompt; begin end; procedure GCharacter.sendBuffer(s : string); begin end; procedure GCharacter.sendPager(txt : string); begin end; procedure GCharacter.emptyBuffer; begin end; // GPlayer constructor GPlayer.Create; var node : GListNode; chan : TChannel; tc : TChannel; begin inherited Create; pagerlen := 25; xptogo := round((20 * power(level, 1.2)) * (1 + (random(3) / 10))); title := 'the Newbie Adventurer'; rank := 'an apprentice'; snooping := nil; cfg_flags := CFG_ASSIST or CFG_BLANK or CFG_ANSI or CFG_AUTOPEEK; bankgold := 500; clan := nil; clanleader := false; condition[COND_FULL] := 100; condition[COND_THIRST] := 100; condition[COND_DRUNK] := 0; condition[COND_HIGH] := 0; condition[COND_CAFFEINE] := 0; logon_first := Now; ld_timer := 0; edit_buffer := ''; edit_dest := nil; aliases := GDLinkedList.Create; skills_learned := GDLinkedList.Create; if (race <> nil) then begin max_skills := race.max_skills; max_spells := race.max_spells; end else begin max_skills := 0; max_spells := 0; end; pracs := 10; // default for new players(?) channels := GDLinkedList.Create(); node := channellist.head; while (node <> nil) do begin chan := node.element; tc := TChannel.Create(chan.channelname); channels.insertLast(tc); node := node.next; end; active_board := 1; boards[BOARD1] := 0; boards[BOARD2] := 0; boards[BOARD3] := 0; boards[BOARD_NEWS] := 0; boards[BOARD_IMM] := 0; apb := 7; hp := 50 + con + random(11); max_hp:=hp; mv := 40 + (dex div 4); max_mv := mv; mana := 25; max_mana := 25; ac_mod := 0; natural_ac := 0; hitroll := 50; position := POS_STANDING; bash_timer := -2; cast_timer := 0; bashing := -2; mental_state := -10; in_command := false; if (IS_GOOD) then room := findRoom(ROOM_VNUM_GOOD_PORTAL) else if (IS_EVIL) then room := findRoom(ROOM_VNUM_EVIL_PORTAL); fighting := nil; end; destructor GPlayer.Destroy; var node, node_next : GListNode; tc : TChannel; begin aliases.clean; aliases.Free; node := channels.head; while (node <> nil) do begin node_next := node.next; tc := node.element; channels.remove(node); tc.Free(); node := node_next; end; channels.clean(); channels.Free(); inherited Destroy; end; procedure GPlayer.quit; var vict : GCharacter; node : GListNode; c : GConnection; begin emptyBuffer; c := conn; if (IS_NPC) then begin if (c <> nil) then c.send('You''re an NPC, you can''t quit!'#13#10); exit; end; if (c = nil) then write_console('(Linkless) '+ name^+ ' has logged out') else if (c <> nil) then write_console('(' + inttostr(c.socket) + ') ' + name^ + ' has logged out'); { switched check} if (conn <> nil) and (not IS_NPC) then begin c.state := CON_LOGGED_OUT; c.ch := nil; try c.thread.terminate; except write_console('could not delete thread of ' + name^); end; conn := nil; closesocket(c.socket); end else if (not IS_NPC) and (not IS_SET(GPlayer(Self).flags, PLR_LINKLESS)) then interpret(Self, 'return sub'); { perform the cleanup } if (snooping <> nil) then begin snooping.snooped_by := nil; snooping := nil; end; { if (snooped_by <> nil) then begin snooped_by.player^.snooping := nil; snooped_by.sendBuffer('No longer snooping.'#13#10); snooped_by := nil; end; } if (leader <> Self) then begin to_channel(leader, '$B$7[Group]: ' + name^ + ' has left the group.', CHANNEL_GROUP, AT_WHITE); leader := nil; end else begin node := char_list.head; while (node <> nil) do begin vict := node.element; if (vict <> Self) and ((vict.leader = Self) or (vict.master = Self)) then begin act(AT_REPORT,'You stop following $N.',false,vict,nil,Self,TO_CHAR); vict.master := nil; vict.leader := vict; end; node := node.next; end; end; save(name^); extract(true); end; function GPlayer.getAge : integer; begin getAge := 17 + (getPlayed div 1000); end; function GPlayer.getPlayed : integer; begin getPlayed := trunc(((played + (Now - logon_now)) * MSecsPerDay) / 60000); end; function GCharacter.IS_IMMORT : boolean; begin Result := false; end; function GNPC.IS_IMMORT : boolean; begin Result := inherited IS_IMMORT; if (IS_SET(act_flags, ACT_IMMORTAL)) then IS_IMMORT := true; end; function GPlayer.IS_IMMORT : boolean; begin Result := inherited IS_IMMORT; if (level >= LEVEL_IMMORTAL) then IS_IMMORT := true; end; function GCharacter.IS_NPC : boolean; begin Result := false; end; function GNPC.IS_NPC : boolean; begin Result := true; end; function GCharacter.IS_LEARNER : boolean; begin Result := false; end; function GNPC.IS_LEARNER : boolean; begin Result := IS_SET(act_flags, ACT_TEACHER); end; function GCharacter.IS_AWAKE : boolean; begin IS_AWAKE := (position <> POS_SLEEPING); end; function GCharacter.IS_INVIS : boolean; begin IS_INVIS := IS_SET(aff_flags, AFF_INVISIBLE); end; function GCharacter.IS_HIDDEN : boolean; begin IS_HIDDEN := IS_SET(aff_flags, AFF_HIDE); end; function GCharacter.IS_WIZINVIS : boolean; begin Result := false; end; function GNPC.IS_WIZINVIS : boolean; begin Result := IS_SET(act_flags, ACT_MOBINVIS) end; function GPlayer.IS_WIZINVIS : boolean; begin Result := IS_SET(flags, PLR_WIZINVIS); end; function GCharacter.IS_GOOD : boolean; begin IS_GOOD := (alignment >= 0) and (not IS_IMMORT); end; function GCharacter.IS_EVIL : boolean; begin IS_EVIL := (alignment < 0) and (not IS_IMMORT); end; function GCharacter.IS_SAME_ALIGN(vict : GCharacter) : boolean; begin IS_SAME_ALIGN := false; if (vict.IS_IMMORT or IS_IMMORT) or (IS_EVIL and vict.IS_EVIL) or (IS_GOOD and vict.IS_GOOD) then IS_SAME_ALIGN := true; end; function GCharacter.IS_FLYING : boolean; begin Result := IS_SET(aff_flags, AFF_FLYING); end; function GCharacter.IS_BANKER : boolean; begin Result := false; end; function GNPC.IS_BANKER : boolean; begin Result := IS_SET(act_flags, ACT_BANKER); end; function GCharacter.IS_SHOPKEEPER : boolean; begin Result := false; end; function GNPC.IS_SHOPKEEPER : boolean; begin Result := IS_SET(act_flags, ACT_SHOPKEEP); end; function GCharacter.IS_OUTSIDE : boolean; begin IS_OUTSIDE := (room.sector <> SECT_INSIDE) and (not IS_SET(room.flags, ROOM_INDOORS)); end; function GCharacter.IS_AFFECT(affect : integer) : boolean; begin IS_AFFECT := IS_SET(aff_flags, affect); end; function GCharacter.IS_DRUNK : boolean; begin if (IS_NPC) then IS_DRUNK := false else IS_DRUNK := (GPlayer(Self).condition[COND_DRUNK] > 80); end; function GCharacter.IS_WEARING(item_type : integer) : boolean; var node : GListNode; obj : GObject; begin node := objects.head; Result := false; while (node <> nil) do begin obj := node.element; if (obj.wear_location <> WEAR_NULL) and (obj.item_type = item_type) then begin Result := true; break; end; node := node.next; end; end; function GCharacter.IS_HOLYWALK : boolean; begin Result := false; end; function GPlayer.IS_HOLYWALK : boolean; begin Result := inherited IS_HOLYWALK; if (IS_SET(flags, PLR_HOLYWALK)) then Result := true; end; function GCharacter.IS_HOLYLIGHT : boolean; begin Result := false; end; function GPlayer.IS_HOLYLIGHT : boolean; begin Result := inherited IS_HOLYLIGHT; if (IS_SET(flags, PLR_HOLYLIGHT)) then Result := true; end; { Utility function - Nemesis } function GCharacter.IS_AFK : boolean; begin Result := false; end; function GPlayer.IS_AFK : boolean; begin if IS_SET(flags, PLR_LINKLESS) then IS_AFK := false else IS_AFK := GConnection(conn).afk = true; end; { utility function - Nemesis } function GCharacter.IS_KEYLOCKED : boolean; begin Result := false; end; function GPlayer.IS_KEYLOCKED : boolean; begin if IS_SET(flags, PLR_LINKLESS) then IS_KEYLOCKED := false else IS_KEYLOCKED := GConnection(conn).keylock = true; end; function GCharacter.IS_EDITING : boolean; begin Result := false; end; function GPlayer.IS_EDITING : boolean; begin IS_EDITING := GConnection(conn).state = CON_EDITING; end; function GCharacter.CAN_FLY : boolean; begin Result := false; if (IS_SET(aff_flags, AFF_LEVITATION)) then Result := true; end; { can ch see ? } function GCharacter.CAN_SEE(target : TObject) : boolean; var vict : GCharacter; begin CAN_SEE := true; if (Self = vict) then exit; if (not IS_AWAKE) then CAN_SEE := false; if (room.IS_DARK) and (not IS_HOLYLIGHT) and (not IS_SET(aff_flags, AFF_INFRAVISION)) then CAN_SEE := false; if (target is GCharacter) then begin vict := GCharacter(target); if (vict.IS_INVIS) and (not (IS_SET(aff_flags, AFF_DETECT_INVIS) or IS_IMMORT)) then CAN_SEE:=false; if (vict.IS_HIDDEN) and (not (IS_SET(aff_flags, AFF_DETECT_HIDDEN) or IS_IMMORT)) then CAN_SEE := false; if (vict.IS_WIZINVIS) and (level < GPlayer(vict).wiz_level) then CAN_SEE := false; end; if (IS_SET(aff_flags, AFF_BLIND)) then CAN_SEE := false; end; function GCharacter.LEARNED(skill : pointer) : integer; var node : GListNode; g : GLearned; begin Result := 0; node := skills_learned.head; while (node <> nil) do begin g := node.element; if (g.skill = skill) then begin Result := g.perc; break; end; node := node.next; end; end; // Xenon 10/Apr/2001: Modified SET_LEARNED() to remove skill from linked list when perc = 0 procedure GCharacter.SET_LEARNED(perc : integer; skill : pointer); var g : GLearned; node : GListNode; begin g := nil; node := skills_learned.head; while (node <> nil) do begin if (GLearned(node.element).skill = skill) then begin g := node.element; break; end; node := node.next; end; if (g = nil) then skills_learned.insertLast(GLearned.Create(perc, skill)) else if (perc > 0) then g.perc := perc else skills_learned.remove(node); end; function GPlayer.getUsedSkillslots() : integer; // returns nr. of skillslots occupied var node : GListNode; g : GLearned; begin Result := 0; node := skills_learned.head; while (node <> nil) do begin g := node.element; if (GSkill(g.skill).skill_type <> SKILL_SPELL) then inc(Result); node := node.next; end; end; function GPlayer.getUsedSpellslots() : integer; // returns nr. of spellslots occupied var node : GListNode; g : GLearned; begin Result := 0; node := skills_learned.head; while (node <> nil) do begin g := node.element; if (GSkill(g.skill).skill_type = SKILL_SPELL) then inc(Result); node := node.next; end; end; function GPlayer.load(fn : string) : boolean; var d, x : longint; af : GFileReader; g , a, t : string; obj : GObject; aff : GAffect; len, modif, inner : integer; s: string; sk : GSkill; al : GAlias; node : GListNode; chan : GChannel; tc : TChannel; begin inner := 0; level := 1; s := fn; s[1] := upcase(s[1]); name := hash_string(s); short := hash_string(s + ' is here'); long := hash_string(s + ' is standing here'); if (race <> nil) then alignment := race.def_alignment else alignment := 0; try af := GFileReader.Create('players\' + fn + '.usr'); except load := false; exit; end; repeat repeat s := af.readLine; s := uppercase(s); until (pos('#', s) = 1) or (af.eof); if (s = '#PLAYER') then begin inc(inner); repeat a := af.readLine; g := uppercase(left(a,':')); if (g = 'TITLE') then title := right(a,' ') else if (g ='SEX') then sex := strtoint(right(a, ' ')) else if g='RACE' then race := findRace(right(a, ' ')) else if (g = 'ALIGNMENT') then alignment := strtoint(right(a, ' ')) else if (g = 'LEVEL') then level := strtoint(right(a, ' ')) else if g='HOMETOWN' then hometown:=strtoint(right(a,' ')) else if g='AGE' then age:=strtoint(right(a,' ')) else if g='WEIGHT' then weight:=strtoint(right(a,' ')) else if g='HEIGHT' then height:=strtoint(right(a,' ')) else if g='STATS' then begin a := right(a,' '); str:=strtoint(left(a,' ')); a := right(a,' '); con:=strtoint(left(a,' ')); a := right(a,' '); dex:=strtoint(left(a,' ')); a := right(a,' '); int:=strtoint(left(a,' ')); a := right(a,' '); wis:=strtoint(left(a,' ')); end else if (g = 'MAX_SKILLS') then max_skills := strtoint(right(a,' ')) else if (g = 'MAX_SPELLS') then max_spells := strtoint(right(a,' ')) else if (g = 'PRACTICES') then pracs := strtoint(right(a,' ')) else if g='APB' then apb:=strtoint(right(a,' ')) else if g='MANA' then begin a:=right(a,' '); mana:=strtoint(left(a,' ')); a:=right(a,' '); max_mana:=strtoint(left(a,' ')); end else if g='HP' then begin a:=right(a,' '); hp:=strtoint(left(a,' ')); a:=right(a,' '); max_hp:=strtoint(left(a,' ')); end else if g='MV' then begin a:=right(a,' '); mv:=strtoint(left(a,' ')); a:=right(a,' '); max_mv:=strtoint(left(a,' ')); end else if g='AC' then ac:=strtoint(right(a,' ')) else if g='HAC' then hac:=strtoint(right(a,' ')) else if g='BAC' then bac:=strtoint(right(a,' ')) else if g='AAC' then aac:=strtoint(right(a,' ')) else if g='LAC' then lac:=strtoint(right(a,' ')) else if g='GOLD' then begin a:=right(a,' '); gold := UMax(strtointdef(left(a, ' '), 0), 0); a:=right(a,' '); bankgold := UMax(strtointdef(left(a, ' '), 0), 0); end else if g='XP' then begin a:=right(a,' '); xptot:=strtoint(left(a,' ')); a:=right(a,' '); xptogo:=strtoint(left(a,' ')); end else if g='ROOMVNUM' then room := findRoom(strtoint(right(a, ' '))) else if g='KILLS' then kills:=strtoint(right(a,' ')) else if g='DEATHS' then deaths:=strtoint(right(a,' ')) else if g='FLAGS' then flags:=strtoint(right(a,' ')) else if g='CLAN' then begin clan := findClan(right(a,' ')); if (clan <> nil) and(clan.leader = name^) then clanleader := true; end else if g='CONFIG' then cfg_flags:=strtoint(right(a,' ')) else if g='AC_MOD' then ac_mod:=strtoint(right(a,' ')) else // for backward compatibility only if g='PASSWORD' then begin password := right(a,' '); md5_password := MD5String(password); end else // the new md5 encrypted pwd if g='MD5-PASSWORD' then begin t := right(a,' '); d := 1; x := 0; while (d <= length(t)) do begin md5_password[x] := strtoint('$' + t[d] + t[d+1]); inc(x); inc(d, 2); end; end else if g='REMORTS' then remorts:=strtoint(right(a,' ')) else if g='WIMPY' then wimpy:=strtoint(right(a,' ')) else if g='AFF_FLAGS' then aff_flags:=strtoint(right(a,' ')) else if g='MENTALSTATE' then mental_state:=strtoint(right(a,' ')) else if g='CONDITION' then begin a:=right(a,' '); condition[COND_DRUNK]:=strtoint(left(a,' ')); a:=right(a,' '); condition[COND_FULL]:=strtoint(left(a,' ')); a:=right(a,' '); condition[COND_THIRST]:=strtoint(left(a,' ')); a:=right(a,' '); condition[COND_CAFFEINE]:=strtoint(left(a,' ')); a:=right(a,' '); condition[COND_HIGH]:=strtoint(left(a,' ')); end else if g='AREA' then begin area_fname := right(a,' '); area := findArea(area_fname); end else if g='RANGES' then begin a:=right(a,' '); r_lo:=strtoint(left(a,' ')); a:=right(a,' '); r_hi:=strtoint(left(a,' ')); a:=right(a,' '); m_lo:=strtoint(left(a,' ')); a:=right(a,' '); m_hi:=strtoint(left(a,' ')); a:=right(a,' '); o_lo:=strtoint(left(a,' ')); a:=right(a,' '); o_hi:=strtoint(left(a,' ')); end else if g='WIZLEVEL' then wiz_level:=strtoint(right(a,' ')) else if g='BGPOINTS' then bg_points:=strtoint(right(a,' ')) else if g='PAGELEN' then pagerlen:=strtoint(right(a,' ')) else if g='LOGON' then begin a:=right(a,' '); logon_first:=strtoint(left(a,' ')); a:=right(a,' '); logon_first:=logon_first + (strtoint(left(a,' '))/MSecsPerDay); if (logon_first = 0)then logon_first:=Now; end else if g='PLAYED' then begin a:=right(a,' '); played:=strtoint(left(a,' ')); a:=right(a,' '); played:=played + (strtoint(left(a,' '))/MSecsPerDay); end else if (g = 'BAMFIN') then bamfin := right(a, ' ') else if (g = 'BAMFOUT') then bamfout := right(a, ' ') else if (g = 'TAUNT') then taunt := right(a, ' ') else if (g = 'PROMPT') then prompt := right(a, ' ') else if (g = 'ACTIVE_BOARD') then active_board := strtoint(right(a,' ')) else if (g = 'READ-NOTES') then begin a := right(a,' '); boards[BOARD1] := strtoint(left(a,' ')); a := right(a,' '); boards[BOARD2] := strtoint(left(a,' ')); a := right(a,' '); boards[BOARD3] := strtoint(left(a,' ')); a := right(a,' '); boards[BOARD_NEWS] := strtoint(left(a,' ')); a := right(a,' '); boards[BOARD_IMM] := strtoint(left(a,' ')); end; until (uppercase(a)='#END') or (af.eof); if (uppercase(a)='#END') then dec(inner); end else if (s = '#SKILLS') then begin inc(inner); repeat a := af.readLine; if (uppercase(a) <> '#END') and (not af.eof) then begin a := right(right(a,' '),''''); g := left(a,''''); a := right(right(a,''''),' '); sk := findSkill(g); if (sk <> nil) then SET_LEARNED(strtointdef(left(a,' '), 0), sk) else bugreport('GArea.load', 'charlist.pas', 'skill '+g+' does not exist', 'The skill specified in the pfile does not exist.'); end; until (uppercase(a)='#END') or (af.eof); if (uppercase(a)='#END') then dec(inner); end else if (s = '#AFFECTS') then begin inc(inner); repeat a := af.readLine; if (uppercase(a) <> '#END') and (not af.eof) then begin aff := GAffect.Create; with aff do begin name := hash_string(left(a, ' ')); wear_msg := 'boe'; a := right(a, ' '); duration := strtointdef(left(a, ' '), 0); len := 1; while (pos('{', a) > 0) do begin a := trim(right(a, '{')); setLength(modifiers, len); modifiers[len - 1].apply_type := findApply(left(a, ' ')); a := right(a, ' '); modif := cardinal(findSkill(left(a, ' '))); if (modif = 0) then modif := strtointdef(left(a, ' '), 0); modifiers[len - 1].modifier := modif; a := trim(right(a, '}')); inc(len); end; end; aff.applyTo(Self); end; until (uppercase(a)='#END') or (af.eof); if (uppercase(a)='#END') then dec(inner); end else if (s = '#ALIASES') then begin inc(inner); repeat a := af.readLine; if (uppercase(a) <> '#END') and (not af.eof) then begin al := GAlias.Create; al.alias := left(a, ':'); al.expand := right(a, ':'); al.node := aliases.insertLast(al); end; until (uppercase(a)='#END') or (af.eof); if (uppercase(a)='#END') then dec(inner); end else if (s = '#OBJECTS') then begin inc(inner); repeat g := af.readLine; if (uppercase(g) <> '#END') and (not af.eof) then begin obj := GObject.Create; with obj do begin d := af.readInteger; if (d <> -1 ) then begin obj_index := findObjectIndex(d); if (obj_index = nil) then bugreport('load_user', 'charlist.pas', 'illegal vnum ' + inttostr(d), 'There is no index for this object.') else inc(obj_index.obj_count); end; a := af.readLine; name := hash_string(a); a := af.readLine; short := hash_string(a); a := af.readLine; long := hash_string(a); a := af.readLine; item_type:=StrToInt(left(a,' ')); a:=right(a,' '); wear1:=StrToInt(left(a,' ')); a:=right(a,' '); wear2:=StrToInt(left(a,' ')); a := af.readLine; value[1]:=StrToInt(left(a,' ')); a:=right(a,' '); value[2]:=StrToInt(left(a,' ')); a:=right(a,' '); value[3]:=StrToInt(left(a,' ')); a:=right(a,' '); value[4]:=StrToInt(left(a,' ')); a := af.readLine; weight:=StrToInt(left(a,' ')); a:=right(a,' '); flags:=StrToInt(left(a,' ')); a:=right(a,' '); cost:=StrToInt(left(a,' ')); a := right(a, ' '); count := strtointdef(left(a, ' '), 1); if (count = 0) then count := 1; room:=nil; end; obj.node_world := object_list.insertLast(obj); obj.toChar(Self); obj.wear_location := strtoint(g); if (obj.wear_location < WEAR_NULL) then obj.wear_location := WEAR_NULL; end; until (uppercase(g) = '#END') or (af.eof); if (uppercase(g) = '#END') then dec(inner); end else if (s = '#TROPHY') then begin inc(inner); repeat g := af.readLine; if (uppercase(g) <> '#END') and (not af.eof) then begin inc(trophysize); g:=right(g,' '); trophy[trophysize].name := left(g,' '); g:=right(g,' '); trophy[trophysize].level:=strtoint(left(g,' ')); g:=right(g,' '); trophy[trophysize].times:=strtoint(left(g,' ')); end; until (uppercase(g) = '#END') or (af.eof); if (uppercase(g) = '#END') then dec(inner); end; until (af.eof); af.Free; if (inner <> 0) then begin bugreport('GCharacter.load', 'chars.pas', 'bugged playerfile ' + name^, 'The pfile of this character was corrupted.'); race := GRace(race_list.head.element); end; if (race <> nil) then begin save_poison:=race.save_poison; save_cold:=race.save_cold; save_para:=race.save_para; save_breath:=race.save_breath; save_spell:=race.save_spell; hitroll:=UMax((level div 5)+50,100); end; if (max_skills = 0) then begin bugreport('GCharacter.load', 'chars.pas', 'bugged playerfile ' + name^, 'The pfile of this character lacks a Max_skills field. ' + 'Fixing this *now* by setting these fields to race-max value. ' + 'Make sure to (force) save this player.'); max_skills := race.max_skills; end; if (max_spells = 0) then begin bugreport('GCharacter.load', 'chars.pas', 'bugged playerfile ' + name^, 'The pfile of this character lacks a Max_spells field. ' + 'Fixing this *now* by setting these fields to race-max value. ' + 'Make sure to (force) save this player.'); max_spells := race.max_spells; end; calcAC; calcRank; load := true; end; function GPlayer.save(fn : string) : boolean; var f : textfile; temp : TDateTime; h : integer; node : GListNode; obj : GObject; al : GAlias; g : GLearned; aff : GAffect; fl : cardinal; begin if (IS_NPC) then begin Result := false; exit; end; assignfile(f, 'players\' + fn + '.usr'); {$I-} rewrite(f); {$I+} if (IOResult <> 0) then begin save := false; exit; end; writeln(f,'#PLAYER'); writeln(f,'User: '+name^); writeln(f,'MD5-Password: '+MD5Print(md5_password)); writeln(f,'Sex: ',sex); writeln(f,'Race: ',race.name); writeln(f,'Alignment: ',alignment); writeln(f,'Level: ',level); writeln(f,'Weight: ',weight); writeln(f,'Height: ',height); writeln(f,'aff_flags: ',aff_flags); writeln(f,'Mentalstate: ',mental_state); writeln(f,'Last-login: ', DateTimeToStr(Now)); writeln(f,'Title: ',title); writeln(f,'Home: ',hometown); writeln(f,'Age: ',age); writeln(f,'Gold: ',gold,' ',bankgold); writeln(f,'XP: ',xptot,' ',xptogo); writeln(f,'Kills: ',kills); writeln(f,'Deaths: ',deaths); writeln(f,'Practices: ', pracs); writeln(f,'Bamfin: ',bamfin); writeln(f,'Bamfout: ',bamfout); writeln(f,'Taunt: ', taunt); writeln(f,'Prompt: ', prompt); writeln(f,'Active_board: ', active_board); writeln(f,'Read-notes: ', boards[BOARD1], ' ', boards[BOARD2], ' ', boards[BOARD3], ' ', boards[BOARD_NEWS], ' ', boards[BOARD_IMM]); fl := flags; REMOVE_BIT(fl, PLR_LINKLESS); REMOVE_BIT(fl, PLR_LOADED); writeln(f,'Flags: ', fl); writeln(f,'Config: ',cfg_flags); writeln(f,'Remorts: ',remorts); writeln(f,'Wimpy: ',wimpy); writeln(f,'Logon: ',trunc(logon_first),' ',trunc(frac(logon_first)*MSecsPerDay)); temp:=played + (Now - logon_now); writeln(f,'Played: ',trunc(temp),' ',trunc(frac(temp)*MSecsPerDay)); writeln(f,'Condition: ',condition[COND_DRUNK],' ',condition[COND_FULL], ' ',condition[COND_THIRST],' ',condition[COND_CAFFEINE],' ',condition[COND_HIGH]); if clan<>nil then writeln(f,'Clan: ',clan.name); if area_fname<>'' then writeln(f,'Area: ',area_fname); writeln(f,'Ranges: ',r_lo,' ',r_hi,' ',m_lo,' ',m_hi,' ',o_lo,' ',o_hi); writeln(f,'Wizlevel: ',wiz_level); writeln(f,'BGpoints: ',bg_points); writeln(f,'Pagerlen: ',pagerlen); writeln(f,'Stats: ',str,' ',con,' ',dex,' ',int,' ',wis); writeln(f, 'Max_skills: ', max_skills); writeln(f, 'Max_spells: ', max_spells); writeln(f,'APB: ',apb); writeln(f,'Mana: ',mana,' ',max_mana); writeln(f,'HP: ',hp,' ',max_hp); writeln(f,'Mv: ',mv,' ',max_mv); writeln(f,'AC: ',ac); writeln(f,'HAC: ',hac); writeln(f,'BAC: ',bac); writeln(f,'AAC: ',aac); writeln(f,'LAC: ',lac); writeln(f,'AC_mod: ',ac_mod); writeln(f,'RoomVNum: ',room.vnum); writeln(f,'#END'); writeln(f); writeln(f,'#SKILLS'); node := skills_learned.head;; while (node <> nil) do begin g := node.element; writeln(f, 'Skill: ''', GSkill(g.skill).name^, ''' ', g.perc); node := node.next; end; writeln(f,'#END'); writeln(f); writeln(f,'#AFFECTS'); node := affects.head; while (node <> nil) do begin aff := node.element; { with aff do writeln(f,'Affect: ''', skill.name, ''' ', printApply(apply_type), ' ', duration, ' ', modifier); } node := node.next; end; writeln(f,'#END'); writeln(f); writeln(f, '#ALIASES'); node := aliases.head; while (node <> nil) do begin al := node.element; writeln(f, al.alias, ':', al.expand); node := node.next; end; writeln(f, '#END'); writeln(f); writeln(f,'#OBJECTS'); node := objects.head; while (node <> nil) do begin obj := node.element; writeln(f, obj.wear_location); if (obj.obj_index <> nil) then writeln(f,obj.obj_index.vnum) else writeln(f,-1); writeln(f,obj.name^); writeln(f,obj.short^); writeln(f,obj.long^); writeln(f,obj.item_type,' ',obj.wear1,' ',obj.wear2,' '); writeln(f,obj.value[1],' ',obj.value[2],' ',obj.value[3],' ',obj.value[4]); writeln(f,obj.weight,' ',obj.flags,' ',obj.cost, ' ', obj.count); node := node.next; end; writeln(f,'#END'); writeln(f); writeln(f,'#TROPHY'); for h:=1 to trophysize do writeln(f,'Trophy: ',trophy[h].name,' ',trophy[h].level,' ',trophy[h].times); writeln(f,'#END'); closefile(f); save := true; end; procedure GPlayer.sendBuffer(s : string); var c : GConnection; begin if (snooped_by <> nil) then GConnection(snooped_by.conn).send(s); // Xenon 21/Feb/2001: I think someone snooping still wants to see output of his own commands if (conn = nil) {or (not IS_NPC and (player^.snooping <> nil))} then exit; if (IS_EDITING) then exit; c := conn; if ((length(c.sendbuffer) + length(s)) > 2048) then begin c.send(c.sendbuffer); c.sendbuffer := ''; end; if (not in_command) and (length(c.sendbuffer) = 0) then c.sendbuffer := c.sendbuffer + #13#10; c.sendbuffer := c.sendbuffer + s; end; procedure GPlayer.sendPager(txt : string); var c : GConnection; begin if (conn = nil) then exit; c := conn; if (IS_NPC) or (not IS_SET(cfg_flags,CFG_PAGER)) then sendBuffer(txt) else c.writePager(txt); end; procedure GPlayer.emptyBuffer; var c : GConnection; begin if (conn = nil) then exit; c := conn; if (c.empty_busy) then exit; c.empty_busy := true; if (length(c.sendbuffer) > 0) then begin c.send(c.sendbuffer); sendPrompt; c.sendbuffer := ''; end; c.empty_busy := false; end; procedure GPlayer.startEditing(text : string); begin if (conn = nil) then exit; if (substate = SUB_SUBJECT) then begin sendBuffer(ansiColor(7) + #13#10 + 'Subject: '); GConnection(conn).state := CON_EDITING; exit; end; GConnection(conn).send(ansiColor(7) + #13#10 + 'Use ~ on a blank line to end. Use .h on a blank line to get help.'#13#10); GConnection(conn).send(ansiColor(7) + '----------------------------------------------------------------------'#13#10'> '); edit_buffer := text; GConnection(conn).afk := true; GConnection(conn).state := CON_EDITING; end; procedure GPlayer.stopEditing; begin sendBuffer('Ok.'#13#10); edit_buffer := ''; substate := SUB_NONE; GConnection(conn).afk := false; GConnection(conn).state := CON_PLAYING; sendBuffer('You are now back at your keyboard.'#13#10); act(AT_REPORT,'$n has returned to $s keyboard.',false,Self,nil,nil,to_room); end; procedure GPlayer.sendEdit(text : string); var note : GNote; begin case substate of SUB_NOTE: begin postNote(Self, text); edit_buffer := ''; substate := SUB_NONE; GConnection(conn).state := CON_PLAYING; GConnection(conn).afk := false; sendBuffer('Note posted.'#13#10); act(AT_REPORT,'You are now back at your keyboard.',false,Self,nil,nil,TO_CHAR); act(AT_REPORT,'$n finished $s note and is now back at the keyboard.',false,Self,nil,nil,TO_ROOM); if (active_board = BOARD_IMM) then begin act(AT_REPORT,'There is a new note on the ' + board_names[active_board] + ' board.', false, Self, nil, nil, TO_IMM); act(AT_REPORT,'Written by ' + name^ + '.',false,Self,nil,nil,TO_IMM); end else begin act(AT_REPORT,'There is a new note on the ' + board_names[active_board] + ' board.', false, Self, nil, nil, TO_ALL); act(AT_REPORT,'Written by ' + name^ + '.', false, Self, nil, nil, TO_ALL); end; exit; end; SUB_ROOM_DESC : begin interpret(Self, 'redit'); GConnection(conn).state := CON_PLAYING; GConnection(conn).afk := false; edit_buffer := ''; substate := SUB_NONE; edit_dest := nil; end else begin bugreport('GCharacter.sendEdit()', 'chars.pas', 'unrecognized substate', 'The substate this character is in has not been recognized.'); end; end; end; procedure GPlayer.editBuffer(text : string); begin if (conn = nil) then exit; if (substate = SUB_SUBJECT) then begin if (length(text) = 0) then subject := 'nil' else subject := text; substate := SUB_NOTE; startEditing(''); exit; end; if (GConnection(conn).state = CON_EDIT_HANDLE) then begin if (uppercase(text) = 'A') then begin stopEditing; exit; end; if (uppercase(text) = 'V') then begin GConnection(conn).send(ansiColor(7) + 'Current text:' + #13#10); GConnection(conn).send(ansiColor(7) + '----------------------------------------------------------------------' + #13#10); GConnection(conn).send(ansiColor(7) + edit_buffer + #13#10); GConnection(conn).send(ansiColor(7) + '(C)ontinue, (V)iew, (S)end or (A)bort? '); exit; end; if (uppercase(text) = 'C') then begin GConnection(conn).send(ansiColor(7) + 'Ok. Continue writing...' + #13#10); GConnection(conn).send(ansiColor(7) + '----------------------------------------------------------------------' + #13#10); GConnection(conn).send(ansiColor(7) + edit_buffer); GConnection(conn).state := CON_EDITING; sendPrompt; exit; end; if (uppercase(text) = 'S') then begin sendEdit(edit_buffer); exit; end; GConnection(conn).send(#13#10 + ansiColor(7) + '(C)ontinue, (V)iew, (S)end or (A)bort? '); exit; end; if (text = '~') then begin GConnection(conn).state := CON_EDIT_HANDLE; GConnection(conn).send(#13#10 + ansiColor(7) + '(C)ontinue, (V)iew, (S)end or (A)bort? '); exit; end else if (text[1] = '.') then begin text := uppercase(text); case text[2] of 'H' : begin GConnection(conn).send(ansiColor(7) + '.h this help' + #13#10); GConnection(conn).send(ansiColor(7) + '.c clear current text' + #13#10); GConnection(conn).send(ansiColor(7) + '.v see current text' + #13#10); end; 'C' : begin edit_buffer := ''; GConnection(conn).send(ansiColor(7) + 'Ok, buffer cleared.' + #13#10); end; 'V' : begin GConnection(conn).send(ansiColor(7) + 'Current text:' + #13#10); GConnection(conn).send(ansiColor(7) + '----------------------------------------------------------------------' + #13#10); GConnection(conn).send(ansiColor(7) + edit_buffer + #13#10); end else begin GConnection(conn).send(ansiColor(7) + 'Enter .h on a blank line for help.' + #13#10); end; end; sendPrompt; exit; end; edit_buffer := edit_buffer + text + #13#10; GConnection(conn).send(ansiColor(7) + '> '); end; function GCharacter.ansiColor(color : integer) : string; begin Result := ''; end; function GPlayer.ansiColor(color : integer) : string; begin if (not IS_SET(cfg_flags, CFG_ANSI)) then ansiColor := '' else ansiColor := ansiio.ANSIColor(color, 0); end; procedure GPlayer.sendPrompt; var s, pr, buf : string; c : GConnection; t : integer; begin c := conn; if (not IS_NPC) then begin if (c.state = CON_EDITING) then begin c.send('> '); exit; end; if (c.pagepoint > 0) then exit; end; if (prompt = '') then pr := '%hhp %mmv %ama (%l)> ' else pr := prompt; buf := ansiColor(7); if (bash_timer > 0) then buf := buf + '[' + inttostr(bash_timer) + '] (Bashed) '; if (bashing > 0) then buf := buf + '[' + inttostr(bashing) + '] '; if (IS_AFK) then buf := buf + '(AFK) '; if (not IS_NPC) then begin if (substate = SUB_SUBJECT) then begin c.send(' '); exit; end; if (c.state = CON_EDITING) then begin c.send('> '); exit; end; if (c.state = CON_EDIT_HANDLE) then begin c.send(' '); exit; end; if (c.pagepoint > 0) then exit; end; if (position = POS_CASTING) then buf := buf + '+'; if (IS_IMMORT) then buf := buf + '#' + inttostr(room.vnum) + ' [' + sector_types[room.sector] + '] '; if (IS_IMMORT) and (room.areacoords <> nil) then buf := buf + room.areacoords.toString() + ' '; t := 1; s := ''; while (t <= length(prompt)) do begin if (prompt[t] = '%') then begin case prompt[t + 1] of 'h': s := s + inttostr(hp); 'H': s := s + inttostr(max_hp); 'm': s := s + inttostr(mv); 'M': s := s + inttostr(max_mv); 'a': s := s + inttostr(mana); 'A': s := s + inttostr(max_mana); 'l': s := s + inttostr(level); 'x': s := s + inttostr(xptogo); 'f': begin if (fighting <> nil) and (position >= POS_FIGHTING) then begin s := s + ' [Oppnt: '; with fighting do s := s + hp_perc[UMax(round((hp / max_hp) * 5), 0)]; s := s + ']'; end; end; 't': begin if (fighting <> nil) and (position >= POS_FIGHTING) then if (fighting.fighting <> nil) and (fighting.fighting <> Self) then begin s := s + ' [' + fighting.fighting.name^ + ': '; with fighting.fighting do s := s + hp_perc[UMax(round((hp / max_hp) * 5), 0)]; s := s + ']'; end; end; else s := s + '%' + prompt[t + 1]; end; inc(t); end else s := s + prompt[t]; inc(t); end; buf := buf + act_color(Self, s, '%') + '> '; { if (snooped_by <> nil) then begin if IS_SET(snooped_by.cfg_flags,CFG_BLANK) then // Xenon 21/Feb/2001: send extra blank line if config says so GConnection(snooped_by.conn).send(#13#10); GConnection(snooped_by.conn).send(buf); end; } if IS_SET(cfg_flags,CFG_BLANK) then c.send(#13#10); c.send(buf); end; procedure GCharacter.fromRoom; begin if (room = nil) then begin bugreport('GCharacter.fromRoom', 'chars.pas', 'room null', 'Attempted to remove character from a null room.'); exit; end; room.chars.remove(node_room); if (IS_WEARING(ITEM_LIGHT)) and (room.light > 0) then inc(room.light); { Only PCs register as players, so increase the number! - Grimlord } if (not IS_NPC) then dec(room.area.nplayer); room:=nil; end; procedure GCharacter.toRoom(to_room : GRoom); var tele : GTeleport; node : GListNode; begin if (to_room = nil) then begin bugreport('GCharacter.toRoom', 'chars.pas', 'room null, moving to portal', 'Character was forced to re-move to portal.'); if (IS_IMMORT) then begin to_room := findRoom(ROOM_VNUM_IMMORTAL_PORTAL); if (to_room = nil) then begin bugreport('GCharacter.toRoom', 'chars.pas', 'immortal portal not found', 'This immortal could not be moved to the immortal portal because '+ 'it doesn''t exit. Immortals get moved to this room when they ' + 'logged out in a room that has now been removed.'#13#10 + 'To fix this, please create a room with vnum #' + IntToStr(ROOM_VNUM_IMMORTAL_PORTAL) + ', ' + 'or change the ROOM_VNUM_IMMORTAL_PORTAL constant in constants.pas to an ' + 'existing vnum and recompile Grendel.'); end; end; if (to_room = nil) then if (IS_EVIL) then to_room := findRoom(ROOM_VNUM_EVIL_PORTAL) else to_room := findRoom(ROOM_VNUM_GOOD_PORTAL); if (to_room = nil) then begin bugreport('GCharacter.toRoom', 'chars/pas', 'HELP! even portal is NULL room! what did you do?', 'There are some serious problems with the limbo area! The portal does NOT exist!'); write_console('System is unstable - prepare for a rough ride'); exit; end; end; room := to_room; if (IS_WEARING(ITEM_LIGHT)) then inc(room.light); node_room := room.chars.insertLast(Self); { Only PCs register as players, so increase the number! - Grimlord } if (not IS_NPC) then inc(to_room.area.nplayer); { check for teleports } if (IS_SET(to_room.flags, ROOM_TELEPORT)) and (to_room.teledelay>0) then begin node := teleport_list.head; while (node <> nil) do begin tele := node.element; if (tele.t_room=to_room) then exit; node := node.next; end; tele := GTeleport.Create; tele.t_room := to_room; tele.timer := to_room.teledelay; teleport_list.insertLast(tele); end; end; procedure GCharacter.die; begin addCorpse(Self); end; procedure GPlayer.die; var node : GListNode; begin inherited die; { when ch died in bg, get him back to room - Grimlord } if (bg_status = BG_PARTICIPATE) then begin hp := max_hp; bg_status := BG_NOJOIN; fromRoom; toRoom(bg_room); exit; end; extract(false); hp := 5; mana := 0; condition[COND_FULL] := 100; condition[COND_THIRST] := 100; condition[COND_DRUNK] := 0; condition[COND_HIGH] := 0; condition[COND_CAFFEINE] := 0; mv := max_mv; while (true) do begin node := affects.head; if (node = nil) then break; removeAffect(Self, node.element); end; end; procedure GNPC.die; begin inherited die; dec(npc_index.count); extract(true); dec(mobs_loaded); end; procedure GCharacter.setWait(ticks : integer); begin wait := UMax(wait, ticks); end; function GCharacter.getEQ(location : integer) : GObject; var node : GListNode; obj : GObject; begin Result := nil; node := objects.head; while (node <> nil) do begin obj := node.element; if (obj.wear_location = location) then begin Result := obj; break; end; node := node.next; end; end; function GCharacter.getWield(item_type : integer) : GObject; var obj : GOBject; begin getWield := nil; obj := getEQ(WEAR_RHAND); if (obj <> nil) and (obj.item_type = item_type) then begin getWield := obj; exit; end; obj := getEQ(WEAR_LHAND); if (obj <> nil) and (obj.item_type = item_type) then begin getWield:=obj; exit; end; end; function GCharacter.getDualWield : GObject; begin getDualWield := nil; { can't dual wield } if (LEARNED(gsn_dual_wield) = 0) then exit; if (getEQ(WEAR_RHAND) <> nil) and (getEQ(WEAR_LHAND) <> nil) then getDualWield := getEQ(WEAR_LHAND); end; procedure GCharacter.affectObject(obj : GObject; remove : boolean); var node : GListNode; aff : GAffect; begin with obj do case obj.item_type of ITEM_ARMOR: calcAC; ITEM_LIGHT: if (remove) then dec(Self.room.light) else inc(Self.room.light); ITEM_GEM: if (remove) then max_mana := max_mana - obj.value[3] else max_mana := max_mana + obj.value[3] end; if (obj.obj_index <> nil) then begin node := obj.obj_index.affects.head; while (node <> nil) do begin aff := node.element; aff.modify(Self, not remove); node := node.next; end; end; end; function GCharacter.equip(obj : GObject) : boolean; const wr_string:array[WEAR_RFINGER..WEAR_EYES, 1..2] of string = (('on your finger', 'on $s finger'), ('on your finger', 'on $s finger'), ('around your neck', 'around $s neck'), ('around your neck', 'around $s neck'), ('on your body', 'on $s body'), ('on your head', 'on $s head'), ('on your legs', 'on $s legs'), ('on your feet', 'on $s feet'), ('on your hands', 'on $s hands'), ('on your arms', 'on $s arms'), ('as your shield', 'as $s shield'), ('about your body', 'about $s body'), ('around your waist', 'around $s waist'), ('around your right wrist', 'around $s right wrist'), ('around your left wrist', 'around $s left wrist'), ('near your head', 'near $s head'), ('in your hand', 'in $s hand'), ('in your hand', 'in $s hand'), ('on your shoulder', 'on $s shoulder'), ('on your shoulder', 'on $s shoulder'), ('on your face', 'on $s face'), ('in your ear', 'in $s ear'), ('in your ear', 'in $s ear'), ('on your ankle', 'on $s ankle'), ('on your ankle', 'on $s ankle'), ('on your eyes', 'on $s eyes')); begin equip := true; if IS_SET(obj.flags,OBJ_ANTI_GOOD) and IS_GOOD then begin act(AT_REPORT,'You are zapped by $p!',false,Self,obj,nil,TO_CHAR); act(AT_REPORT,'$n is zapped by $p and burns $s hands.',false,Self,obj,nil,TO_ROOM); obj.fromChar; obj.toRoom(room); exit; end; if IS_SET(obj.flags,OBJ_ANTI_EVIL) and IS_EVIL then begin act(AT_REPORT,'You are zapped by $p!',false,Self,obj,nil,TO_CHAR); act(AT_REPORT,'$n is zapped by $p and burns $s hands.',false,Self,obj,nil,TO_ROOM); obj.fromChar; obj.toRoom(room); exit; end; if (obj.wear1 > 0) and (getEQ(obj.wear1) = nil) then { Wear on spot #1} begin act(AT_REPORT,'You wear $p ' + wr_string[obj.wear1, 1] + '.',false, Self, obj, nil, TO_CHAR); act(AT_REPORT,'$n wears $p ' + wr_string[obj.wear1, 2] + '.',false, Self, obj, nil, TO_ROOM); obj.wear_location := obj.wear1; affectObject(obj, false); end else if (obj.wear2 > 0) and (getEQ(obj.wear2) = nil) then { Wear on spot #2} begin act(AT_REPORT,'You wear $p ' + wr_string[obj.wear2, 1] + '.',false, Self, obj, nil, TO_CHAR); act(AT_REPORT,'$n wears $p ' + wr_string[obj.wear2, 2] + '.',false, Self, obj, nil, TO_ROOM); obj.wear_location := obj.wear2; affectObject(obj, false); end else { No spots left } begin act(AT_REPORT,'You are already wearing something there!',false,Self,nil,nil,TO_CHAR); equip := false; end; end; function GCharacter.calcxp2lvl : cardinal; begin calcxp2lvl := round((20*power(level,1.2))*(1+(random(2)/10))); end; procedure GCharacter.calcAC; var dex_mod:integer; node : GListNode; obj : GObject; begin dex_mod := (dex-50) div 12; hac := natural_ac - dex_mod - ac_mod; bac := natural_ac - dex_mod - ac_mod; aac := natural_ac - dex_mod - ac_mod; lac := natural_ac - dex_mod - ac_mod; node := objects.head; while (node <> nil) do begin obj := node.element; if (obj.wear_location > WEAR_NULL) and (obj.item_type = ITEM_ARMOR) then case obj.value[2] of ARMOR_HAC : dec(hac, obj.value[3]); ARMOR_BAC : dec(bac, obj.value[3]); ARMOR_AAC : dec(aac, obj.value[3]); ARMOR_LAC : dec(lac, obj.value[3]); end; node := node.next; end; ac := (hac + bac + aac + lac) div 4; end; procedure GPlayer.calcRank; var r:string; begin if level<30 then r:='an apprentice' else if level<60 then r:='a student' else if level<100 then r:='a scholar' else if level<150 then r:='knowledgeable' else if level<200 then r:='skilled' else if level<250 then r:='experienced' else if level<300 then r:='well known' else if level<350 then r:='powerful' else if level<400 then r:='brave' else if level<450 then r:='a hero' else if level<=500 then r:='a legend' else r:='a god'; rank := r; end; procedure GCharacter.startFlying; begin if (not IS_OUTSIDE) then begin sendBuffer('You cannot fly while indoors!'#13#10); exit; end; if (IS_FLYING) then begin sendBuffer('You are already flying!'#13#10); exit; end else if (CAN_FLY) then begin SET_BIT(aff_flags, AFF_FLYING); act(AT_REPORT,'You begin to fly again!',false,Self,nil,nil,TO_CHAR); act(AT_REPORT,'$n gently floats up in the air.',false,Self,nil,nil,TO_ROOM); end else begin act(AT_REPORT,'You flap your arms, but never leave the ground.',false,Self,nil,nil,TO_CHAR); act(AT_REPORT,'$n flaps $s arms to fly, but can''t.',false,Self,nil,nil,TO_ROOM); end; end; procedure GCharacter.stopFlying; begin if (IS_FLYING) then begin REMOVE_BIT(aff_flags, AFF_FLYING); act(AT_REPORT,'You slowly land on the ground.',false,Self,nil,nil,TO_CHAR); act(AT_REPORT,'$n gently lands on the ground.',false,Self,nil,nil,TO_ROOM); end; end; function GCharacter.findInventory(s : string) : GObject; var obj : GObject; node : GListNode; begin findInventory := nil; node := objects.head; while (node <> nil) do begin obj := node.element; if (obj.wear_location = WEAR_NULL) and (isObjectName(obj.name^, s) or isObjectName(obj.short^, s)) then begin findInventory := obj; exit; end; node := node.next; end; end; { Xenon 20/Feb/2001: like findInventory searches thru inv, findEquipment searches thru stuff being worn } function GCharacter.findEquipment(s : string) : GObject; var obj : GObject; node : GListNode; begin findEquipment := nil; node := objects.head; while (node <> nil) do begin obj := node.element; if (obj.wear_location <> WEAR_NULL) and (isObjectName(obj.name^, s) or isObjectName(obj.short^, s)) then begin findEquipment := obj; exit; end; node := node.next; end; end; { Added 2.<char> - Nemesis } function findCharWorld(ch : GCharacter; name : string) : GCharacter; var node : GListNode; vict : GCharacter; number,count : integer; begin findCharWorld := nil; number := findNumber(name); // eg 2.char if (uppercase(name) = 'SELF') then begin findCharWorld := ch; exit; end; count := 0; node := char_list.head; while (node <> nil) do begin vict := node.element; if (isName(vict.name^,name)) or (isName(vict.short^,name)) and (ch.CAN_SEE(vict)) then begin inc(count); if (count = number) then begin findCharWorld := vict; exit; end; end; node := node.next; end; end; function findPlayerWorld(ch : GCharacter; name : string) : GPlayer; var node : GListNode; vict : GCharacter; number,count : integer; begin Result := nil; number := findNumber(name); // eg 2.char if (uppercase(name) = 'SELF') and (not ch.IS_NPC) then begin Result := GPlayer(ch); exit; end; count := 0; node := char_list.head; while (node <> nil) do begin vict := node.element; if (isName(vict.name^,name)) or (isName(vict.short^,name)) and (ch.CAN_SEE(vict)) and (not ch.IS_NPC) then begin inc(count); if (count = number) then begin Result := GPlayer(vict); exit; end; end; node := node.next; end; end; procedure cleanChars; var ext : GExtractedCharacter; node : GListNode; begin while (true) do begin node := extracted_chars.tail; if (node = nil) then exit; ext := node.element; extracted_chars.remove(node); if (ext.pull) then ext.ch.Free; ext.free; end; end; { GLearned } constructor GLearned.Create(perc_: integer; skill_: pointer); begin inherited Create; perc := perc_; skill := skill_; end; initialization char_list := GDLinkedList.Create; extracted_chars := GDLinkedList.Create; end.