{
Summary:
Player specific functions
## $Id: player.pas,v 1.31 2004/04/15 17:47:22 druid Exp $
}
unit player;
interface
uses
Classes,
md5,
area,
dtypes,
conns,
socket,
constants,
chars;
const
PLAYER_FIELDS_HASHSIZE = 256; { estimated size of hash table for dynamic fields }
PLAYER_MAX_QUEUESIZE = 16; { max size of command queue (per client) }
{$M+}
type
{ The various states of GPlayerConnection}
GPlayerConnectionStates = ( CON_STATE_PLAYING, CON_STATE_ACCEPTED, CON_STATE_NAME, CON_STATE_PASSWORD,
CON_STATE_NEW_NAME, CON_STATE_NEW_PASSWORD, CON_STATE_NEW_RACE, CON_STATE_NEW_SEX,
CON_STATE_NEW_STATS, CON_STATE_PRESS_ENTER, CON_STATE_MOTD, CON_STATE_EDITING,
CON_STATE_LOGGED_OUT, CON_STATE_CHECK_PASSWORD );
GPlayer = class;
GPlayerConnection = class(GConnection)
protected
_ch : GPlayer;
_pagepoint : integer;
pagebuf : string;
pagecmd : char;
fcommand : boolean;
copyover : boolean;
copyover_name : string;
state : GPlayerConnectionStates;
commandQueue : TStringList;
procedure OnOpenEvent();
procedure OnInputEvent();
procedure OnTickEvent();
procedure OnOutputEvent();
procedure OnCloseEvent();
procedure clearCommandQueue();
procedure addCommandQueue(const line : string);
procedure emptyCommandQueue();
function checkAliases(line : string) : boolean;
public
destructor Destroy(); override;
constructor Create(socket : GSocket; from_copyover : boolean = false; const copyover_name : string = '');
procedure writePager(const txt : string);
procedure setPagerInput(argument : string);
procedure outputPager();
function findDualConnection(const name: string) : GPlayer;
procedure nanny(argument : string);
function isPlaying() : boolean;
function isEditing() : boolean;
procedure startEditing();
procedure stopEditing();
procedure pulse();
function stateAsString() : string;
published
property pagepoint : integer read _pagepoint write _pagepoint;
property ch: GPlayer read _ch write _ch;
end;
GPlayer = class(GCharacter)
protected
_keylock: boolean;
_afk : boolean;
_fields : GHashTable;
_title : string; { Title of PC }
function getField(name : string) : TObject;
procedure putField(name : string; obj : TObject);
public
edit_buffer : string;
edit_dest : pointer;
pagerlen : integer;
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;
switching : GCharacter;
reply : GPlayer;
trophy : array[1..15] of GTrophy;
trophysize: integer;
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;
conn : GPlayerConnection;
active_board : integer;
boards : array[BOARD1..BOARD_MAX-1] of integer;
subject : string;
constructor Create(conn : GPlayerConnection);
destructor Destroy(); override;
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 IS_DRUNK : boolean; override;
function getUsedSkillslots() : integer; // returns nr. of skillslots occupied
function getUsedSpellslots() : integer; // returns nr. of spellslots occupied
function load(const fn : string) : boolean;
function save(const fn : string) : boolean;
function getAge() : integer;
function getPlayed() : integer;
procedure die; override;
procedure calcRank();
procedure quit;
procedure sendPrompt; override;
procedure sendBuffer(const s : string); override;
procedure sendPager(const txt : string); override;
procedure emptyBuffer; override;
procedure startEditing(const text : string);
procedure stopEditing();
procedure editBuffer(text : string);
procedure sendEdit(const text : string);
property fields[name : string] : TObject read getField write putField;
property keylock : boolean read _keylock write _keylock;
property afk : boolean read _afk write _afk;
published
property title : string read _title write _title;
end;
GPlayerField = class
protected
_name : string;
public
constructor Create(const name : string);
function default() : TObject; virtual; abstract;
function fromString(const s : string) : TObject; virtual; abstract;
function toString(x : TObject) : string; virtual; abstract;
property name : string read _name;
end;
GPlayerFieldFlag = class(GPlayerField)
public
function default() : TObject; override;
function fromString(const s : string) : TObject; override;
function toString(x : TObject) : string; override;
end;
GPlayerFieldInteger = class(GPlayerField)
public
function default() : TObject; override;
function fromString(const s : string) : TObject; override;
function toString(x : TObject) : string; override;
end;
GPlayerFieldString = class(GPlayerField)
public
function default() : TObject; override;
function fromString(const s : string) : TObject; override;
function toString(x : TObject) : string; override;
end;
{$M-}
var
fieldList : GHashTable;
procedure registerField(field : GPlayerField);
procedure unregisterField(const name : string);
function findPlayerWorld(ch : GCharacter; name : string) : GCharacter;
function findPlayerWorldEx(ch : GCharacter; name : string) : GCharacter;
function existsPlayer(const name : string) : boolean;
procedure acceptConnection(list_socket : GSocket);
procedure initPlayers();
procedure cleanupPlayers();
function act_string(const acts : string; to_ch, ch : GCharacter; arg1, arg2 : pointer) : string;
function act_color(to_ch : GCharacter; const acts : string; sep : char) : string;
procedure act(atype : integer; const acts : string; hideinvis : boolean; ch : GCharacter; arg1, arg2 : pointer; typ : integer);
function playername(from_ch, to_ch : GCharacter) : string;
implementation
uses
Math,
SysUtils,
FastStrings,
FastStringFuncs,
ansiio,
timers,
console,
util,
debug,
strip,
commands,
skills,
fsys,
race,
mudsystem,
mudhelp,
clan,
events,
server,
bulletinboard,
Channels;
{ Array with symbolic names for connection states }
var
con_states : array[CON_STATE_PLAYING..CON_STATE_CHECK_PASSWORD ] of string = (
'CON_STATE_PLAYING', 'CON_STATE_ACCEPTED', 'CON_STATE_NAME',
'CON_STATE_PASSWORD', 'CON_STATE_NEW_NAME', 'CON_STATE_NEW_PASSWORD',
'CON_STATE_NEW_SEX', 'CON_STATE_NEW_RACE', 'CON_STATE_NEW_STATS',
'CON_STATE_PRESS_ENTER', 'CON_STATE_MOTD', 'CON_STATE_EDITING',
'CON_STATE_LOGGED_OUT', 'CON_STATE_CHECK_PASSWORD');
{ GPlayerConnection constructor }
constructor GPlayerConnection.Create(socket : GSocket; from_copyover : boolean = false; const copyover_name : string = '');
begin
inherited Create(socket);
FOnOpen := OnOpenEvent;
FOnClose := OnCloseEvent;
FOnTick := OnTickEvent;
FOnInput := OnInputEvent;
FOnOutput := OnOutputEvent;
state := CON_STATE_NAME;
ch := GPlayer.Create(Self);
copyover := from_copyover;
Self.copyover_name := copyover_name;
commandQueue := TStringList.Create();
end;
{ GPlayerConnection destructor }
destructor GPlayerConnection.Destroy();
begin
commandQueue.Clear();
commandQueue.Free();
inherited Destroy();
end;
{ Fired by timer 4 times per second }
procedure GPlayerConnection.pulse();
begin
inc(_idle);
if ((state = CON_STATE_NAME) and (idle > IDLE_NAME)) or
((state <> CON_STATE_PLAYING) and (idle > IDLE_NOT_PLAYING)) or
((idle > IDLE_PLAYING) and (ch <> nil) and (not ch.afk) and (not ch.IS_IMMORT)) or
((idle > IDLE_AFK) and (ch.afk)) then
begin
send(#13#10'You have been idle too long. Disconnecting.'#13#10);
Terminate();
exit;
end;
if (state = CON_STATE_PLAYING) and (not ch.in_command) then
ch.emptyBuffer();
if (state = CON_STATE_PLAYING) and (ch.wait > 0) then
dec(ch.wait);
end;
{ Event handler for OnOpen }
procedure GPlayerConnection.OnOpenEvent();
var
temp_buf : string;
begin
if (not copyover) then
begin
state := CON_STATE_NAME;
send(AnsiColor(2,0) + findHelp('M_DESCRIPTION_').text);
temp_buf := AnsiColor(6,0) + #13#10;
temp_buf := temp_buf + version_info + ', ' + version_number + '.'#13#10;
temp_buf := temp_buf + version_copyright + '.';
temp_buf := temp_buf + AnsiColor(7,0) + #13#10;
send(temp_buf);
send(#13#10#13#10'Enter your name or CREATE to create a new character.'#13#10'Please enter your name: ');
end
else
begin
state := CON_STATE_MOTD;
ch.setName(copyover_name);
ch.load(copyover_name);
send(#13#10#13#10'Gradually, the clouds form real images again, recreating the world...'#13#10);
send('Copyover complete!'#13#10);
nanny('');
end;
end;
{ Event handler for OnClose }
procedure GPlayerConnection.OnCloseEvent();
begin
if (state = CON_STATE_LOGGED_OUT) then
dec(system_info.user_cur)
else
if (not ch.CHAR_DIED) and ((state = CON_STATE_PLAYING) or (state = CON_STATE_EDITING)) then
begin
writeConsole('(' + IntToStr(socket.getDescriptor) + ') ' + ch.name + ' has lost the link');
act(AT_REPORT,'$n has lost $s link.', false, ch, nil, nil, TO_ROOM);
if (ch.level >= LEVEL_IMMORTAL) then
interpret(ch, 'return');
ch.conn := nil;
SET_BIT(ch.flags, PLR_LINKLESS);
end
else
begin
ch.Free();
end;
end;
{ Event handler for OnTick }
procedure GPlayerConnection.OnTickEvent();
begin
if (fcommand) then
begin
if (pagepoint <> 0) then
outputPager()
else
ch.emptyBuffer();
fcommand := false;
end;
emptyCommandQueue();
end;
{ Event handler for OnInput }
procedure GPlayerConnection.OnInputEvent();
var
cmdline : string;
i : integer;
begin
cmdline := trim(comm_buf);
i := pos(#13, cmdline);
if (i <> 0) then
delete(cmdline, i, 1);
i := pos(#10, cmdline);
if (i <> 0) then
delete(cmdline, i, 1);
comm_buf := '';
fcommand := true;
if (pagepoint <> 0) then
setPagerInput(cmdline)
else
case state of
CON_STATE_PLAYING: begin
if (IS_SET(ch.flags,PLR_FROZEN)) and (cmdline <> 'quit') then
begin
ch.sendBuffer('You have been frozen by the gods and cannot do anything.'#13#10);
ch.sendBuffer('To be unfrozen, send an e-mail to the administration, '+system_info.admin_email+'.'#13#10);
exit;
end;
if (not checkAliases(cmdline)) then
addCommandQueue(cmdline);
emptyCommandQueue();
end;
CON_STATE_EDITING: ch.editBuffer(cmdline);
else
nanny(cmdline);
end;
end;
{ Find aliases matching 'line' }
function GPlayerConnection.checkAliases(line : string) : boolean;
var
iterator : GIterator;
al : GAlias;
cmdline, ale, param : string;
begin
Result := false;
param := one_argument(line, cmdline);
cmdline := uppercase(cmdline);
iterator := ch.aliases.iterator();
while (iterator.hasNext()) do
begin
al := GAlias(iterator.next());
if (uppercase(al.alias) = cmdline) then
begin
ale := stringreplace(al.expand, '%', param, [rfReplaceAll]);
while (pos(':', ale) > 0) do
begin
line := left(ale, ':');
ale := right(ale, ':');
addCommandQueue(line);
end;
addCommandQueue(ale);
Result := true;
break;
end;
end;
iterator.Free();
end;
{ Event handler for OnOutput }
procedure GPlayerConnection.OnOutputEvent();
begin
ch.sendPrompt();
end;
{ Flush all lines from the command queue }
procedure GPlayerConnection.clearCommandQueue();
begin
commandQueue.Clear();
end;
{ Add a line to the command queue}
procedure GPlayerConnection.addCommandQueue(const line : string);
begin
if (commandQueue.Count < PLAYER_MAX_QUEUESIZE) then
begin
commandQueue.Add(line);
end
else
begin
ch.sendBuffer('Stop spamming all those commands!'#13#10);
end;
end;
{ Execute all lines in the command queue }
procedure GPlayerConnection.emptyCommandQueue();
begin
while (commandQueue.Count > 0) and (not Terminated) do
begin
if (ch.wait > 0) then
break;
ch.in_command := true;
interpret(ch, commandQueue[0]);
commandQueue.delete(0);
if (not ch.CHAR_DIED) then
ch.in_command := false;
end;
end;
{ Returns true if connection has state CON_STATE_PLAYING }
function GPlayerConnection.isPlaying() : boolean;
begin
Result := (state = CON_STATE_PLAYING);
end;
{ Returns true if connection has state CON_STATE_EDITING }
function GPlayerConnection.isEditing() : boolean;
begin
Result := (state = CON_STATE_EDITING);
end;
{ Sets the state to CON_STATE_EDITING }
procedure GPlayerConnection.startEditing();
begin
state := CON_STATE_EDITING;
end;
{ Sets the state to CON_STATE_PLAYING }
procedure GPlayerConnection.stopEditing();
begin
state := CON_STATE_PLAYING;
end;
{ Returns a text version of the state of the connection }
function GPlayerConnection.stateAsString() : string;
begin
Result := con_states[state];
end;
{ Find out wether 'name' is already connected }
function GPlayerConnection.findDualConnection(const name: string): GPlayer;
var
iterator : GIterator;
dual: GPlayerConnection;
begin
Result := nil;
iterator := connection_list.iterator();
while (iterator.hasNext()) do
begin
dual := GPlayerConnection(iterator.next());
// is there another conn with exactly the same name?
if (dual <> Self) and (Assigned(dual)) and Assigned(dual.ch) and (lowercase(dual.ch.name) = lowercase(name)) then
begin
Result := dual.ch;
exit;
end;
end;
iterator.Free();
end;
procedure GPlayerConnection.nanny(argument : string);
var
vict : GPlayer;
tmp : GCharacter;
iterator : GIterator;
race : GRace;
digest : MD5Digest;
top, x, temp : integer;
buf, pwd : string;
begin
case state of
CON_STATE_NAME: begin
pwd := one_argument(argument, argument);
if (length(argument) = 0) then
begin
send('Please enter your name: ');
exit;
end;
if (uppercase(argument) = 'CREATE') then
begin
if (system_info.deny_newplayers) then
begin
send(#13#10'Currently we do not accept new players. Please come back some other time.'#13#10#13#10);
send('Name: ');
exit;
end
else
begin
send(#13#10'By what name do you wish to be known? ');
state := CON_STATE_NEW_NAME;
exit;
end;
end;
if (isNameBanned(argument)) then
begin;
send('Illegal name.'#13#10);
send('Please enter your name: ');
exit;
end;
vict := findDualConnection(argument); // returns nil if player is not yet connected
if (vict <> nil) and (not vict.IS_NPC) and (vict.conn <> nil) and (cap(vict.name) = cap(argument)) then
begin
if (not MD5Match(MD5String(pwd), GPlayer(vict).md5_password)) then
begin
send(#13#10'You are already logged in under that name! Type your name and password on one line to break in.'#13#10);
Terminate();
end
else
begin
GConnection(vict.conn).Terminate();
while (not IS_SET(vict.flags, PLR_LINKLESS)) do;
vict.conn := Self;
ch := vict;
REMOVE_BIT(ch.flags,PLR_LINKLESS);
state := CON_STATE_PLAYING;
ch.sendPrompt();
end;
exit;
end;
if (not ch.load(argument)) then
begin
send(#13#10'Are you sure about that name?'#13#10'Name: ');
exit;
end;
state := CON_STATE_PASSWORD;
send('Password: ');
end;
CON_STATE_PASSWORD: begin
if (length(argument) = 0) then
begin
send('Password: ');
exit;
end;
if (not MD5Match(MD5String(argument), ch.md5_password)) then
begin
writeConsole('(' + inttostr(socket.getDescriptor) + ') Failed password');
send('Wrong password.'#13#10);
send('Password: ');
exit;
end;
vict := findDualConnection( ch.name); // returns nil if player is not dual connected
if (not Assigned(vict)) then
vict := GPlayer(findPlayerWorldEx(nil, ch.name));
if (vict <> nil) and (vict.conn = nil) then
begin
ch.Free();
ch := vict;
vict.conn := Self;
ch.ld_timer := 0;
send('You have reconnected.'#13#10);
act(AT_REPORT, '$n has reconnected.', false, ch, nil, nil, TO_ROOM);
REMOVE_BIT(ch.flags, PLR_LINKLESS);
writeConsole('(' + inttostr(socket.getDescriptor) + ') ' + ch.name + ' has reconnected');
ch.sendPrompt();
state := CON_STATE_PLAYING;
exit;
end;
if (ch.IS_IMMORT) then
send(ch.ansiColor(2) + #13#10 + findHelp('IMOTD').text)
else
send(ch.ansiColor(2) + #13#10 + findHelp('MOTD').text);
send('Press Enter.'#13#10);
state := CON_STATE_MOTD;
end;
CON_STATE_MOTD: begin
send(ch.ansiColor(6) + #13#10#13#10'Welcome, ' + ch.name + ', to this MUD. May your stay be pleasant.'#13#10);
with system_info do
begin
user_cur := connection_list.size();
if (user_cur > user_high) then
user_high := user_cur;
end;
ch.toRoom(ch.room);
act(AT_WHITE, '$n enters through a magic portal.', true, ch, nil, nil, TO_ROOM);
writeConsole('(' + inttostr(socket.getDescriptor) + ') '+ ch.name +' has logged in');
ch.node_world := char_list.insertLast(ch);
ch.logon_now := Now;
if (ch.level = LEVEL_RULER) then
interpret(ch, 'uptime')
else
ch.sendPrompt();
state := CON_STATE_PLAYING;
fcommand := true;
raiseEvent('char-login', ch);
end;
CON_STATE_NEW_NAME: begin
if (length(argument) = 0) then
begin
send('By what name do you wish to be known? ');
exit;
end;
if (FileExists('players\' + argument + '.usr')) or (findDualConnection(argument) <> nil) then
begin
send('That name is already used.'#13#10);
send('By what name do you wish to be known? ');
exit;
end;
tmp := findPlayerWorldEx(nil, argument);
if (isNameBanned(argument)) or (tmp <> nil) then
begin
send('That name cannot be used.'#13#10);
send('By what name do you wish to be known? ');
exit;
end;
if (length(argument) < 3) or (length(argument) > 15) then
begin
send('Your name must be between 3 and 15 characters long.'#13#10);
send('By what name do you wish to be known? ');
exit;
end;
ch.setName(cap(argument));
state := CON_STATE_NEW_PASSWORD;
send(#13#10'Allright, '+ch.name+', choose a password: ');
end;
CON_STATE_NEW_PASSWORD: begin
if (length(argument)=0) then
begin
send('Choose a password: ');
exit;
end;
ch.md5_password := MD5String(argument);
state := CON_STATE_CHECK_PASSWORD;
send(#13#10'Please retype your password: ');
end;
CON_STATE_CHECK_PASSWORD: begin
if (length(argument) = 0) then
begin
send('Please retype your password: ');
exit;
end;
if (not MD5Match(MD5String(argument), ch.md5_password)) then
begin
send(#13#10'Password did not match!'#13#10'Choose a password: ');
state := CON_STATE_NEW_PASSWORD;
exit;
end
else
begin
state := CON_STATE_NEW_SEX;
send(#13#10'What sex do you wish to be (M/F/N): ');
exit;
end;
end;
CON_STATE_NEW_SEX: begin
if (length(argument) = 0) then
begin
send('Choose a sex (M/F/N): ');
exit;
end;
case upcase(argument[1]) of
'M':ch.sex:=0;
'F':ch.sex:=1;
'N':ch.sex:=2;
else
begin
send('That is not a valid sex.'#13#10);
send('Choose a sex (M/F/N): ');
exit;
end;
end;
state := CON_STATE_NEW_RACE;
send(#13#10'Available races: '#13#10#13#10);
iterator := raceList.iterator();
while (iterator.hasNext()) do
begin
race := GRace(iterator.next());
if (race.convert) then
begin
buf := ' [' + ANSIColor(11,0) + race.short + ANSIColor(7,0) + '] ' + pad_string(race.name, 15);
if (race.def_alignment < 0) then
buf := buf + ANSIColor(12,0) + '<- EVIL' + ANSIColor(7,0);
buf := buf + #13#10;
send(buf);
end;
end;
iterator.Free();
send(#13#10'Choose a race: ');
end;
CON_STATE_NEW_RACE: begin
if (length(argument) = 0) then
begin
send(#13#10'Choose a race: ');
exit;
end;
race := findRace(argument);
if (race = nil) or (race.convert = false) then
begin
send('Not a valid race.'#13#10);
send(#13#10'Choose a race: ');
exit;
end;
ch.race := race;
send(race.description + #13#10#13#10);
send('250 stat points will be randomly distributed over your five attributes.'#13#10);
send('It is impossible to get a lower or a higher total of stat points.'#13#10);
with ch do
begin
top:=250;
str:=URange(25,random(UMax(top,75))+ch.race.str_bonus,75);
dec(top,str);
con:=URange(25,random(UMax(top,75))+ch.race.con_bonus,75);
dec(top,con);
dex:=URange(25,random(UMax(top,75))+ch.race.dex_bonus,75);
dec(top,dex);
int:=URange(25,random(UMax(top,75))+ch.race.int_bonus,75);
dec(top,int);
wis:=URange(25,random(UMax(top,75))+ch.race.wis_bonus,75);
dec(top,wis);
while (top>0) do
begin
x:=random(5);
case x of
0:begin
temp:=UMax(75-str,top);
if (temp>0) then
begin
dec(top,temp);
str := str + temp;
end;
end;
1:begin
temp:=UMax(75-con,top);
if (temp>0) then
begin
dec(top,temp);
con := con + temp;
end;
end;
2:begin
temp:=UMax(75-dex,top);
if (temp>0) then
begin
dec(top,temp);
dex := dex + temp;
end;
end;
3:begin
temp:=UMax(75-int,top);
if (temp>0) then
begin
dec(top,temp);
int := int + temp;
end;
end;
4:begin
temp:=UMax(75-wis,top);
if (temp>0) then
begin
dec(top,temp);
wis := wis + temp;
end;
end;
end;
end;
//top:=str+con+dex+int+wis;
end;
send(#13#10'Your character statistics are: '#13#10#13#10);
buf := 'Strength: '+ANSIColor(10,0)+inttostr(ch.str)+ANSIColor(7,0)+#13#10 +
'Constitution: '+ANSIColor(10,0)+inttostr(ch.con)+ANSIColor(7,0)+#13#10 +
'Dexterity: '+ANSIColor(10,0)+inttostr(ch.dex)+ANSIColor(7,0)+#13#10 +
'Intelligence: '+ANSIColor(10,0)+inttostr(ch.int)+ANSIColor(7,0)+#13#10 +
'Wisdom: '+ANSIColor(10,0)+inttostr(ch.wis)+ANSIColor(7,0)+#13#10;
send(buf);
send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
state := CON_STATE_NEW_STATS;
end;
CON_STATE_NEW_STATS: begin
if (length(argument) =0) then
begin
send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
exit;
end;
case (upcase(argument[1])) of
'C':begin
digest := ch.md5_password;
ch.load(ch.name);
ch.md5_password := digest;
ch.save(ch.name);
send(#13#10'Thank you. You have completed your entry.'#13#10);
send(ch.ansiColor(2) + #13#10);
if (ch.IS_IMMORT) then
send(ch.ansiColor(2) + #13#10 + findHelp('IMOTD').text)
else
send(ch.ansiColor(2) + #13#10 + findHelp('MOTD').text);
send('Press Enter.'#13#10);
state := CON_STATE_MOTD;
end;
'R':begin
with ch do
begin
top:=250;
str:=URange(25,random(UMax(top,75))+ch.race.str_bonus,75);
dec(top,str);
con:=URange(25,random(UMax(top,75))+ch.race.con_bonus,75);
dec(top,con);
dex:=URange(25,random(UMax(top,75))+ch.race.dex_bonus,75);
dec(top,dex);
int:=URange(25,random(UMax(top,75))+ch.race.int_bonus,75);
dec(top,int);
wis:=URange(25,random(UMax(top,75))+ch.race.wis_bonus,75);
dec(top,wis);
while (top>0) do
begin
x:=random(5);
case x of
0:begin
temp:=UMax(75-str,top);
if (temp>0) then
begin
dec(top,temp);
str := str + temp;
end;
end;
1:begin
temp:=UMax(75-con,top);
if (temp>0) then
begin
dec(top,temp);
con := con + temp;
end;
end;
2:begin
temp:=UMax(75-dex,top);
if (temp>0) then
begin
dec(top,temp);
dex := dex + temp;
end;
end;
3:begin
temp:=UMax(75-int,top);
if (temp>0) then
begin
dec(top,temp);
int := int + temp;
end;
end;
4:begin
temp:=UMax(75-wis,top);
if (temp>0) then
begin
dec(top,temp);
wis := wis + temp;
end;
end;
end;
end;
//top:=str+con+dex+int+wis;
end;
send(#13#10'Your character statistics are: '#13#10#13#10);
buf := 'Strength: '+ANSIColor(10,0)+inttostr(ch.str)+ANSIColor(7,0)+#13#10 +
'Constitution: '+ANSIColor(10,0)+inttostr(ch.con)+ANSIColor(7,0)+#13#10 +
'Dexterity: '+ANSIColor(10,0)+inttostr(ch.dex)+ANSIColor(7,0)+#13#10 +
'Intelligence: '+ANSIColor(10,0)+inttostr(ch.int)+ANSIColor(7,0)+#13#10 +
'Wisdom: '+ANSIColor(10,0)+inttostr(ch.wis)+ANSIColor(7,0)+#13#10;
send(buf);
send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
end;
'S':begin
send(#13#10'Very well, restarting.'#13#10);
send('By what name do you wish to be known?');
state := CON_STATE_NEW_NAME;
end;
else
send('Do you wish to (C)ontinue, (R)eroll or (S)art over? ');
exit;
end;
end;
else
bugreport('nanny', 'mudthread.pas', 'illegal state');
end;
end;
procedure GPlayerConnection.writePager(const txt : string);
begin
if (pagepoint = 0) then
begin
pagepoint := 1;
pagecmd:=#0;
end;
pagebuf := pagebuf + txt;
end;
procedure GPlayerConnection.setPagerInput(argument : string);
begin
argument := trim(argument);
if (length(argument) > 0) then
pagecmd := argument[1];
end;
procedure GPlayerConnection.outputPager();
var
c : GPlayer;
last, pclines, lines : integer;
buf : string;
begin
if (pagepoint = 0) then
exit;
{ if (original <> nil) then
c := original
else
c := ch; }
c := ch;
pclines := UMax(c.pagerlen, 5) - 2;
c.emptyBuffer;
if (pagecmd <> #0) then
send(#13#10);
case pagecmd of
'b':lines:=-1-(pclines*2);
'r':lines:=-1-pclines;
'q':begin
ch.sendBuffer(' ');
pagepoint := 0;
pagebuf := '';
exit;
end;
else
lines:=0;
end;
while (lines<0) and (pagepoint >= 1) do
begin
if (pagebuf[pagepoint] = #13) then
inc(lines);
pagepoint := pagepoint - 1;
end;
if (pagepoint < 1) then
pagepoint := 1;
lines := 0;
last := pagepoint;
while (lines < pclines) and (last <= length(pagebuf)) do
begin
if (pagebuf[last] = #0) then
break
else
if (pagebuf[last] = #13) then
inc(lines);
inc(last);
end;
if (last <= length(pagebuf)) and (pagebuf[last] = #10) then
inc(last);
if (last <> pagepoint) then
begin
buf := copy(pagebuf, pagepoint, last - pagepoint);
send(buf);
pagepoint := last;
end;
while (last <= length(pagebuf)) and (pagebuf[last] = ' ') do
inc(last);
if (last >= length(pagebuf)) then
begin
pagepoint := 0;
c.sendPrompt;
pagebuf := '';
exit;
end;
pagecmd:=#0;
send(#13#10'(C)ontinue, (R)efresh, (B)ack, (Q)uit: ');
end;
// GPlayer constructor
constructor GPlayer.Create(conn : GPlayerConnection);
var
iterator : GIterator;
chan : GUserChannel;
tc : GUserChannel;
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;
switching := nil;
reply := 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();
max_skills := 0;
max_spells := 0;
pracs := 10; // default for new players(?)
channels := GDLinkedList.Create();
iterator := channellist.iterator();
while (iterator.hasNext()) do
begin
chan := GUserChannel(iterator.next());
tc := GUserChannel.Create(chan.channelname);
channels.insertLast(tc);
end;
iterator.Free();
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;
state := STATE_IDLE;
bash_timer := -2;
cast_timer := 0;
bashing := -2;
mental_state := -10;
in_command := false;
keylock := false;
afk := false;
Self.conn := conn;
if (IS_GOOD) then
room := findRoom(ROOM_VNUM_GOOD_PORTAL)
else
if (IS_EVIL) then
room := findRoom(ROOM_VNUM_EVIL_PORTAL);
fighting := nil;
_fields := GHashTable.Create(PLAYER_FIELDS_HASHSIZE);
end;
// GPlayer destructor
destructor GPlayer.Destroy();
var
node, node_next : GListNode;
tc : GUserChannel;
begin
aliases.clear();
aliases.Free();
node := channels.head;
while (node <> nil) do
begin
node_next := node.next;
tc := GUserChannel(node.element);
channels.remove(node);
tc.Free();
node := node_next;
end;
channels.clear();
channels.Free();
skills_learned.clear();
skills_learned.Free();
_fields.clear();
_fields.Free();
inherited Destroy;
end;
function GPlayer.getField(name : string) : TObject;
begin
if (_fields = nil) then
Result := GPlayerField(fieldList[name]).default()
else
begin
name := prep(name);
if (_fields[name] = nil) then
_fields[name] := GPlayerField(fieldList[name]).default();
Result := _fields[name];
end;
end;
procedure GPlayer.putField(name : string; obj : TObject);
begin
if (_fields = nil) then
exit;
name := prep(name);
if (_fields[name] <> nil) then
begin
_fields[name].Free();
_fields.remove(name);
end;
_fields[name] := obj;
end;
// Quit procedure
procedure GPlayer.quit();
var
vict : GCharacter;
iterator : GIterator;
begin
raiseEvent('char-logout', Self);
emptyBuffer();
if (conn = nil) then
writeConsole('(Linkless) '+ name + ' has logged out')
else
if (conn <> nil) then
writeConsole('(' + IntToStr(conn.socket.getDescriptor) + ') ' + name + ' has logged out');
{ switched check}
if (conn <> nil) and (not IS_NPC) then
begin
conn.state := CON_STATE_LOGGED_OUT;
try
conn.Terminate();
except
writeConsole('could not delete thread of ' + name);
end;
conn := nil;
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 (switching <> nil) then
begin
switching.snooped_by := nil;
switching := 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
iterator := char_list.iterator();
while (iterator.hasNext()) do
begin
vict := GCharacter(iterator.next());
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;
end;
iterator.Free();
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;
// Player is Immortal
function GPlayer.IS_IMMORT : boolean;
begin
Result := inherited IS_IMMORT;
if (level >= LEVEL_IMMORTAL) then
IS_IMMORT := true;
end;
// Player is wizinvis
function GPlayer.IS_WIZINVIS : boolean;
begin
Result := IS_SET(flags, PLR_WIZINVIS);
end;
// Player has holywalk
function GPlayer.IS_HOLYWALK : boolean;
begin
Result := inherited IS_HOLYWALK;
if (IS_SET(flags, PLR_HOLYWALK)) then
Result := true;
end;
// Player has holylight
function GPlayer.IS_HOLYLIGHT : boolean;
begin
Result := inherited IS_HOLYLIGHT;
if (IS_SET(flags, PLR_HOLYLIGHT)) then
Result := true;
end;
// Player is AFK
function GPlayer.IS_AFK : boolean;
begin
if IS_SET(flags, PLR_LINKLESS) then
IS_AFK := false
else
IS_AFK := afk = true;
end;
// Player has a locked keyboard
function GPlayer.IS_KEYLOCKED : boolean;
begin
if IS_SET(flags, PLR_LINKLESS) then
IS_KEYLOCKED := false
else
IS_KEYLOCKED := keylock = true;
end;
// Player is editing (writing a note)
function GPlayer.IS_EDITING : boolean;
begin
IS_EDITING := (conn.isEditing());
end;
// Player is drunk
function GPlayer.IS_DRUNK : boolean;
begin
IS_DRUNK := (condition[COND_DRUNK] > 80);
end;
// Returns nr. of skillslots occupied
function GPlayer.getUsedSkillslots() : integer;
var
iterator : GIterator;
g : GLearned;
begin
Result := 0;
iterator := skills_learned.iterator();
while (iterator.hasNext()) do
begin
g := GLearned(iterator.next());
if (GSkill(g.skill).skill_type <> SKILL_SPELL) then
inc(Result);
end;
iterator.Free();
end;
// Returns nr. of spellslots occupied
function GPlayer.getUsedSpellslots() : integer;
var
iterator : GIterator;
g : GLearned;
begin
Result := 0;
iterator := skills_learned.iterator();
while (iterator.hasNext()) do
begin
g := GLearned(iterator.next());
if (GSkill(g.skill).skill_type = SKILL_SPELL) then
inc(Result);
end;
iterator.Free();
end;
// Load player file
function GPlayer.load(const fn : string) : boolean;
var
num, 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;
iterator : GIterator;
tc : GUserChannel;
begin
inner := 0;
level := 1;
s := fn;
s[1] := upcase(s[1]);
try
af := GFileReader.Create('players\' + fn + '.usr');
except
load := false;
exit;
end;
_name := hash_string(s);
_short := hash_string(s + ' is here');
_long := hash_string(s + ' is standing here');
repeat
repeat
s := af.readLine;
s := uppercase(s);
until (pos('#', s) = 1) or (af.eof);
if (s = '#PLAYER') then
begin
inc(inner);
a := af.readLine();
while (a <> '#END') and (not af.eof()) do
begin
g := uppercase(left(a,':'));
if (g = 'USER') then
else
if (g = 'LAST-LOGIN') then
else
if (g = 'TITLE') then
title := right(a,' ')
else
if (g ='SEX') then
sex := strtoint(right(a, ' '))
else
if (g = 'RACE') then
begin
race := findRace(right(a, ' '));
if (race = nil) then
begin
bugreport('GPlayer.load', 'chars.pas', 'Unknown race ' + right(a, ' ') + ', reverting to default instead');
race := GRace(raceList.head.element);
end;
end
else
if (g = 'ALIGNMENT') then
alignment := strtoint(right(a, ' '))
else
if (g = 'LEVEL') then
level := UMin(strtoint(right(a, ' ')), LEVEL_MAX_IMMORTAL)
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 = 'PAGERLEN') 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
else
if (g = 'IGNORE') then
begin
iterator := channels.iterator();
while (iterator.hasNext()) do
begin
tc := GUserChannel(iterator.next());
if (tc.channelname = right(a, ' ')) then
tc.ignored := true;
end;
end
else
begin
if (fieldList[g] <> nil) then
_fields[g] := GPlayerField(fieldList[g]).fromString(right(a, ' '))
else
writeConsole('Dropping unknown field "' + g + '"');
end;
a := af.readLine();
end;
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');
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
a := right(a, '''');
name := left(a, '''');
a := right(right(a, ''''), '''');
wear_msg := left(a, '''');
a := trim(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, ' ');
try
modif := strtoint(left(a, ' '));
except
modif := cardinal(hash_string(left(a, ' ')));
end;
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);
g := af.readLine;
repeat
if (uppercase(g) <> '#END') and (not af.eof) then
begin
obj := GObject.Create();
obj.worn := g;
if (obj.worn = 'none') then
obj.worn := '';
with obj do
begin
vnum := af.readInteger();
name := af.readLine();
short := af.readLine();
long := af.readLine();
a := af.readLine;
item_type :=StrToInt(left(a,' '));
a := right(a,' ');
wear_location1 := left(a,' ');
a := right(a,' ');
wear_location2 := 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;
g := af.readToken();
if (g = 'A') then
begin
aff := GAffect.Create();
aff.name := af.readToken();
aff.wear_msg := '';
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);
g := af.readToken();
modif := cardinal(findSkill(g));
if (modif = 0) then
modif := strtointdef(g, 0);
aff.modifiers[num - 1].modifier := modif;
g := af.readToken();
inc(num);
end;
obj.affects.insertLast(aff);
g := af.readLine();
end;
end;
objectList.add(obj);
obj.toChar(Self);
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('GPlayer.load', 'chars.pas', 'corrupted playerfile ' + name);
race := GRace(raceList.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);
node := race.abilities.head;
while (node <> nil) do
begin
SET_LEARNED(100, GSkill(node.element));
node := node.next;
end;
end
else
begin
bugreport('GPlayer.load', 'chars.pas', 'corrupted playerfile ' + name);
race := GRace(raceList.head.element);
end;
if (max_skills = 0) then
max_skills := race.max_skills;
if (max_spells = 0) then
max_spells := race.max_spells;
calcAC();
calcRank();
// backwards compatibility fixes
REMOVE_BIT(aff_flags, AFF_BASHED);
REMOVE_BIT(aff_flags, AFF_STUNNED);
load := true;
end;
// Save player
function GPlayer.save(const fn : string) : boolean;
var
af : GFileWriter;
temp : TDateTime;
h : integer;
obj : GObject;
al : GAlias;
g : GLearned;
aff : GAffect;
fl : cardinal;
tc : GUserChannel;
iterator, inner_iterator : GIterator;
w1, w2 : string;
field : GPlayerField;
begin
if (IS_NPC) then
begin
Result := false;
exit;
end;
try
af := GFileWriter.Create('players\' + fn + '.usr');
except
save := false;
exit;
end;
// reset the character to a basic state (without affects) before writing
iterator := affects.iterator();
while (iterator.hasNext()) do
begin
aff := GAffect(iterator.next());
aff.modify(Self, false);
end;
try
af.writeLine('#PLAYER');
af.writeLine('User: ' + name);
af.writeLine('MD5-Password: ' + MD5Print(md5_password));
af.writeLine('Sex: ' + IntToStr(sex));
af.writeLine('Race: ' + race.name);
af.writeLine('Alignment: ' + IntToStr(alignment));
af.writeLine('Level: ' + IntToStr(level));
af.writeLine('Weight: ' + IntToStr(weight));
af.writeLine('Height: ' + IntToStr(height));
af.writeLine('aff_flags: ' + IntToStr(aff_flags));
af.writeLine('Mentalstate: ' + IntToStr(mental_state));
af.writeLine('Last-login: ' + DateTimeToStr(Now));
af.writeLine('Title: ' + title);
af.writeLine('Age: ' + IntToStr(age));
af.writeLine('Gold: ' + IntToStr(gold) + ' ' + IntToStr(bankgold));
af.writeLine('XP: ' + IntToStr(xptot) + ' ' + IntToStr(xptogo));
af.writeLine('Kills: ' + IntToStr(kills));
af.writeLine('Deaths: ' + IntToStr(deaths));
af.writeLine('Practices: ' + IntToStr( pracs));
af.writeLine('Bamfin: ' + bamfin);
af.writeLine('Bamfout: ' + bamfout);
af.writeLine('Taunt: ' + taunt);
af.writeLine('Prompt: ' + prompt);
af.writeLine('Active_board: ' + IntToStr( active_board));
af.writeLine('Read-notes: ' + IntToStr(boards[BOARD1]) + ' ' + IntToStr(boards[BOARD2]) + ' ' + IntToStr(boards[BOARD3]) + ' ' + IntToStr(boards[BOARD_NEWS]) + ' ' + IntToStr(boards[BOARD_IMM]));
fl := flags;
REMOVE_BIT(fl, PLR_LINKLESS);
REMOVE_BIT(fl, PLR_LOADED);
af.writeLine('Flags: ' + IntToStr(fl));
af.writeLine('Config: ' + IntToStr(cfg_flags));
af.writeLine('Remorts: ' + IntToStr(remorts));
af.writeLine('Wimpy: ' + IntToStr(wimpy));
af.writeLine('Logon: ' + IntToStr(trunc(logon_first)) + ' ' + IntToStr(trunc(frac(logon_first)*MSecsPerDay)));
temp:=played + (Now - logon_now);
af.writeLine('Played: ' + IntToStr(trunc(temp)) + ' ' + IntToStr(trunc(frac(temp)*MSecsPerDay)));
af.writeLine('Condition: ' + IntToStr(condition[COND_DRUNK]) + ' ' + IntToStr(condition[COND_FULL]) +
' ' + IntToStr(condition[COND_THIRST]) + ' ' + IntToStr(condition[COND_CAFFEINE]) + ' ' + IntToStr(condition[COND_HIGH]));
if clan<>nil then
af.writeLine('Clan: ' + clan.name);
if area_fname<>'' then
af.writeLine('Area: ' + area_fname);
af.writeLine('Ranges: ' + IntToStr(r_lo) + ' ' + IntToStr(r_hi) + ' ' + IntToStr(m_lo) + ' ' + IntToStr(m_hi) + ' ' + IntToStr(o_lo) + ' ' + IntToStr(o_hi));
af.writeLine('Wizlevel: ' + IntToStr(wiz_level));
af.writeLine('BGpoints: ' + IntToStr(bg_points));
af.writeLine('Pagerlen: ' + IntToStr(pagerlen));
af.writeLine('Stats: ' + IntToStr(str) + ' ' + IntToStr(con) + ' ' + IntToStr(dex) + ' ' + IntToStr(int) + ' ' + IntToStr(wis));
af.writeLine('Max_skills: ' + IntToStr(max_skills));
af.writeLine('Max_spells: ' + IntToStr(max_spells));
af.writeLine('APB: ' + IntToStr(apb));
af.writeLine('Mana: ' + IntToStr(mana) + ' ' + IntToStr(max_mana));
af.writeLine('HP: ' + IntToStr(hp) + ' ' + IntToStr(max_hp));
af.writeLine('Mv: ' + IntToStr(mv) + ' ' + IntToStr(max_mv));
af.writeLine('AC: ' + IntToStr(ac));
af.writeLine('HAC: ' + IntToStr(hac));
af.writeLine('BAC: ' + IntToStr(bac));
af.writeLine('AAC: ' + IntToStr(aac));
af.writeLine('LAC: ' + IntToStr(lac));
af.writeLine('AC_mod: ' + IntToStr(ac_mod));
af.writeLine('RoomVNum: ' + IntToStr(room.vnum));
iterator := channels.iterator();
while (iterator.hasNext()) do
begin
tc := GUserChannel(iterator.next());
if (tc.ignored) then
af.writeLine('Ignore: ' + tc.channelname);
end;
iterator.Free();
iterator := fieldList.iterator();
while (iterator.hasNext()) do
begin
field := GPlayerField(iterator.next());
af.writeLine(field.name + ': ' + field.toString(fields[field.name]));
end;
iterator.Free();
af.writeLine('#END');
af.writeLine('');
af.writeLine('#SKILLS');
iterator := skills_learned.iterator();
while (iterator.hasNext()) do
begin
g := GLearned(iterator.next());
af.writeLine( 'Skill: ''' + GSkill(g.skill).name + ''' ' + IntToStr(g.perc));
end;
iterator.Free();
af.writeLine('#END');
af.writeLine('');
af.writeLine('#AFFECTS');
iterator := affects.iterator();
while (iterator.hasNext()) do
begin
aff := GAffect(iterator.next());
with aff do
begin
af.writeString('''' + name + ''' ''' + wear_msg + ''' ');
af.writeInteger(duration);
for h := 0 to length(modifiers) - 1 do
begin
af.writeString(' { ');
af.writeString(printApply(modifiers[h].apply_type) + ' ');
case modifiers[h].apply_type of
APPLY_STRIPNAME: af.writeString(PString(modifiers[h].modifier)^);
else
af.writeInteger(modifiers[h].modifier);
end;
af.writeString(' }');
end;
af.writeLine('');
end;
end;
iterator.Free();
af.writeLine('#END');
af.writeLine('');
af.writeLine( '#ALIASES');
iterator := aliases.iterator();
while (iterator.hasNext()) do
begin
al := GAlias(iterator.next());
af.writeLine(al.alias + ':' + al.expand);
end;
iterator.Free();
af.writeLine( '#END');
af.writeLine('');
af.writeLine('#OBJECTS');
iterator := inventory.iterator();
while (iterator.hasNext()) do
begin
obj := GObject(iterator.next());
af.writeLine('none');
af.writeLine(IntToStr(obj.vnum));
af.writeLine(obj.name);
af.writeLine(obj.short);
af.writeLine(obj.long);
af.writeLine(IntToStr(obj.item_type) + ' ' + obj.wear_location1 + ' ' + obj.wear_location2);
af.writeLine(IntToStr(obj.value[1]) + ' ' + IntToStr(obj.value[2]) + ' ' + IntToStr(obj.value[3]) + ' ' + IntToStr(obj.value[4]));
af.writeLine(IntToStr(obj.weight) + ' ' + IntToStr(obj.flags) + ' ' + IntToStr(obj.cost) + ' ' + IntToStr(obj.count));
if (obj.affects.size() > 0) then
begin
inner_iterator := obj.affects.iterator();
while (inner_iterator.hasNext()) do
begin
aff := GAffect(inner_iterator.next());
with aff do
begin
af.writeString('A "' + name + '" ');
af.writeInteger(duration);
for h := 0 to length(modifiers) - 1 do
begin
af.writeString(' { ');
af.writeString(printApply(modifiers[h].apply_type) + ' ');
case modifiers[h].apply_type of
APPLY_STRIPNAME: af.writeString(PString(modifiers[h].modifier)^);
else
af.writeInteger(modifiers[h].modifier);
end;
af.writeString(' }');
end;
af.writeLine('');
end;
end;
inner_iterator.Free();
end;
end;
iterator.Free();
iterator := equipment.iterator();
while (iterator.hasNext()) do
begin
obj := GObject(iterator.next());
af.writeLine(obj.worn);
af.writeLine(IntToStr(obj.vnum));
af.writeLine(obj.name);
af.writeLine(obj.short);
af.writeLine(obj.long);
if (obj.wear_location1 = '') then
w1 := 'none'
else
w1 := obj.wear_location1;
if (obj.wear_location2 = '') then
w2 := 'none'
else
w2 := obj.wear_location2;
af.writeLine(IntToStr(obj.item_type) + ' ' + w1 + ' ' + w2);
af.writeLine(IntToStr(obj.value[1]) + ' ' + IntToStr(obj.value[2]) + ' ' + IntToStr(obj.value[3]) + ' ' + IntToStr(obj.value[4]));
af.writeLine(IntToStr(obj.weight) + ' ' + IntToStr(obj.flags) + ' ' + IntToStr(obj.cost) + ' ' + IntToStr(obj.count));
if (obj.affects.size() > 0) then
begin
inner_iterator := obj.affects.iterator();
while (inner_iterator.hasNext()) do
begin
aff := GAffect(inner_iterator.next());
with aff do
begin
af.writeString('A "' + name + '" ');
af.writeInteger(duration);
for h := 0 to length(modifiers) - 1 do
begin
af.writeString(' { ');
af.writeString(printApply(modifiers[h].apply_type) + ' ');
case modifiers[h].apply_type of
APPLY_STRIPNAME: af.writeString(PString(modifiers[h].modifier)^);
else
af.writeInteger(modifiers[h].modifier);
end;
af.writeString(' }');
end;
af.writeLine('');
end;
end;
inner_iterator.Free();
end;
end;
iterator.Free();
af.writeLine('#END');
af.writeLine('');
af.writeLine('#TROPHY');
for h := 1 to trophysize do
af.writeLine('Trophy: ' + trophy[h].name + ' ' + IntToStr(trophy[h].level) + ' ' + IntToStr(trophy[h].times));
af.writeLine('#END');
finally
af.Free();
end;
// re-apply affects to character
iterator := affects.iterator();
while (iterator.hasNext()) do
begin
aff := GAffect(iterator.next());
aff.modify(Self, true);
end;
save := true;
end;
procedure GPlayer.sendBuffer(const s : string);
begin
if (snooped_by <> nil) then
begin
if (not snooped_by.IS_NPC) then
GConnection(GPlayer(snooped_by).conn).send(s);
end;
// Xenon 21/Feb/2001: I think someone snooping still wants to see output of his own commands
// Grimlord 18/Aug/2002: A switched player should not, too much clutter
if (conn = nil) or (not IS_NPC and (switching <> nil)) then
exit;
if (IS_EDITING) then
exit;
GConnection(conn).writeBuffer(s, in_command);
end;
procedure GPlayer.sendPager(const txt : string);
begin
if (conn = nil) then
exit;
if (IS_NPC) or (not IS_SET(cfg_flags,CFG_PAGER)) then
sendBuffer(txt)
else
conn.writePager(txt);
end;
procedure GPlayer.emptyBuffer();
begin
if (conn = nil) then
exit;
conn.emptyBuffer();
end;
// Start editing mode
procedure GPlayer.startEditing(const text : string);
begin
if (conn = nil) then
exit;
conn.startEditing();
if (substate = SUB_SUBJECT) then
begin
sendBuffer(ansiColor(7) + #13#10 + 'Subject: ');
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;
afk := true;
end;
// Return from editing mode
procedure GPlayer.stopEditing();
begin
sendBuffer('Ok.'#13#10);
edit_buffer := '';
substate := SUB_NONE;
afk := false;
conn.stopEditing();
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(const text : string);
begin
case substate of
SUB_NOTE:
begin
postNote(Self, text);
edit_buffer := '';
substate := SUB_NONE;
conn.stopEditing();
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');
conn.stopEditing();
afk := false;
edit_buffer := '';
substate := SUB_NONE;
edit_dest := nil;
end
else
begin
bugreport('GPlayer.sendEdit()', 'chars.pas', 'unrecognized substate');
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 (length(text) > 0) and (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);
GConnection(conn).send(ansiColor(7) + '.w to write and quit' + #13#10);
GConnection(conn).send(ansiColor(7) + '.q to quit without writing' + #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;
'W' :
begin
sendEdit(edit_buffer);
end;
'Q' :
begin
stopEditing();
end;
else
GConnection(conn).send(ansiColor(7) + 'Enter .h on a blank line for help.' + #13#10);
end;
sendPrompt();
exit;
end;
edit_buffer := edit_buffer + text + #13#10;
GConnection(conn).send(ansiColor(7) + '> ');
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;
// Send prompt
procedure GPlayer.sendPrompt();
var
s, pr, buf : string;
t : integer;
begin
if (conn.isEditing()) then
begin
conn.send('> ');
exit;
end;
if (conn.pagepoint > 0) then
exit;
if (prompt = '') then
pr := '%hhp %mmv %ama (%l)%t%f> '
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
conn.send(' ');
exit;
end;
if (conn.isEditing()) then
begin
conn.send('> ');
exit;
end;
if (conn.pagepoint > 0) then
exit;
end;
if (hasTimer(Self, TIMER_ACTION) <> nil) 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(pr)) do
begin
if (pr[t] = '%') then
begin
case pr[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 (state = STATE_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 = STATE_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 + '%' + pr[t + 1];
end;
inc(t);
end
else
s := s + pr[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
conn.send(#13#10 + buf)
else
conn.send(buf);
end;
// Player dies
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, GAffect(node.element));
end;
end;
// Calculate rank
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;
// GPlayerField
constructor GPlayerField.Create(const name : string);
begin
inherited Create();
_name := prep(name);
end;
// GPlayerFieldFlag
function GPlayerFieldFlag.default() : TObject;
begin
Result := GBitVector.Create(0);
end;
function GPlayerFieldFlag.fromString(const s : string) : TObject;
begin
Result := GBitVector.Create(StrToIntDef(s, 0));
end;
function GPlayerFieldFlag.toString(x : TObject) : string;
begin
Result := IntToStr((x as GBitVector).value);
end;
// GPlayerFieldInteger
function GPlayerFieldInteger.default() : TObject;
begin
Result := GInteger.Create(0);
end;
function GPlayerFieldInteger.fromString(const s : string) : TObject;
begin
Result := GInteger.Create(StrToIntDef(s, 0));
end;
function GPlayerFieldInteger.toString(x : TObject) : string;
begin
Result := IntToStr((x as GInteger).value);
end;
// GPlayerFieldString
function GPlayerFieldString.default() : TObject;
begin
Result := GString.Create('');
end;
function GPlayerFieldString.fromString(const s : string) : TObject;
begin
Result := GString.Create(s);
end;
function GPlayerFieldString.toString(x : TObject) : string;
begin
Result := (x as GString).value;
end;
procedure registerField(field : GPlayerField);
begin
if (fieldList[field.name] <> nil) then
fieldList.remove(field.name);
fieldList[field.name] := field;
end;
procedure unregisterField(const name : string);
begin
fieldList.remove(prep(name));
end;
// Find player by name
function findPlayerWorld(ch : GCharacter; name : string) : GCharacter;
var
iterator : GIterator;
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 := ch;
exit;
end;
count := 0;
iterator := char_list.iterator();
while (iterator.hasNext()) do
begin
vict := GCharacter(iterator.next());
if ((isName(vict.name,name)) or (isName(vict.short,name))) and (not vict.IS_NPC) then
begin
if (ch <> nil) and (not ch.CAN_SEE(vict)) then
continue;
inc(count);
if (count = number) then
begin
Result := vict;
exit;
end;
end;
end;
iterator.Free();
end;
function findPlayerWorldEx(ch : GCharacter; name : string) : GCharacter;
var
iterator : GIterator;
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 := ch;
exit;
end;
count := 0;
iterator := char_list.iterator();
while (iterator.hasNext()) do
begin
vict := GCharacter(iterator.next());
if (lowercase(vict.name) = lowercase(name)) and (not vict.IS_NPC) then
begin
if (ch <> nil) and (not ch.CAN_SEE(vict)) then
continue;
inc(count);
if (count = number) then
begin
Result := vict;
exit;
end;
end;
end;
iterator.Free();
end;
function existsPlayer(const name : string) : boolean;
begin
Result := FileExists('players\' + name + '.usr');
end;
procedure acceptConnection(list_socket : GSocket);
var
ac : GSocket;
conn : GPlayerConnection;
begin
ac := list_socket.acceptConnection(system_info.lookup_hosts);
ac.setNonBlocking();
if (isMaskBanned(ac.hostString)) then
begin
writeConsole('(' + IntToStr(ac.getDescriptor) + ') Closed banned IP (' + ac.hostString + ')');
ac.send(system_info.mud_name + #13#10#13#10);
ac.send('Your site has been banned from this server.'#13#10);
ac.send('For more information, please mail the administration, ' + system_info.admin_email + '.'#13#10);
ac.Free();
end
else
if (not serverBooted) then
begin
ac.send(system_info.mud_name+#13#10#13#10);
ac.send('Currently, this server is in the process of a reboot.'#13#10);
ac.send('Please try again later.'#13#10);
ac.send('For more information, mail the administration, '+system_info.admin_email+'.'#13#10);
ac.Free();
end
else
if system_info.deny_newconns then
begin
ac.send(system_info.mud_name+#13#10#13#10);
ac.send('Currently, this server is refusing new connections.'#13#10);
ac.send('Please try again later.'#13#10);
ac.send('For more information, mail the administration, '+system_info.admin_email+'.'#13#10);
ac.Free();
end
else
if (connection_list.size() >= system_info.max_conns) then
begin
ac.send(system_info.mud_name+#13#10#13#10);
ac.send('Currently, this server is too busy to accept new connections.'#13#10);
ac.send('Please try again later.'#13#10);
ac.send('For more information, mail the administration, '+system_info.admin_email+'.'#13#10);
ac.Free();
end
else
begin
conn := GPlayerConnection.Create(ac, false, '');
conn.Resume();
end;
end;
procedure initPlayers();
begin
fieldList := GHashTable.Create(PLAYER_FIELDS_HASHSIZE);
end;
procedure cleanupPlayers();
begin
fieldList.clear();
fieldList.Free();
end;
function playername(from_ch, to_ch : GCharacter) : string;
begin
if (not to_ch.CAN_SEE(from_ch)) then
playername := 'someone'
else
if (not to_ch.IS_NPC) and (not from_ch.IS_NPC) then
begin
if (from_ch.IS_IMMORT) then
playername := from_ch.name
else
if (not to_ch.IS_SAME_ALIGN(from_ch)) then
begin
if (from_ch.race.name[1] in ['A','E','O','I','U']) then
playername := '+* An ' + from_ch.race.name + ' *+'
else
playername := '+* A ' + from_ch.race.name + ' *+';
end
else
playername := from_ch.name;
end
else
playername := from_ch.name;
if (from_ch = to_ch) then
playername := 'you';
end;
function act_color(to_ch : GCharacter; const acts : string; sep : char) : string;
var
last, current : integer;
boldflag : boolean;
res : string;
begin
last := 1;
current := FastCharPos(acts, sep, last);
boldflag := false;
res := '';
while (current <> 0) do
begin
if (current - last > 0) then
res := res + CopyStr(acts, last, current - last);
last := current + 2;
case acts[current + 1] of
'B': boldflag := true;
'A': boldflag := false;
'0'..'9': begin
if (boldflag) then
res := res + to_ch.ansiColor(strtoint(acts[current + 1]) + 8)
else
res := res + to_ch.ansiColor(strtoint(acts[current + 1]));
end;
end;
current := FastCharPos(acts, sep, last);
end;
Result := res + CopyStr(acts, last, length(acts) - last + 1)
end;
function act_string(const acts : string; to_ch, ch : GCharacter; arg1, arg2 : pointer) : string;
var
last, current : integer;
res, temp : string;
vch : GCharacter;
obj1, obj2 : TObject;
ex : GExit;
begin
last := 1;
current := FastCharPos(acts, '$', last);
res := '';
vch := arg2;
obj1 := arg1; obj2 := arg2;
while (current <> 0) do
begin
if (current - last > 0) then
res := res + CopyStr(acts, last, current - last);
last := current + 2;
case acts[current + 1] of
'n': res := res + playername(ch, to_ch);
'N': begin
if (vch = nil) then
writeConsole('[BUG]: act() -> vch null')
else
res := res + playername(vch, to_ch);
end;
'm': res := res + sex_nm[ch.sex];
'M': begin
if (vch = nil) then
writeConsole('[BUG]: act() -> vch null')
else
res := res + sex_nm[vch.sex];
end;
's': res := res + sex_bm[ch.sex];
'S': begin
if (vch = nil) then
writeConsole('[BUG]: act() -> vch null')
else
res := res + sex_bm[vch.sex];
end;
'e': res := res + sex_pm[ch.sex];
'E': begin
if (vch = nil) then
writeConsole('[BUG]: act() -> vch null')
else
res := res + sex_pm[vch.sex];
end;
'o': begin
if (obj1 = nil) then
writeConsole('[BUG]: act() -> obj1 null')
else
res := res + GObject(obj1).name;
end;
'O': begin
if (obj2 = nil) then
writeConsole('[BUG]: act() -> obj2 null')
else
res := res + GObject(obj2).name;
end;
'p': begin
if (obj1 = nil) then
writeConsole('[BUG]: act() -> obj1 null')
else
res := res + GObject(obj1).short;
end;
'P': begin
if (obj2 = nil) then
writeConsole('[BUG]: act() -> obj2 null')
else
res := res + GObject(obj2).short;
end;
't': begin
if (arg1 = nil) then
writeConsole('[BUG]: act() -> pchar(arg1) null')
else
res := res + (PString(arg1))^;
end;
'T': begin
if (arg2 = nil) then
writeConsole('[BUG]: act() -> pchar(arg2) null')
else
res := res + (PString(arg2))^;
end;
'd': begin
if (arg2 = nil) then
writeConsole('[BUG]: act() -> arg2 is nil')
else
begin
ex := GExit(arg2);
if ((ex.keywords <> nil) and (length(ex.keywords^) = 0)) then
res := res + 'door'
else
begin
one_argument(ex.keywords^, temp);
res := res + temp;
end;
end;
end;
else
res := res + '$' + acts[current + 1];
end;
current := FastCharPos(acts, '$', last);
end;
res := cap(res + CopyStr(acts, last, length(acts) - last + 1));
Result := act_color(to_ch, res, '$');
end;
procedure act(atype : integer; const acts : string; hideinvis : boolean; ch : GCharacter;
arg1, arg2 : pointer; typ : integer);
{ Documentation of act routine:
atype - Ansi color to start with
acts - A string to send
hideinvis - Hide action when ch is invisible?
ch - Number of character
arg1, arg2 - Respectively object or character
type - Who gets the resulting string:
TO_ROOM = everybody in the room, except ch
TO_VICT = character in vict
TO_NOTVICT = everybody in the room, except ch and vict
TO_CHAR = to ch
TO_ALL = to everyone, except ch
prompt - Do we need to send a prompt after the string?
}
function HIDE_VIS(ch, vict : GCharacter) : boolean;
begin
if (ch = nil) then
HIDE_VIS := false
else
HIDE_VIS := (not ch.CAN_SEE(vict)) and (hideinvis);
end;
var
txt : string;
vch : GCharacter;
to_ch : GCharacter;
node : GListNode;
actList : TList;
x : integer;
label wind;
begin
if (length(acts) = 0) then
exit;
vch := GCharacter(arg2);
if (ch = nil) and (typ <> TO_ALL) then
begin
writeConsole('[BUG]: act() -> ch null');
exit;
end;
if (typ = TO_CHAR) then
node := ch.node_world
else
if (typ = TO_VICT) then
begin
if (vch = nil) then
begin
writeConsole('[BUG]: act() -> vch null');
exit;
end;
node := vch.node_world;
end
else
if (typ = TO_ALL) then
node := char_list.head
else
if (typ = TO_ROOM) or (typ = TO_NOTVICT) then
node := ch.room.chars.head
else
node := nil;
actList := TList.Create();
while (node <> nil) do
begin
to_ch := GCharacter(node.element);
if ((not to_ch.IS_AWAKE) and (to_ch <> ch)) then goto wind;
if (typ = TO_CHAR) and (to_ch <> ch) then
goto wind;
if (typ = TO_VICT) and ((to_ch <> vch) or (to_ch = ch) or (HIDE_VIS(to_ch, ch))) then
goto wind;
if (typ = TO_ROOM) and ((to_ch = ch) or (HIDE_VIS(to_ch, ch))) then
goto wind;
if (typ = TO_ALL) and (to_ch = ch) then
goto wind;
if (typ = TO_NOTVICT) and ((to_ch = ch) or (to_ch = vch)) then
goto wind;
if (typ = TO_IMM) and ((to_ch = ch) or (not to_ch.IS_IMMORT)) then
goto wind;
txt := act_string(acts, to_ch, ch, arg1, arg2);
to_ch.sendBuffer(to_ch.ansiColor(atype) + txt + #13#10);
if (to_ch.IS_NPC) and (to_ch <> ch) then
begin
if (GNPC(to_ch).context.existsSymbol('onAct')) then
actList.add(to_ch);
end;
wind:
if (typ = TO_CHAR) or (typ = TO_VICT) or (typ = TO_IMM) then
node := nil
else
if (typ = TO_ROOM) or (typ = TO_NOTVICT) or (typ = TO_ALL) then
node := node.next;
end;
if (actList.Count > 0) then
begin
for x := 0 to actList.Count - 1 do
begin
GNPC(actList[x]).context.runSymbol('onAct', [integer(actList[x]), integer(ch), txt]);
end;
end;
actList.Free();
end;
end.