DIRECTORY Commander, CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, OrderedSymbolTableRef, Process, PupDefs, Random, Rope, RPC, TIPUser, UserCredentials, UserProfile, ViewerClasses, ViewerLocks, ViewerOps; MazeWarPlayerImpl1: CEDAR MONITOR IMPORTS CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, Process, PupDefs, Random, Rope, RPC, TIPUser, UserCredentials, UserProfile, ViewerLocks, ViewerOps EXPORTS MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides = 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 _ " "; CallUnderMonitor1: PUBLIC ENTRY PROC [p: PROC] = BEGIN ENABLE UNWIND => {}; p[]; END; StartPlayer: PUBLIC Commander.CommandProc--PROC [cmd: Handle]-- = BEGIN MonitoredStart: ENTRY PROC = BEGIN ENABLE UNWIND => {}; --not strictly right, but it helps debugging name, machineName, picsSource, TIPTableName, mazeSource: ROPE _ NIL; reread: BOOLEAN _ FALSE; cls: IO.STREAM _ IO.RIS[cmd.commandLine]; self: Player _ myself; position: INTEGER; IF self # NIL THEN {cmd.err.PutRope[IF self.destroyed THEN "Still dying... wait half a minute 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.STREAM _ IO.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 picsSource _ 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 => picsSource _ 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 _ PupDefs.GetMyName[]; IF playerNumber # 1 THEN machineName _ IO.PutFR["%g#%g", IO.rope[machineName], IO.card[playerNumber]]; IF picsSource.Length[] < 1 THEN picsSource _ UserProfile.Token[key: "MazeWar.Pics", default: installationDirectory.Cat["Eyeball"]]; 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[picsSource]; cmd.err.PutRope[MakeNewPlayer[name: name, instance: myInstance, picsSource: picsSource, 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 BOOLEAN _ FALSE; blt: PUBLIC BOOLEAN _ TRUE; 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 BOOLEAN _ FALSE; 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; 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: BOOLEAN _ FALSE] 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 { SELECT TRUE FROM (myLastScore > p.score) AND (better > worse) => autoPeriod _ (autoPeriod*4)/5; (myLastScore < p.score) AND (better < worse) => autoPeriod _ (autoPeriod*5)/4; ENDCASE => NULL; IF autoPeriod < minAutoPeriod THEN autoPeriod _ minAutoPeriod; }; myLastScore _ p.score }; SELECT TRUE FROM ~IsSpace[] => { turn: Angle _ SELECT Random.Choose[0, autoParms.backRange] FROM <= autoParms.backChooseLeft => left, <= autoParms.backChooseRight => right, ENDCASE => behind; inc: Angle ~ SELECT turn FROM left => right, right => left, ENDCASE => (SELECT Random.Choose[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 Random.Choose[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 Random.Choose[0, autoParms.lookBehindRange] <= autoParms.lookBehindChoose AND IsSpace[behind] THEN { Do[$TurnBack]; IF TargetPresent[] THEN LOOP; Do[$TurnBack] }; peekDirn _ SELECT Random.Choose[0, 1] FROM 0=> left, 1=> right, ENDCASE => ERROR; IF ~PeekAndShoot[peekDirn] THEN IF ~PeekAndShoot[Compose[peekDirn, behind]] THEN { turn: Angle _ SELECT Random.Choose[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 Random.Choose[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: BOOLEAN _ TRUE; 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: BOOLEAN _ TRUE; 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: BOOLEAN _ TRUE; shot: BOOLEAN _ FALSE; 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: BOOLEAN _ TRUE; IF victim # myself THEN ERROR; TRUSTED {Process.Detach[FORK BlinkIt[myself]]}; [row, col] _ PickAPlace[victim.mv]; angle _ Random.Choose[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:-- 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: BOOLEAN _ FALSE] = BEGIN IF why = unbound THEN BEGIN TRUSTED {RemovePlayer[callee.id]}; handled _ TRUE; END; IF debugFailures THEN whys _ CONS[why, whys]; END; debugFailures: BOOLEAN _ FALSE; 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. Β[Indigo]{Cedar}Top>MazeWar.df=>MazeWarPlayerImpl1.Mesa Last Edited by: Spreitzer, August 30, 1984 9:36:49 pm PDT 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. THROUGH [1 .. playerNumber] DO instance _ instance.Cat["1"] ENDLOOP; IF p.pausing AND input.first # $Grab THEN { MessageWindow.Append[" patience!", FALSE]; RETURN}; I'm not the middle player. SpeedUp. SlowDown. p.blinking _ TRUE; IncrPaint[p]; Process.Pause[blinkTicks]; p.blinking _ FALSE; IncrPaint[p]; Κ9– "cedar" style˜J™6J™9J˜codešΟk ˜ Kšœ3œœO˜€—K˜šΠbxœœ˜!Kšœ)œhœ?˜άKšœ5˜<š ™ ™/K™)K™—K™)K™5K™OK™/K™GK™K™#K™/——K˜Kšœœ˜ K˜Kšœœœ˜-šœœœ"˜YK˜K˜K˜Kšœ Οc{œ˜‰Kšœ˜—K˜Kšœœ)˜DK˜KšœœG˜^K˜Kšœœ˜ K˜š Οnœœœœœ˜0Kšœœœ˜K˜Kšœ˜—K˜š  œœŸœ˜AKš˜š œœœ˜KšœœœŸ,˜GKšœ9œœ˜DKšœœœ˜Kš œœœœœ˜)K˜Kšœ œ˜Kšœœœœœ3œœ˜ŽK˜ šœ˜!Kšœœ˜ š œœœœ˜$K˜)Kšœœ*œ#˜g—š  œœœœœ˜,Kš œœœœœ!˜6—Kšœ'œ˜.Kšœ œœ˜Kšœ+˜+Kšœœ#œ˜CKšœœœ˜2Kšœœœœ˜=Kšœœœœ˜CKšœœœœ˜CKšœœœœ˜Cšœœ ˜K˜K˜Kšœ˜Kšœ˜Kšœ˜šœ˜ Kšœ.œ˜@Kšœ˜——Kšœ˜—K˜"Kš œœœœœ˜fKšœœœ™DKšœœd˜ƒKšœœ8œ˜VKšœœ9˜RKšœœn˜Kšœœo˜Žšœœœ(˜8šœ˜KšœC˜C——Kšœœ˜*Kšœq˜qKšœ˜—K˜Kšœ œœ<œ˜VKšœ˜—K˜š   œœœœ˜4Kšœœœœœœœœœœ ˜,K˜ Kšœ ˜—K˜š  œœŸœ˜@Kš˜K˜Kšœœœ#œ˜=KšœœHœ˜fKšœ+˜+Kšœ˜—K˜š  œœœ˜&KšœœœŸ,˜GKšœ œœ˜šœ(œœ˜Kšœ˜—šœ:œœ˜NKšœ œ0˜>Kšœ˜—Kšœ˜—K˜Kš   œœœœ œ˜NK˜š   œœœœœ˜;Kšœ˜Kš œœœœœ˜K—K˜š œœœœœ"œœ˜uKš˜Kšœr˜xKšœ˜—K˜š  œœœœ˜2Kš˜K˜ Kšœ œœœ˜Kšœ˜š œœœœ#˜9Kšœ9œ˜?—Kšœ˜—K˜š  œœœœ˜AKš˜Kšœœ˜Kšœœ˜%K˜ Kšœœ˜K˜ Kšœœ˜!K˜š  œœ˜Kšœœœœ˜Kšœ=œœœ˜OKšœJœœœ˜\KšœXœœœ˜‚Kš œœœœœ˜&KšœKœœœ˜}Kšœœœœ˜Kšœ œœœ˜(Kšœœ"œ˜