[Ivy]<Spreitzer>Top>MazeWar.df=>MazeWarPlayerImpl2.Mesa
Last Edited by: Spreitzer, August 21, 1984 7:06:10 pm PDT
DIRECTORY
Atom, Commander, Containers, FS, Graphics, GraphicsOps, IO, IOClasses, Labels, MazeWarFinder, MazeWarFinderRpcControl, MazeWarPlayer, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, OrderedSymbolTableRef, Process, PupDefs, Random, Rope, RPC, UserCredentials, ViewerClasses, ViewerOps;
MazeWarPlayerImpl2: CEDAR MONITOR
IMPORTS Atom, Commander, Containers, FS, Graphics, GraphicsOps, IO, IOClasses, Labels, MazeWarFinder, MazeWarFinderRpcControl, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, OSTR:OrderedSymbolTableRef, Process, PupDefs, Random, Rope, RPC, UserCredentials, ViewerOps
EXPORTS MazeWarPlayer, MazeWarPlayerInsides =
INVARIANT
No clippable paints are going on.
BEGIN OPEN MazeWarPlayerInsides;
myself: PUBLIC Player ← NIL;
myAddr: PUBLIC PupDefs.PupAddress;
playerNumber: PUBLIC CARDINAL ← 47;
myInstance: PUBLIC ROPE ← "??";
topviews: PUBLIC ARRAY Angle OF Bitmap ← ALL[NIL];
exportedPlayer: PUBLIC BOOLEANFALSE;
defaultPlayerPics: PUBLIC PlayerPics ← NEW [PlayerPicsRep ← [NIL, ALL[ALL[NIL]]]];
pieces: PUBLIC ARRAY [0 .. MaxDistance] OF ARRAY Piece OF LocatedBitmap ← ALL[ALL[[]]];
shownPlayers: PUBLIC PlayerList ← NIL;
MakeNewPlayer: PUBLIC PROC [name, instance, picsSource, mazeSource: ROPE] RETURNS [errmsg: ROPENIL] =
BEGIN
mv: MazeView;
id: PlayerId ← Random.Choose[FIRST[INTEGER], LAST[INTEGER]];
userName, password, mazeRope: ROPE;
failure: BOOLEANFALSE;
pp: PlayerPics;
mazeStreamIn, mazeStreamOut: IO.STREAM;
mazeStreamIn ← FS.StreamOpen[fileName: mazeSource ! FS.Error => {errmsg ← IO.PutFR["Unable to open maze source because %g", IO.rope[error.explanation]]; failure ← TRUE; CONTINUE}];
IF failure THEN RETURN;
[] ← mazeStreamIn.SkipWhitespace[flushComments: FALSE];
mazeStreamOut ← IO.ROS[];
IOClasses.Copy[from: mazeStreamIn, to: mazeStreamOut, closeFrom: TRUE, closeTo: FALSE];
mazeRope ← mazeStreamOut.RopeFromROS[];
pp ← GetPlayerPics[picsSource !
FS.Error => {errmsg ← error.explanation; failure ← TRUE; CONTINUE}];
IF failure THEN RETURN;
[mv, errmsg] ← MazeFromRope[mazeRope];
IF errmsg.Length[] > 0 THEN RETURN;
myself ← Plop[mv, id, name, instance, pp];
ViewMaze[myself, [iconic: TRUE, name: name]];
AddScoring[myself, myself];
[userName, password] ← UserCredentials.Get[];
TRUSTED {MazeWarPlayerRpcControl.ExportInterface[
interfaceName: [instance: instance],
user: userName,
password: RPC.MakeKey[password]]};
exportedPlayer ← TRUE;
TRUSTED {Process.Detach[FORK StayAlive[myself]]};
END;
debugImport: BOOLEANFALSE;
importFailures: LIST OF CARDINALNIL;
exportedFinder: BOOLEANFALSE;
disabled: BOOLFALSE;
finderID: BasicPlayer;
StayAlive: PROC [p: Player] =
BEGIN ENABLE UNWIND => {IF p.destroyed THEN myself ← NIL};
TRUSTED {Process.SetPriority[Process.priorityBackground]};
FOR age: CARDINAL ← 0, age + 1 WHILE NOT p.destroyed DO
ok: BOOLEANTRUE;
IF NOT disabled THEN {
MazeWarFinderRpcControl.ImportInterface[interfaceName: [instance: "MazeWar.Auto"] !RPC.ImportFailed => {ok ← FALSE; CONTINUE}];
IF debugImport THEN {
MessageWindow.Append[IO.PutFR["Import %g at age %g", IO.bool[ok], IO.card[age]], TRUE];
IF NOT ok THEN importFailures ← CONS[age, importFailures]};
IF ok THEN {
players: BasicPlayerList;
TRUSTED {
finderID ← MazeWarFinder.GetFinderID[!RPC.CallFailed => {ok ← FALSE; CONTINUE}];
IF ok THEN MazeWarFinder.TakePlayer[[myself.instanceName, myself.id] !RPC.CallFailed => {ok ← FALSE; CONTINUE}];
IF ok THEN players ← MazeWarFinder.ListPlayers[!RPC.CallFailed => {ok ← FALSE; CONTINUE}]};
IF ok THEN {
FOR players ← players, players.rest WHILE players # NIL DO
q: Player ← EnsurePlayer[players.first];
IF q # NIL AND NOT q.instanceName.Equal[players.first.instance] THEN
MessageWindow.Append["Random number collision", TRUE];
ENDLOOP;
};
MazeWarFinderRpcControl.UnimportInterface[];
}
ELSE {
IF exportedFinder THEN
{MazeWarFinderRpcControl.UnexportInterface[]; exportedFinder ← FALSE};
TRUSTED {MazeWarFinderRpcControl.ExportInterface[
interfaceName: [instance: "MazeWar.Auto"],
user: "MazeWar.Auto",
password: RPC.MakeKey["MazeWar"]]};
exportedFinder ← TRUE;
};
};
Delay[age];
ENDLOOP;
IF exportedFinder THEN
{MazeWarFinderRpcControl.UnexportInterface[]; exportedFinder ← FALSE};
myself ← NIL;
END;
WhoAmI: PROC [cmd: Commander.Handle] RETURNS [result: REFNIL, msg: ROPENIL] --Commander.CommandProc-- =
BEGIN
p: Player ← myself;
IF p = NIL THEN RETURN [$Failure, "Not playing"] ELSE cmd.out.PutF["\"%q\" %g\n", IO.rope[p.instanceName], IO.int[p.id]];
END;
Contact: PROC [cmd: Commander.Handle] RETURNS [result: REFNIL, msg: ROPENIL] --Commander.CommandProc-- =
BEGIN
Parse: PROC = {
bp.instance ← in.GetRopeLiteral[];
bp.id ← in.GetInt[];
};
bp: BasicPlayer;
in: IO.STREAMIO.RIS[cmd.commandLine];
ok: BOOLTRUE;
IF myself = NIL THEN RETURN [$Failure, "Not playing"];
Parse[!IO.Error, IO.EndOfStream => {
ok ← FALSE;
CONTINUE}];
IF ok THEN [] ← EnsurePlayer[bp] ELSE RETURN [$Failure, "Syntax error; say: MazeWarContact \"nn#mmm#ss\" iiiii\n"];
END;
Delay: PROC [age: CARDINAL] =
BEGIN
center: CARDINAL ← (IF age < 3 THEN 4 ELSE IF age < 16 THEN 16 ELSE 256) * Process.SecondsToTicks[1];
wait: CARDINAL ← Random.Choose[center/2, 3*center/2];
Process.Pause[wait];
END;
MazeFromRope: PROC [rope: ROPE] RETURNS [mv: MazeView, errmsg: ROPENIL] =
BEGIN
Check: PROC [row, col, drow, dcol: INTEGER] RETURNS [bad: BOOLEANFALSE] =
BEGIN
index: INTEGER ← Index[mv, row-drow, col-dcol];
di: INTEGER ← Direction[mv, drow, dcol];
IF NOT mv.squares[index].open THEN
BEGIN
d: INTEGER ← 0;
FOR index ← index+di, index+di WHILE mv.squares[index].open DO d ← d + 1 ENDLOOP;
IF d > MaxDistance+1 THEN {errmsg ← IO.PutFR["Maze has open distance too long (> %g)", IO.int[MaxDistance+1]]; RETURN[TRUE]};
END;
END;
rl: INT ← rope.Length[];
cols: INTEGER ← rope.Find["\n"];
rows: INTEGER ← rl/(cols+1);
IF rows*(cols+1) # rl THEN RETURN [NIL, "Maze description not properly punctuated with newlines"];
mv ← NEW [MazeViewRep[rows*cols]];
mv.asRope ← rope;
mv.rows ← rows;
mv.cols ← cols;
FOR row: INTEGER IN [0 .. rows) DO
FOR col: INTEGER IN [0 .. cols) DO
char: CHAR ← rope.Fetch[row*(cols+1)+col];
index: INTEGER ← Index[mv, row, col];
mv.squares[index] ← [];
SELECT char FROM
'X => mv.squares[index].open ← FALSE;
'. => mv.squares[index].open ← TRUE;
ENDCASE => RETURN [NIL, IO.PutFR["Bad character in maze: 0%bC", IO.char[char]]];
IF mv.squares[index].open AND (row = 0 OR row = rows-1 OR col = 0 OR col = cols-1) THEN RETURN [NIL, IO.PutFR["Gap in border"]];
ENDLOOP;
ENDLOOP;
FOR row: INTEGER IN (0 .. rows-1) DO
FOR col: INTEGER IN (0 .. cols-1) DO
IF Check[row, col, 0, 1] THEN RETURN;
IF Check[row, col, 1, 0] THEN RETURN;
ENDLOOP;
ENDLOOP;
mv.topX ← innerBorder + w - tvd*cols/2;
mv.topY ← innerBorder + tvd*rows;
mv.inY ← w + hallTopSep;
mv.inX ← tvd*cols/2;
END;
hallTopSep: INTEGER ← 3;
scoreTopSep: INTEGER ← 5;
scoreSep: INTEGER ← 1;
innerBorder: INTEGER ← 2;
Plop: PROC [mv: MazeView, id: PlayerId, name, instance: ROPE, pp: PlayerPics] RETURNS [p: Player] =
BEGIN
row, col: INTEGER;
[row, col] ← PickAPlace[mv];
p ← AddPlayerAt[mv, NIL, instance, id, name, row, col, 0, 0, pp];
END;
PickAPlace: PUBLIC PROC [mv: MazeView] RETURNS [row, col: INTEGER] =
BEGIN
me: INTEGER;
DO
row ← Random.Choose[0, mv.rows-1];
col ← Random.Choose[0, mv.cols-1];
me ← Index[mv, row, col];
IF mv.squares[me].open THEN RETURN;
ENDLOOP;
END;
ViewMaze: PROC [p: Player, viewerInit: ViewerClasses.ViewerRec] =
BEGIN
IF viewerInit.icon = unInit THEN viewerInit.icon ← mazeWarriorIcon;
p.mv.container ← Containers.Create[info: viewerInit, paint: FALSE];
p.mv.maze ← ViewerOps.CreateViewer[flavor: mazeViewerFlavor, info: [parent: p.mv.container, wx: 10, wy: 10, ww: 100, wh: 100, data: p, border: FALSE], paint: FALSE];
ViewerOps.MoveViewer[
viewer: p.mv.maze,
x: p.mv.maze.wx,
y: p.mv.maze.wy,
w: 2*(w+innerBorder) + p.mv.maze.ww - p.mv.maze.cw,
h: 2*(w+innerBorder) + hallTopSep + tvd*p.mv.rows + p.mv.maze.wh - p.mv.maze.ch,
paint: FALSE];
END;
IncrPaint: PUBLIC PROC [p: Player] = {
IF p.shot THEN TRUSTED {Process.Detach[FORK ReallyIncrPaint[p]]}
ELSE ReallyIncrPaint[p]};
ReallyIncrPaint: PROC [p: Player] = {
InnerPaint: ENTRY PROC = {
ViewerOps.PaintViewer[viewer: p.mv.maze, hint: client, clearClient: FALSE, whatChanged: p];
};
IF p # myself OR NOT clippable THEN
ViewerOps.PaintViewer[viewer: p.mv.maze, hint: client, clearClient: FALSE, whatChanged: p]
ELSE {
paints ← paints + 1;
InnerPaint[!UNWIND => paints ← paints - 1];
paints ← paints - 1};
};
AddScoring: PUBLIC PROC [vp, p: Player] =
BEGIN
mv: MazeView ← p.mv;
y: INTEGER ← vp.mv.maze.wy + vp.mv.maze.wh + scoreTopSep;
p.nameLabel ← Labels.Create[info: [parent: mv.container, name: p.name, border: FALSE], paint: FALSE];
p.scoreLabel ← Labels.Create[info: [parent: mv.container, name: "-65,535", border: FALSE], paint: FALSE];
Labels.Set[p.scoreLabel, IO.PutFR["%6g", IO.int[p.score]], FALSE];
Labels.SetDisplayStyle[p.nameLabel, IF Visibility[vp, p] > 0 THEN $WhiteOnBlack ELSE $BlackOnWhite, FALSE];
FOR pl: PlayerList ← mv.players, pl.rest WHILE pl # NIL DO
n: Viewer ← pl.first.nameLabel;
v: Viewer ← pl.first.scoreLabel;
ViewerOps.MoveViewer[viewer: n, x: mv.maze.wx, y: y, w: n.ww, h: n.wh, paint: FALSE];
ViewerOps.MoveViewer[viewer: v, x: mv.maze.wx + mv.maze.ww - v.ww, y: y, w: v.ww, h: v.wh, paint: FALSE];
y ← MAX[n.wy+n.wh, v.wy+v.wh] + scoreSep;
ENDLOOP;
END;
DrawHall: PUBLIC PROC [context: Context, mv: MazeView, row, col, drow, dcol: INTEGER, angle: Angle] =
BEGIN
Line: PROC [a, b: Pt3] = INLINE {
Graphics.SetCP[context, a.x/a.z, a.y/a.z];
Graphics.DrawTo[context, b.x/b.z, b.y/b.z]};
DoIt: PROC = {
Blt: PROC [piece: Piece] = {
lb: LocatedBitmap ← pieces[d][piece];
GraphicsOps.DrawBitmap[self: context, bitmap: lb.bitmap, w: lb.bitmap.width, h: lb.bitmap.height, x: 0, y: 0, xorigin: lb.xorigin, yorigin: lb.yorigin];
};
DrawPlayers: PROC = {
Graphics.SetCP[context, 0, 0];
[] ← Graphics.SetPaintMode[context, invert];
FOR pl: PlayerList ← shownPlayers, pl.rest WHILE pl # NIL DO
b: Bitmap ← pl.first.pics.pics[pl.first.sdistance][(pl.first.sangle+4-angle) MOD 4];
GraphicsOps.DrawBitmap[self: context, bitmap: b, w: b.width, h: b.height, x: 0, y: 0, xorigin: b.width/2, yorigin: b.height/2];
ENDLOOP;
};
me, di, left, right, d, crow, ccol: INTEGER;
f, b: Number;
mes, lefts, rights: Square;
di ← Direction[mv, drow, dcol];
left ← Direction[mv, -dcol, drow];
right ← Direction[mv, dcol, -drow];
crow ← row;
ccol ← col;
mes ← mv.squares[me ← Index[mv, row, col]];
f ← z1;
b ← hither;
[] ← Graphics.SetPaintMode[context, opaque];
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, [-w, -w, w, w]];
Graphics.SetColor[context, Graphics.black];
Graphics.SetCP[context, w, w];
Graphics.DrawTo[context, w, -w];
Graphics.DrawTo[context, -w, -w];
Graphics.DrawTo[context, -w, w];
Graphics.DrawTo[context, w, w];
IF NOT mes.open THEN RETURN;
lefts ← mv.squares[me + left];
rights ← mv.squares[me + right];
IF blt THEN {
Graphics.SetCP[context, 0, 0];
[] ← Graphics.SetPaintMode[context, transparent]};
d ← 0;
WHILE mes.open DO
nu: INTEGER ← me + di;
fls: Square ← mv.squares[nu + left];
frs: Square ← mv.squares[nu + right];
fs: Square ← mv.squares[nu];
r2: Number ← f*r/b;
IF paints > 1 AND clippable THEN RETURN;
IF NOT lefts.open THEN {
IF blt THEN Blt[LeftFilled]
ELSE {
Line[[-r, r, f], [-r, r, b]];
Line[[-r, -r, f], [-r, -r, b]]}}
ELSE IF NOT fls.open THEN {
IF blt THEN Blt[LeftGap]
ELSE {
Line[[-r2, r, f], [-r, r, f]];
Line[[-r2, -r, f], [-r, -r, f]]}};
IF (IF fs.open THEN lefts.open # fls.open ELSE IF lefts.open THEN fls.open ELSE TRUE) THEN {
IF blt THEN Blt[LeftEdge] ELSE Line[[-r, r, f], [-r, -r, f]]};
IF NOT rights.open THEN {
IF blt THEN Blt[RightFilled]
ELSE {
Line[[ r, r, f], [ r, r, b]];
Line[[ r, -r, f], [ r, -r, b]]}}
ELSE IF NOT frs.open THEN {
IF blt THEN Blt[RightGap]
ELSE {
Line[[ r2, r, f], [ r, r, f]];
Line[[ r2, -r, f], [ r, -r, f]]}};
IF (IF fs.open THEN rights.open # frs.open ELSE IF rights.open THEN frs.open ELSE TRUE) THEN {
IF blt THEN Blt[RightEdge] ELSE Line[[r, r, f], [r, -r, f]]};
b ← f;
f ← f + dz;
d ← d + 1;
me ← nu;
mes ← fs;
lefts ← fls;
rights ← frs;
crow ← crow + drow;
ccol ← ccol + dcol;
ENDLOOP;
d ← d - 1;
IF blt THEN Blt[HorEdges]
ELSE {
Line[[-r, r, b], [r, r, b]];
Line[[-r, -r, b], [r, -r, b]]};
CallUnderMonitor1[DrawPlayers];
};
mark: BasicTime.Pulses;
IF hallHist # NIL THEN mark ← BasicTime.GetClockPulses[];
DoIt[];
IF hallHist # NIL THEN hallHist.Increment[ BasicTime.PulsesToMicroseconds[BasicTime.GetClockPulses[] - mark]/5000];
END;
hallHist: Histograms.Histogram;
hallHistViewer: Viewer;
HistHall: PROC =
BEGIN
hallHist ← Histograms.NewHistogram[];
hallHistViewer ← hallHist.ShowIn[name: "Hall Paint Times/(.005 sec)"];
END;
AddPlayerAt: PUBLIC PROC [mv: MazeView, ir: PlayerInterface, instanceName: ROPE, id: PlayerId, name: ROPE, row, col: INTEGER, angle: Angle, score: INTEGER, pp: PlayerPics] RETURNS [p: Player] =
BEGIN
me: INTEGER ← Index[mv, row, col];
p ← NEW [PlayerRep ← [mv: mv, ir: ir, instanceName: instanceName, id: id, row: row, col: col, angle: angle, peek: 0, score: score, name: name, pics: pp]];
mv.squares[me].occupants ← CONS[p, mv.squares[me].occupants];
mv.players ← CONS[p, mv.players];
END;
Rotate: PROC [x, y: REAL, angle: Angle] RETURNS [rx, ry: REAL] =
BEGIN
IF angle >= 2 THEN {x ← -x; y ← -y; angle ← angle-2};
IF angle = 0 THEN RETURN [x, y];
RETURN [-y, x];
END;
picsTable: OSTR.Table ← OSTR.CreateTable[ComparePix];
ComparePix: OSTR.CompareProc--PROC [r1, r2: Item] RETURNS [Comparison]-- =
BEGIN
k1, k2: ROPE;
WITH r1 SELECT FROM
r: ROPE => k1 ← r;
pp: PlayerPics => k1 ← pp.name;
ENDCASE => ERROR;
WITH r2 SELECT FROM
r: ROPE => k2 ← r;
pp: PlayerPics => k2 ← pp.name;
ENDCASE => ERROR;
RETURN [k1.Compare[s2: k2, case: FALSE]];
END;
LosePlayerPics: PUBLIC ENTRY PROC [root: ROPE] = {[] ← picsTable.Delete[root]};
GetPlayerPics: PUBLIC PROC [root: ROPE] RETURNS [pp: PlayerPics] =
BEGIN
IF root.Length[] < 1 OR root.Equal["default", FALSE] THEN RETURN [defaultPlayerPics]; no more--
pp ← NARROW[picsTable.Lookup[root]];
IF pp = NIL THEN {
newPics: PlayerPics ← ReadPlayerPics[root];
Insert: ENTRY PROC = {
pp ← NARROW[picsTable.Lookup[root]];
IF pp = NIL THEN picsTable.Insert[pp ← newPics]};
Insert[];
};
END;
ReadPlayerPics: PROC [root: ROPE] RETURNS [pp: PlayerPics] =
BEGIN
negate: BOOLEAN ← '- = root.Fetch[0];
IF myself # NIL THEN myself.pausing ← TRUE;
MessageWindow.Append[Rope.Cat["Patience... loading images from ", root, " ..."], TRUE];
pp ← NEW [PlayerPicsRep ← [root, ALL[ALL[NIL]]]];
IF negate THEN root ← root.Substr[1, root.Length[]-1];
FOR a: Angle IN Angle DO
image: GraphicsOps.ImageRef;
xmin,ymin,xmax,ymax, imageSize, cx, cy: REAL;
image ← GraphicsOps.NewAisImage[root.Cat[suffixes[a]]];
[xmin,ymin,xmax,ymax] ← GraphicsOps.ImageBox[image];
imageSize ← MAX[xmax-xmin, ymax-ymin];
cx ← (xmin+xmax)/2;
cy ← (ymin+ymax)/2;
FOR d: INTEGER IN [1 .. MaxDistance] DO
rd: INTEGER ← r/(z1+(d-1)*dz);
scale: REAL ← 2*rd/imageSize;
context: Graphics.Context;
mark: Graphics.Mark;
pp.pics[d][a] ← GraphicsOps.NewBitmap[width: rd*2, height: rd*2];
context ← GraphicsOps.NewContextFromBitmap[pp.pics[d][a]];
Graphics.Translate[context, rd, rd];
IF negate THEN {
Graphics.DrawBox[context, [-rd, -rd, rd, rd]];
mark ← Graphics.Save[context]};
Graphics.Scale[context, scale, scale];
Graphics.Translate[context, -cx, -cy];
Graphics.SetCP[context, 0, 0];
Graphics.DrawImage[context, image];
IF negate THEN {
Graphics.Restore[context, mark];
[] ← Graphics.SetPaintMode[context, invert];
Graphics.DrawBox[context, [-rd, -rd, rd, rd]]};
ENDLOOP;
ENDLOOP;
MessageWindow.Append[" done", FALSE];
IF myself # NIL THEN myself.pausing ← FALSE;
END;
suffixes: ARRAY Angle OF ROPE ← ["Back.ais", "Right.ais", "Front.ais", "Left.ais"];
runMess: ROPENIL;
runError: BOOLEANFALSE;
Setup: PROC =
BEGIN
arrowPath: Graphics.Path ← Graphics.NewPath[8];
pp: Graphics.Path ← Graphics.NewPath[8];
f, b: Number;
count: REF CARDINALNARROW[Atom.GetProp[atom: $MazeWar, prop: $PlayerCount]];
playerRope: ROPENIL;
IF count = NIL THEN Atom.PutProp[atom: $MazeWar, prop: $PlayerCount, val: count ← NEW [CARDINAL ← 0]];
count^ ← playerNumber ← count^ + 1;
TRUSTED {myAddr ← PupDefs.GetLocalPupAddress[[0, 0], [[0], [0], [0, 0]]]};
myInstance ← IO.PutFR["%b#%b#%b", IO.card[myAddr.net], IO.card[myAddr.host], IO.card[playerNumber]];
IF playerNumber # 1 THEN playerRope ← IO.PutFR["%g", IO.card[playerNumber]];
[runMess, runError] ← CommandTool.Run["/Ivy/Spreitzer/Games/MazeWarCommon.BCD"];
[] ← Random.Init[seed: -1];
{q: REAL = 2; q2: REAL = 5;
Graphics.MoveTo[arrowPath, tvd/q, 0];
Graphics.LineTo[arrowPath, 0, tvd/q];
Graphics.LineTo[arrowPath, 0, tvd/q2];
Graphics.LineTo[arrowPath, -tvd/q, tvd/q2];
Graphics.LineTo[arrowPath, -tvd/q, -tvd/q2];
Graphics.LineTo[arrowPath, 0, -tvd/q2];
Graphics.LineTo[arrowPath, 0, -tvd/q];
Graphics.LineTo[arrowPath, tvd/q, 0];
};
FOR a: Angle IN Angle DO
context: Context;
topviews[a] ← GraphicsOps.NewBitmap[tvd, tvd];
context ← GraphicsOps.NewContextFromBitmap[topviews[a]];
Graphics.Translate[context, tvd/2, tvd/2];
Graphics.Rotate[context, a*-90];
Graphics.DrawArea[context, arrowPath];
--no more need for arrows, so commented out--
FOR d: INTEGER IN [1 .. MaxDistance] DO
Draw: PROC [x, z: REAL] =
BEGIN
y: REAL;
[z, x] ← Rotate[z, x, a];
z ← ((z+1)*dz/2 + (d-1)*dz + z1)/r;
y ← -1/z;
x ← x/z;
Graphics.LineTo[pp, x, y];
END;
Move: PROC [x, z: REAL] =
BEGIN
y: REAL;
[z, x] ← Rotate[z, x, a];
z ← ((z+1)*dz/2 + (d-1)*dz + z1)/r;
y ← -1/z;
x ← x/z;
Graphics.MoveTo[pp, x, y];
END;
context: Context;
rd: INTEGER ← (s + d-1)/d;
defaultPlayerPics.pics[d][a] ← GraphicsOps.NewBitmap[width: rd*2, height: rd*2];
context ← GraphicsOps.NewContextFromBitmap[defaultPlayerPics.pics[d][a]];
Graphics.Translate[context, rd, rd];
Move[0, 1];
Draw[-1, 0];
Draw[-.5, 0];
Draw[-.5, -1];
Draw[.5, -1];
Draw[.5, 0];
Draw[1, 0];
Draw[0, 1];
Graphics.DrawArea[context, pp];
ENDLOOP;
ENDLOOP;
f ← z1;
b ← hither;
FOR d: INTEGER IN [0 .. MaxDistance] DO
Start: PROC [piece: Piece] = {
rmin: Number ← r/f;
rmax: Number ← r/b;
bounds: Box;
bounds ← SELECT piece FROM
LeftFilled => [-rmax, -rmax, -rmin, rmax],
LeftGap => [-rmax, -rmin, -rmin, rmin],
LeftEdge => [-rmin, -rmin, -rmin, rmin],
RightFilled => [rmin, -rmax, rmax, rmax],
RightGap => [rmin, -rmin, rmax, rmin],
RightEdge => [rmin, -rmin, rmin, rmin],
HorEdges => [-rmin, -rmin, rmin, rmin],
ENDCASE => ERROR;
bounds.xmin ← bounds.xmin - 1;
bounds.ymin ← bounds.ymin - 1;
bounds.xmax ← bounds.xmax + 1;
bounds.ymax ← bounds.ymax + 1;
pieces[d][piece] ← [
bitmap: GraphicsOps.NewBitmap[
width: bounds.xmax-bounds.xmin,
height: bounds.ymax-bounds.ymin],
xorigin: -bounds.xmin,
yorigin: bounds.ymax];
context ← GraphicsOps.NewContextFromBitmap[pieces[d][piece].bitmap];
Graphics.Translate[context, -bounds.xmin, -bounds.ymin];
};
Line: PROC [a, b: Pt3] = INLINE {
Graphics.SetCP[context, a.x/a.z, a.y/a.z];
Graphics.DrawTo[context, b.x/b.z, b.y/b.z]};
context: Graphics.Context;
r2: Number ← f*r/b;
Start[LeftFilled]; Line[[-r, r, f], [-r, r, b]]; Line[[-r, -r, f], [-r, -r, b]];
Start[LeftGap]; Line[[-r2, r, f], [-r, r, f]]; Line[[-r2, -r, f], [-r, -r, f]];
Start[LeftEdge]; Line[[-r, r, f], [-r, -r, f]];
Start[RightFilled]; Line[[ r, r, f], [ r, r, b]]; Line[[ r, -r, f], [ r, -r, b]];
Start[RightGap]; Line[[ r2, r, f], [ r, r, f]]; Line[[ r2, -r, f], [ r, -r, f]];
Start[RightEdge]; Line[[r, r, f], [r, -r, f]];
Start[HorEdges]; Line[[-r, r, f], [r, r, f]]; Line[[-r, -r, f], [r, -r, f]];
b ← f;
f ← f + dz;
ENDLOOP;
ViewerOps.RegisterViewerClass[flavor: mazeViewerFlavor, class: mazeViewerClass];
Commander.Register[key: Rope.Cat["MazeWar", playerRope], proc: StartPlayer, doc: "starts playing maze war; see MazeWarDoc.Tioga for possible command line arguments"];
Commander.Register[key: Rope.Cat["MazeWarStop", playerRope], proc: StopPlayer, doc: "exits maze war game"];
Commander.Register[key: Rope.Cat["WhoAmI", playerRope], proc: WhoAmI, doc: "gives your MazeWar identification"];
Commander.Register[key: Rope.Cat["MazeWarContact", playerRope], proc: Contact, doc: "MazeWarContact \"nn#mmm#ss\" iiiii makes contact with that MazeWar player"];
END;
Box: TYPE = RECORD [xmin, ymin, xmax, ymax: Number];
StupidFuckingWarnings: PUBLIC SIGNAL [ATOM] = CODE;
Setup[];
END.