[Cedar]<CedarChest®>Top>MazeWar.df=>MazeWarPlayerImpl1.Mesa
Last Edited by: Spreitzer, August 17, 1985 5:55:22 pm PDT
Hal Murray, March 16, 1986 7:23:12 pm PST
DIRECTORY Commander, CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, Process, Random, Rope, RPC, ThisMachine, TIPUser, UserCredentials, UserProfile, ViewerClasses, ViewerLocks, ViewerOps;
MazeWarPlayerImpl1: CEDAR MONITOR
IMPORTS CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, Process, Random, Rope, RPC, ThisMachine, TIPUser, UserCredentials, UserProfile, ViewerLocks, ViewerOps
EXPORTS MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides =
INVARIANT
p.srow, p.scol, p.sangle, and p.speek describe:
If p is me: the topview and the hallview,
Otherwise: no guarantees.
p.distance > 0 <=> p appears in hallview.
p.srow, p.scol, and p.sangle describe p's appearance.
The "occupants" fields of the Squares are redundant with the p.rows and p.cols.
p.distance is redundant with current positions.
Video inversness of opponent names is redundant with current positions.
All Players are "fully formed".
No more than one user being served.
myself # NIL <=> exactly one user being served.
BEGIN OPEN MazeWarPlayerInsides;
mazeViewerFlavor: PUBLIC ATOM ← GenSym.Gen[];
mazeViewerClass: PUBLIC ViewerClasses.ViewerClass ← NEW [ViewerClasses.ViewerClassRec ← [
paint: PaintMaze,
notify: NotifyMaze,
destroy: DestroyMaze,
tipTable: --TIPUser.InstantiateNewTIPTable[UserProfile.Token[key: "MazeWar.TIPTable", default: "/Ivy/Spreitzer/Games/MazeWar.TIP"]]-- NIL
]];
installationDirectory: ROPE ← CommandTool.CurrentWorkingDirectory[];
mazeWarriorIcon: PUBLIC Icons.IconFlavor ← Icons.NewIconFromFile[file: "MazeWar.icons", n: 0];
currentTIPTableName: ROPE ← " ";
rs: Random.RandomStream ← Random.Create[seed: -1];
CallUnderMonitor1: PUBLIC ENTRY PROC [p: PROC] =
BEGIN ENABLE UNWIND => {};
p[];
END;
StartPlayer: PUBLIC PROC [cmd: Commander.Handle] RETURNS [result: REFNIL, msg: ROPENIL] --Commander.CommandProc-- =
BEGIN
MonitoredStart: ENTRY PROC =
BEGIN ENABLE UNWIND => {}; --not strictly right, but it helps debugging
name, machineName, userPicsSource, normdPicsSource, TIPTableName, mazeSource: ROPENIL;
reread: BOOLEANFALSE;
cls: IO.STREAMIO.RIS[cmd.commandLine];
self: Player ← myself;
position: INTEGER;
IF self # NIL THEN {cmd.err.PutRope[IF self.destroyed THEN "Still dying... wait 1.5 - 4.5 minutes and try again" ELSE "Already playing"]; RETURN};
paints ← 0;
FOR position ← 1, position + 1 DO
token: ROPE;
GetArg: PROC RETURNS [arg: ROPE] = {
arg ← cls.GetTokenRope[TokenBreak].token;
IF arg.Equal["="] THEN arg ← cls.GetTokenRope[TokenBreak].token ELSE cmd.err.PutRope["Missing \"=\""]};
ToBool: PROC [r: ROPE] RETURNS [b: BOOL] = {
s: IO.STREAMIO.RIS[r]; b ← s.GetBool[]; s.Close[]};
[] ← cls.SkipWhitespace[flushComments: FALSE];
IF cls.EndOf[] THEN EXIT;
token ← cls.GetTokenRope[TokenBreak].token;
IF token.Equal["="] THEN {cmd.err.PutRope["Syntax error"]; RETURN};
IF token.Equal["name", FALSE] THEN name ← GetArg[]
ELSE IF token.Equal["pics", FALSE] THEN userPicsSource ← GetArg[]
ELSE IF token.Equal["TIPTable", FALSE] THEN TIPTableName ← GetArg[]
ELSE IF token.Equal["MazeSource", FALSE] THEN mazeSource ← GetArg[]
ELSE IF token.Equal["reread", FALSE] THEN reread ← ToBool[GetArg[]]
ELSE SELECT position FROM
1 => name ← token;
2 => userPicsSource ← token;
3 => TIPTableName ← token;
4 => mazeSource ← token;
5 => reread ← ToBool[token];
ENDCASE => {
cmd.err.PutF["I won't take a %th argument\n", IO.int[position]];
RETURN};
ENDLOOP;
machineName ← ThisMachine.Name[$Pup];
IF playerNumber # 1 THEN machineName ← IO.PutFR["%g#%g", IO.rope[machineName], IO.card[playerNumber]];
THROUGH [1 .. playerNumber] DO instance ← instance.Cat["1"] ENDLOOP;
IF userPicsSource.Length[] < 1 THEN userPicsSource ← UserProfile.Token[key: "MazeWar.Pics", default: installationDirectory.Cat["Eyeball"]];
IF (normdPicsSource ← NormalizePlayerPicsRoot[userPicsSource]) = NIL THEN {
result ← $Failure;
msg ← IO.PutFR["Pics %g don't exist or are local with no attachment", IO.rope[userPicsSource]];
RETURN;
};
IF name.Length[] < 1 THEN name ← UserProfile.Token[key: "MazeWar.Name", default: NIL];
IF name.Length[] < 1 THEN name ← UserCredentials.Get[].name.Cat["@", machineName];
IF TIPTableName.Length[] < 1 THEN TIPTableName ← UserProfile.Token[key: "MazeWar.TIPTable", default: installationDirectory.Cat["MazeWar.TIP"]];
IF mazeSource.Length[] < 1 THEN mazeSource ← UserProfile.Token[key: "MazeWar.MazeSource", default: installationDirectory.Cat["MazeWar.Maze"]];
IF reread OR NOT TIPTableName.Equal[currentTIPTableName]
THEN mazeViewerClass.tipTable ←
TIPUser.InstantiateNewTIPTable[currentTIPTableName ← TIPTableName];
IF reread THEN LosePlayerPics[normdPicsSource];
cmd.err.PutRope[MakeNewPlayer[name: name, instance: myInstance, picsSource: normdPicsSource, mazeSource: mazeSource]];
END;
MonitoredStart[];
IF myself # NIL THEN ViewerOps.OpenIcon[icon: myself.mv.container, closeOthers: TRUE];
END;
TokenBreak: IO.BreakProc = {RETURN [SELECT char FROM
IO.SP, IO.CR, IO.ESC, IO.LF, IO.TAB => sepr,
'= => break,
ENDCASE => other]};
StopPlayer: PUBLIC Commander.CommandProc--PROC [cmd: Handle]-- =
BEGIN
self: Player ← myself;
IF self = NIL THEN {cmd.err.PutRope["Not playing!"]; RETURN};
IF self.destroyed THEN {cmd.err.PutRope["I'm busy dying... should be done in half a minute"]; RETURN};
ViewerOps.DestroyViewer[self.mv.container];
END;
UnMakePlayer: ENTRY PROC [p: Player] =
BEGIN ENABLE UNWIND => {}; --not strictly right, but it helps debugging
IF p.destroyed THEN RETURN;
FOR pl: PlayerList ← p.mv.players, pl.rest WHILE pl # NIL DO
IF pl.first = myself THEN LOOP;
TRUSTED {pl.first.ir.RemovePlayer[p.id !RPC.CallFailed => CONTINUE]};
pl.first.ir ← NIL; --unimports dynamic interface
ENDLOOP;
IF exportedPlayer THEN MazeWarPlayerRpcControl.UnexportInterface[];
exportedPlayer ← FALSE;
p.destroyed ← TRUE;
END;
paints: PUBLIC INTEGER ← 0;
clippable: PUBLIC BOOLEANFALSE;
blt: PUBLIC BOOLEANTRUE;
Peek: PUBLIC PROC [peek, row, col, angle: INTEGER] RETURNS [prow, pcol, pdrow, pdcol, pangle: INTEGER] =
BEGIN
[pdrow, pdcol] ← aToD[angle];
IF peek = 0 THEN RETURN [row, col, pdrow, pdcol, angle];
prow ← row + pdrow;
pcol ← col + pdcol;
[pdrow, pdcol] ← aToD[pangle ← (angle + peek) MOD 4];
END;
Visible: PUBLIC PROC [mv: MazeView, frow, fcol, fdrow, fdcol, trow, tcol: INTEGER] RETURNS [distance: INTEGER] =
BEGIN
drow, dcol: INTEGER;
drow ← trow - frow;
dcol ← tcol - fcol;
IF SGN[drow] # fdrow THEN RETURN [-1];
IF SGN[dcol] # fdcol THEN RETURN [-1];
distance ← 0;
DO
IF frow = trow AND fcol = tcol THEN RETURN;
IF NOT mv.squares[Index[mv, frow, fcol]].open THEN RETURN [-1];
frow ← frow + fdrow;
fcol ← fcol + fdcol;
distance ← distance + 1;
ENDLOOP;
END;
SGN: PROC [x: INTEGER] RETURNS [sgn: INTEGER] =
{sgn ← IF x < 0 THEN -1 ELSE IF x > 0 THEN 1 ELSE 0};
forkNotify: PUBLIC BOOLEANFALSE;
NotifyMaze: ViewerClasses.NotifyProc--PROC [self: Viewer, input: LIST OF REF ANY]-- =
BEGIN
IF NOT forkNotify THEN ReallyNotifyMaze[self, input]
ELSE TRUSTED {Process.Detach[FORK ReallyNotifyMaze[self, input]]};
END;
ReallyNotifyMaze: ViewerClasses.NotifyProc--PROC [self: Viewer, input: LIST OF REF ANY]-- =
BEGIN
p: Player ← NARROW[self.data];
IF p = NIL THEN RETURN;
IF p.pausing AND input.first # $Grab THEN {
MessageWindow.Append[" patience!", FALSE];
RETURN};
SELECT input.first FROM
$Grab => {InputFocus.SetInputFocus[self]; RETURN};
$MoveFwd => {MoveSelf[p, p.angle]; RETURN};
$MoveBack => {MoveSelf[p, (p.angle+2) MOD 4]; RETURN};
$Shoot => TRUSTED {Process.Detach[FORK RawShoot[p]]; RETURN};
$TurnLeft => SetDirection[p, (p.angle+3) MOD 4];
$TurnRight => SetDirection[p, (p.angle+1) MOD 4];
$TurnBack => SetDirection[p, (p.angle+2) MOD 4];
$PeekLeft => SetPeek[p, peekLeft];
$PeekRight => SetPeek[p, peekRight];
$UnPeek => SetPeek[p, 0];
$Auto => IF ((auto ← auto.SUCC) MOD 2) # 0 THEN TRUSTED {Process.Detach[FORK Auto[p, auto]]};
ENDCASE => ERROR;
IncrPaint[p];
END;
DestroyMaze: ViewerClasses.DestroyProc--PROC [self: Viewer]-- =
BEGIN
p: Player ← NARROW[self.data];
IF p = myself THEN UnMakePlayer[p];
END;
auto: INT ← 0;
autoPeriod: INT;
atuoParmsRep: TYPE ~ RECORD [
forwardRange: INT ← 2,
forwardChooseAhead: INT ← 0,
forwardChooseLeft: INT ← 1,
forwardChooseRight: INT ← 2,
backRange: INT ← 1,
backChooseLeft: INT ← 0,
backChooseRight: INT ← 1,
lookBehindRange: INT ← 3,
lookBehindChoose: INT ← 0
];
autoParms: REF atuoParmsRep ~ NEW [atuoParmsRep];
Auto: PROC [p: Player, myAutoId: INT] =
BEGIN
defaultAutoPeriod: INT ~ 400;
minAutoPeriod: INT ~ 50;
myLastScore: INT ← p.score;
TRUSTED {Process.SetPriority[Process.priorityNormal]};
autoPeriod ← defaultAutoPeriod;
DO
Do: PROC [action: ATOM] ~ {
IF p.destroyed OR p.mv.container.destroyed OR auto # myAutoId THEN
ERROR ABORTED;
p.mv.maze.class.notify[p.mv.maze, LIST[action]];
Process.Pause[Process.MsecToTicks[autoPeriod]];
};
ahead: Angle ~ 0; left: Angle ~ 3; right: Angle ~ 1; behind: Angle ~ 2;
Compose: PROC [d1, d2: Angle] RETURNS [Angle] ~ {
RETURN [(d1+d2) MOD 4]
};
IsSpace: PROC [dirn: Angle ← ahead, whenPeeking: BOOLEANFALSE] RETURNS [BOOLEAN] ~ {
drow, dcol: INT;
[drow, dcol] ← aToD[(p.angle+dirn) MOD 4];
IF whenPeeking THEN {
fdrow, fdcol: INT;
[fdrow, fdcol] ← aToD[p.angle];
drow ← drow+fdrow;
dcol ← dcol+fdcol
};
RETURN [p.mv.squares[Index[p.mv, p.row, p.col]+Direction[p.mv, drow, dcol]].open];
};
TargetPresent: PROC RETURNS [BOOLEAN] ~ {
FOR pl: PlayerList ← p.mv.players, pl.rest WHILE pl # NIL DO
IF pl.first.distance >= 0 THEN RETURN [TRUE]
ENDLOOP;
RETURN [FALSE]
};
IF myLastScore # p.score THEN {
better: INT ← 0;
worse: INT ← 0;
FOR pl: PlayerList ← p.mv.players, pl.rest WHILE pl # NIL DO
IF pl.first.score > p.score THEN better ← better.SUCC;
IF pl.first.score < p.score THEN worse ← worse.SUCC;
ENDLOOP;
IF better # worse THEN {
I'm not the middle player.
SELECT TRUE FROM
(myLastScore > p.score) AND (better > worse) =>
SpeedUp.
autoPeriod ← (autoPeriod*4)/5;
(myLastScore < p.score) AND (better < worse) =>
SlowDown.
autoPeriod ← (autoPeriod*5)/4;
ENDCASE => NULL;
IF autoPeriod < minAutoPeriod THEN
autoPeriod ← minAutoPeriod;
};
myLastScore ← p.score
};
SELECT TRUE FROM
~IsSpace[] => {
turn: Angle ← SELECT rs.ChooseInt[0, autoParms.backRange] FROM <= autoParms.backChooseLeft => left, <= autoParms.backChooseRight => right, ENDCASE => behind;
inc: Angle ~ SELECT turn FROM left => right, right => left, ENDCASE => (SELECT rs.ChooseInt[0, 1] FROM 0=> right, 1=> left, ENDCASE => ERROR);
WHILE ~IsSpace[turn] DO turn ← Compose[turn, inc] ENDLOOP;
Do[SELECT turn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => $TurnBack];
IF turn = left OR turn = right THEN Do[$MoveFwd]
};
TargetPresent[] => {
Do[$Shoot];
DO
side: Angle ← SELECT rs.ChooseInt[0, 1] FROM 0=> left, 1=> right, ENDCASE => ERROR;
IF IsSpace[Compose[ side, behind]] THEN side ← Compose[ side, behind];
IF IsSpace[side] THEN {
Do[SELECT side FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR];
Do[$MoveFwd];
EXIT
};
IF ~IsSpace[] THEN EXIT;
Do[$MoveFwd]
ENDLOOP
};
ENDCASE => {
PeekAndShoot: PROC [dirn: Angle] RETURNS [didShoot: BOOLEAN] ~ {
IF ~IsSpace[ dirn, TRUE] THEN RETURN [didShoot ← FALSE];
Do[SELECT dirn FROM left => $PeekLeft, right => $PeekRight, ENDCASE => ERROR];
didShoot ← TargetPresent[];
Do[$UnPeek];
IF didShoot THEN {
Do[$MoveFwd];
Do[SELECT dirn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR];
Do[$Shoot];
Do[SELECT dirn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR];
Do[$MoveFwd];
}
};
peekDirn: Angle;
IF rs.ChooseInt[0, autoParms.lookBehindRange] <= autoParms.lookBehindChoose AND IsSpace[behind] THEN {
Do[$TurnBack];
IF TargetPresent[] THEN LOOP;
Do[$TurnBack]
};
peekDirn ← SELECT rs.ChooseInt[0, 1] FROM 0=> left, 1=> right, ENDCASE => ERROR;
IF ~PeekAndShoot[peekDirn] THEN
IF ~PeekAndShoot[Compose[peekDirn, behind]] THEN {
turn: Angle ← SELECT rs.ChooseInt[0, autoParms.forwardRange] FROM <= autoParms.forwardChooseAhead => ahead, <= autoParms.forwardChooseLeft => left, <= autoParms.forwardChooseRight => right, ENDCASE => behind;
inc: Angle ~ SELECT turn FROM left => right, right => left, ENDCASE => (SELECT rs.ChooseInt[0, 1] FROM 0=> right, 1=> left, ENDCASE => ERROR);
WHILE ~IsSpace[turn] DO turn ← Compose[turn, inc] ENDLOOP;
Do[SELECT turn FROM ahead => $MoveFwd, left => $TurnLeft, right => $TurnRight, ENDCASE => $TurnBack];
IF turn # ahead THEN Do[$MoveFwd]
}
};
ENDLOOP;
END;
ListPlayers: PUBLIC UNSAFE PROC RETURNS [players: BasicPlayerList] =
BEGIN
IF myself = NIL THEN RETURN [NIL];
FOR pl: PlayerList ← myself.mv.players, pl.rest WHILE pl # NIL DO
players ← CONS[[pl.first.instanceName, pl.first.id], players];
ENDLOOP;
FOR pl: PlayerList ← myself.mv.differentMazePlayers, pl.rest WHILE pl # NIL DO
players ← CONS[[pl.first.instanceName, pl.first.id], players];
ENDLOOP;
END;
TakePlayer: PUBLIC UNSAFE PROC [bp: BasicPlayer] = UNCHECKED {KnowPlayer[bp]};
GetFinderID: PUBLIC UNSAFE PROC RETURNS [bp: BasicPlayer] =
{self: Player ← myself;
bp ← IF self # NIL THEN [self.instanceName, self.id] ELSE [myInstance, 0]};
GetState: PUBLIC UNSAFE PROC RETURNS [id: PlayerId, name, pics, maze: ROPE, row, col, score: INTEGER, angle: Angle] =
BEGIN
RETURN [myself.id, myself.name, myself.pics.name, myself.mv.asRope, myself.row, myself.col, myself.score, myself.angle];
END;
KnowPlayer: PUBLIC UNSAFE PROC [bp: BasicPlayer] =
BEGIN
q: Player;
IF myself = NIL THEN RETURN;
q ← EnsurePlayer[bp];
IF q # NIL AND NOT q.instanceName.Equal[bp.instance] THEN
MessageWindow.Append["Random number collision detected", TRUE];
END;
EnsurePlayer: PUBLIC PROC [bp: BasicPlayer] RETURNS [q: Player] =
BEGIN
ir: PlayerInterface ← NIL;
alreadyKnown, differentMaze: BOOLEAN;
id: PlayerId;
row, col, score: INTEGER;
angle: Angle;
name, picsSource, mazeRope: ROPE;
pp: PlayerPics;
Internals1: PROC =
BEGIN ENABLE UNWIND => NULL;
IF alreadyKnown ← (q ← FindPlayer[myself.mv.players, bp.id]) # NIL THEN RETURN;
IF alreadyKnown ← (q ← FindPlayer[myself.mv.differentMazePlayers, bp.id]) # NIL THEN RETURN;
ir ← MazeWarPlayerRpcControl.ImportNewInterface[interfaceName: [instance: bp.instance] !RPC.ImportFailed => {ir ← NIL; CONTINUE}];
IF ir = NIL THEN {ok ← FALSE; RETURN};
TRUSTED {[id, name, picsSource, mazeRope, row, col, score, angle] ← ir.GetState[ !RPC.CallFailed => {ok ← FALSE; CONTINUE}]};
IF NOT ok THEN RETURN;
IF id # bp.id THEN {ok ← FALSE; RETURN};
differentMaze ← NOT mazeRope.Equal[myself.mv.asRope, FALSE];
END;
Internals2: ENTRY PROC =
BEGIN ENABLE UNWIND => NULL;
IF alreadyKnown ← (q ← FindPlayer[myself.mv.players, bp.id]) # NIL THEN RETURN;
IF alreadyKnown ← (q ← FindPlayer[myself.mv.differentMazePlayers, bp.id]) # NIL THEN RETURN;
IF differentMaze THEN
BEGIN
myself.mv.differentMazePlayers ← CONS[NEW [PlayerRep ← [id: bp.id, instanceName: bp.instance]], myself.mv.differentMazePlayers];
END
ELSE BEGIN
q ← AddPlayerAt[myself.mv, ir, bp.instance, id, name, row, col, angle, score, pp];
AddScoring[myself, q];
END;
END;
ok: BOOLEANTRUE;
me: Player ← myself; --'cause the debugger can't.
q ← NIL;
Internals1[];
IF NOT ok THEN RETURN [NIL];
IF q # NIL THEN RETURN;
IF NOT differentMaze THEN pp ← GetPlayerPics[picsSource];
ViewerLocks.CallUnderWriteLock[Internals2, myself.mv.container];
IF NOT ok THEN RETURN [NIL];
IF alreadyKnown THEN RETURN;
IF differentMaze THEN
BEGIN
--nothing to do here!?--
END
ELSE BEGIN
ViewerOps.PaintViewer[viewer: myself.mv.container, hint: client];
TRUSTED {ir.KnowPlayer[[myself.instanceName, myself.id] !RPC.CallFailed => {ok ← HandleCallFailure[q, why]; CONTINUE}]};
NoticeCall[q, ok];
END;
END;
TakeStatus: PUBLIC UNSAFE PROC [id: PlayerId, row, col, score: INTEGER, angle: Angle, shot: BOOLEAN] =
BEGIN
q: Player;
IF (q ← FindPlayer[myself.mv.players, id]) = NIL THEN RETURN;
IF row # q.row OR col # q.col OR angle # q.angle THEN {
q.shot ← shot;
MoveOther[myself, q, row, col, angle]};
IF score # q.score THEN Labels.Set[q.scoreLabel, IO.PutFR["%6g", IO.int[q.score ← score]]];
END;
AnnounceStatus: PROC [shot: BOOLEAN] =
BEGIN
FOR pl: PlayerList ← myself.mv.players, pl.rest WHILE pl # NIL DO
ok: BOOLEANTRUE;
IF pl.first = myself THEN LOOP;
TRUSTED {pl.first.ir.TakeStatus[id: myself.id, row: myself.row, col: myself.col, score: myself.score, angle: myself.angle, shot: shot !RPC.CallFailed => {ok ← HandleCallFailure[pl.first, why]; CONTINUE}]};
NoticeCall[pl.first, ok];
ENDLOOP;
END;
RemovePlayer: PUBLIC UNSAFE PROC [id: PlayerId] =
BEGIN
MonitoredRemove: ENTRY PROC =
BEGIN
goner: Player;
index: INTEGER;
goner ← FindPlayer[myself.mv.players, id];
IF goner = NIL THEN RETURN;
myself.mv.players ← Filter[myself.mv.players, goner];
ViewerOps.DestroyViewer[goner.nameLabel, FALSE];
ViewerOps.DestroyViewer[goner.scoreLabel, FALSE];
index ← Index[myself.mv, goner.row, goner.col];
myself.mv.squares[index].occupants ← Filter[myself.mv.squares[index].occupants, goner];
goner.ir ← NIL; --Unimports dynamic interface
goner.mv ← NIL;
END;
IF myself = NIL THEN RETURN;
MonitoredRemove[];
ViewerOps.PaintViewer[myself.mv.container, client];
END;
FindPlayer: PUBLIC PROC [pl: PlayerList, id: PlayerId] RETURNS [p: Player] =
BEGIN
FOR pl ← pl, pl.rest WHILE pl # NIL DO
IF pl.first.id = id THEN RETURN [pl.first];
ENDLOOP;
p ← NIL;
END;
MoveSelf: PROC [p: Player, angle: Angle] =
BEGIN
moving: BOOLEAN;
MonitoredMove: ENTRY PROC =
BEGIN
di, me, nu, drow, dcol: INTEGER;
[drow, dcol] ← aToD[angle];
di ← Direction[p.mv, drow, dcol];
me ← Index[p.mv, p.row, p.col];
nu ← me+di;
IF moving ← p.mv.squares[nu].open THEN moving ← MoveSelfBegin[p, p.row+drow, p.col+dcol, p.angle];
END;
MonitoredMove[];
IF moving THEN MoveSelfEnd[p, FALSE];
END;
MoveSelfWork: PROC [p: Player, row, col: INTEGER, angle: Angle, shot: BOOLEAN] =
BEGIN
EntryWork: ENTRY PROC RETURNS [BOOLEAN] = {
p.shot ← shot;
RETURN[MoveSelfBegin[p, row, col, angle]]};
IF EntryWork[] THEN MoveSelfEnd[p, shot];
END;
MoveSelfBegin: PROC [p: Player, row, col: INTEGER, angle: Angle] RETURNS [changed: BOOLEAN] =
BEGIN
old, new: INTEGER;
IF row # p.row OR col # p.col THEN {
old ← Index[p.mv, p.row, p.col];
new ← Index[p.mv, row, col];
p.mv.squares[old].occupants ← Filter[p.mv.squares[old].occupants, p];
p.mv.squares[new].occupants ← CONS[p, p.mv.squares[new].occupants]}
ELSE IF angle = p.angle THEN RETURN [FALSE];
changed ← TRUE;
p.row ← row;
p.col ← col;
p.angle ← angle;
RecomputeVisibilities[p];
END;
MoveSelfEnd: PROC [p: Player, shot: BOOLEAN] =
BEGIN
IncrPaint[p];
AnnounceStatus[shot];
END;
RecomputeVisibilities: PROC [from: Player] =
BEGIN
row, col, drow, dcol: INTEGER;
[row, col, drow, dcol, ] ← Peek[from.peek, from.row, from.col, from.angle];
FOR pl: PlayerList ← from.mv.players, pl.rest WHILE pl # NIL DO
wasVisible: BOOLEAN ← pl.first.distance > 0;
nowVisible: BOOLEAN;
IF pl.first = from THEN LOOP;
nowVisible ← (pl.first.distance ← Visible[from.mv, row, col, drow, dcol, pl.first.row, pl.first.col]) > 0;
IF wasVisible # nowVisible THEN Labels.SetDisplayStyle[pl.first.nameLabel, IF nowVisible THEN $WhiteOnBlack ELSE $BlackOnWhite];
ENDLOOP;
END;
MoveOther: PROC [p, q: Player, row, col: INTEGER, angle: Angle] =
BEGIN
EntryWork: ENTRY PROC =
BEGIN
index: INTEGER ← Index[q.mv, q.row, q.col];
wasShown ← q.sdistance > 0;
wasVisible ← q.distance > 0;
q.mv.squares[index].occupants ← Filter[q.mv.squares[index].occupants, q];
q.row ← row;
q.col ← col;
q.angle ← angle;
index ← Index[q.mv, q.row, q.col];
q.mv.squares[index].occupants ← CONS[q, q.mv.squares[index].occupants];
nowVisible ← (q.distance ← Visibility[p, q]) > 0;
IF wasVisible # nowVisible THEN
BEGIN
Labels.SetDisplayStyle[label: q.nameLabel, style: IF nowVisible THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
END;
wasShown, wasVisible, nowVisible: BOOLEAN;
EntryWork[];
IF wasShown OR nowVisible THEN IncrPaint[q];
END;
Visibility: PUBLIC PROC [from, to: Player] RETURNS [distance: INTEGER] =
BEGIN
frow, fcol, drow, dcol: INTEGER;
[frow, fcol, drow, dcol, ] ← Peek[from.peek, from.row, from.col, from.angle];
distance ← Visible[from.mv, frow, fcol, drow, dcol, to.row, to.col];
END;
ShownDistance: PROC [from, to: Player] RETURNS [distance: INTEGER] =
BEGIN
frow, fcol, drow, dcol: INTEGER;
[frow, fcol, drow, dcol, ] ← Peek[from.speek, from.srow, from.scol, from.sangle];
distance ← Visible[from.mv, frow, fcol, drow, dcol, to.srow, to.scol];
END;
SetDirection: PROC [p: Player, angle: Angle] =
{MonitoredSet: ENTRY PROC = {RecomputeVisibilities[p]};
p.angle ← angle;
MonitoredSet[];
AnnounceStatus[FALSE];
};
SetPeek: ENTRY PROC [p: Player, peek: INTEGER] =
{p.peek ← peek;
RecomputeVisibilities[p]};
shootDelay: Process.Ticks ← Process.MsecToTicks[700];
RawShoot: PROC [p: Player] =
BEGIN
row, col, drow, dcol: INTEGER;
victims: PlayerList ← NIL;
EntryShoot: ENTRY PROC =
BEGIN
row ← p.row; col ← p.col;
[drow, dcol] ← aToD[p.angle];
FOR pl: PlayerList ← p.mv.players, pl.rest WHILE pl # NIL DO
IF pl.first = p THEN LOOP;
IF pl.first.distance > 0 THEN victims ← CONS[pl.first, victims];
ENDLOOP;
END;
EntryShoot[];
Process.Pause[shootDelay];
FOR pl: PlayerList ← victims, pl.rest WHILE pl # NIL DO
ok: BOOLEANTRUE;
shot: BOOLEANFALSE;
TRUSTED {shot ← pl.first.ir.Shoot[p.id, row, col, drow, dcol !RPC.CallFailed => {ok ← HandleCallFailure[pl.first, why]; CONTINUE}]};
NoticeCall[pl.first, ok];
IF ok AND shot THEN
BEGIN
Labels.Set[p.scoreLabel, IO.PutFR["%6g", IO.int[p.score ← p.score + 10]]];
AnnounceStatus[FALSE];
END;
ENDLOOP;
END;
Shoot: PUBLIC UNSAFE PROC [shooterId: PlayerId, row, col, drow, dcol: INTEGER] RETURNS [shot: BOOLEAN] =
BEGIN
shooter: Player ← FindPlayer[myself.mv.players, shooterId];
IF shooter = NIL THEN RETURN;
DO
row ← row + drow;
col ← col + dcol;
IF NOT myself.mv.squares[Index[myself.mv, row, col]].open THEN EXIT;
IF row = myself.row AND col = myself.col THEN {HitPlayer[victor: shooter, victim: myself]; RETURN [TRUE]};
ENDLOOP;
shot ← FALSE;
END;
HitPlayer: PROC [victor, victim: Player] =
BEGIN
row, col: INTEGER;
angle: Angle;
ok: BOOLEANTRUE;
IF victim # myself THEN ERROR;
TRUSTED {Process.Detach[FORK BlinkIt[myself]]};
[row, col] ← PickAPlace[victim.mv];
angle ← rs.ChooseInt[0, 3];
Labels.Set[victim.scoreLabel, IO.PutFR["%6g", IO.int[victim.score ← victim.score - 5]]];
MoveSelfWork[victim, row, col, angle, TRUE];
END;
BlinkIt: PROC [p: Player] =
BEGIN
--commented out:--
p.blinking ← TRUE;
IncrPaint[p];
Process.Pause[blinkTicks];
p.blinking ← FALSE;
IncrPaint[p];
END;
blinkTicks: Process.Ticks ← Process.MsecToTicks[400];
Filter: PUBLIC PROC [org: PlayerList, rem: Player] RETURNS [ohne: PlayerList] =
BEGIN
last: PlayerList ← NIL;
ohne ← org;
FOR org ← org, org.rest WHILE org # NIL DO
IF org.first = rem THEN {IF last = NIL THEN ohne ← org.rest ELSE last.rest ← org.rest; RETURN};
last ← org;
ENDLOOP;
ERROR;
END;
HandleCallFailure: PROC [callee: Player, why: RPC.CallFailure] RETURNS [handled: BOOLEANFALSE] =
BEGIN
IF why = unbound THEN
BEGIN
TRUSTED {RemovePlayer[callee.id]};
handled ← TRUE;
END;
IF debugFailures THEN whys ← CONS[why, whys];
END;
debugFailures: BOOLEANFALSE;
whys: LIST OF RPC.CallFailure ← NIL;
maxAcceptableCallFailures: NAT ← 0;
NoticeCall: PROC [p: Player, ok: BOOLEAN] =
BEGIN
IF p.destroyed THEN RETURN;
p.callFailures ← MAX[0, (p.callFailures + (IF ok THEN -1 ELSE 1))];
IF p.callFailures > maxAcceptableCallFailures THEN TRUSTED {RemovePlayer[p.id]};
END;
StupidFuckingWarnings: PUBLIC SIGNAL [atom: ATOM] = CODE;
END.