FootballViewer.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
John Maxwell on: January 28, 1983 11:20 am
Spreitzer, May 1, 1984 11:42:52 pm PDT
Russ Atkinson (RRA) January 23, 1985 8:36:04 pm PST
DIRECTORY
Commander USING [Handle, CommandProc, Register],
Convert USING [RopeFromInt],
FootballInternal,
FootballMaster,
FootballMasterRpcControl USING [ImportInterface],
FS USING [ExpandName],
Graphics USING [black, Box, Context, DrawBox, DrawChar, DrawRope, DrawTo, GetBounds, SetColor, SetCP, SetPaintMode, SetStipple, Translate, white],
GraphicsColor USING [green],
InputFocus USING [SetInputFocus],
IO USING [Close, RopeFromROS, GetTokenRope, int, PutF, RIS, ROS, STREAM, EndOfStream],
Menus USING [ClickProc, CreateEntry, CreateMenu, InsertMenuEntry, Menu],
MessageWindow USING[Append, Blink, Clear],
Process USING [Detach, SetTimeout],
Real USING [RoundI],
Rope USING [Cat, Find, FromChar, Length, ROPE, Substr],
RPC USING [ImportFailed],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPTable],
UserCredentials USING [Get],
ViewerClasses USING [DestroyProc, ModifyProc, NotifyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetMenu],
ViewerTools USING [GetSelectionContents, MakeNewTextViewer];
FootballViewer: MONITOR
IMPORTS Commander, Convert, FootballInternal, FootballMaster, FootballMasterRpcControl, FS, Graphics, InputFocus, IO, Menus, MessageWindow, Process, Real, Rope, RPC, TIPUser, UserCredentials, ViewerOps, ViewerTools
EXPORTS FootballInternal = { OPEN FootballInternal, FootballMaster;
ROPE: TYPE = Rope.ROPE;
game: PUBLIC Game;
myTeam: PUBLIC Team;
displayLine: INTEGER ← 20;
viewer: PUBLIC ViewerClasses.Viewer;
shadow: ARRAY Position OF RECORD[oldX, oldY: REAL, char: CHAR];
docName: ROPEFS.ExpandName["FootballDoc.Tioga"].fullFName;
menu: Menus.Menu;
imported: BOOLFALSE;
selected: PUBLIC Position ← none;
setUp: BOOLFALSE;
deltaX: REAL;
deltaY: REAL = 50;
lastState: State;
lastTime: INTEGER ← 0;
penaltyState: PenaltyState ← none;
timeouts: ARRAY Team OF INTEGER ← [4,4];
penalties: ARRAY Team OF Penalty ← [none, none];
oldScore: ARRAY Team OF INTEGER ← [0, 0];
timer: CONDITION;
yard: INTEGER ← 6;
grey: CARDINAL ← 055132B;
black: CARDINAL ← 177777B;
statsX: REAL ← 37*yard;
statsY: REAL ← 100 + fieldWidth*yard;
scoreboard: Graphics.Box = [statsX - 30, statsY - 65, statsX + 300, statsY + 30];
old: ARRAY [0..6] OF Pattern;
Pattern: TYPE = RECORD[x1, y1, x2, y2: REAL, command: Command];
d: REAL ← 4;
block: REAL ← 2.5;
zone: REAL ← 10;
Initialize: PROC = {
Process.SetTimeout[@timer, 1];
game ← NEW[GameRec ← []];
FOR i: Position IN Players DO
game.player[i].position ← i;
ENDLOOP;
CreateMenu[];
MakeFootballClass[];
Commander.Register[key: "Football", proc: Create, doc: "Football arcade game."];
};
CreateMenu: PROC = {
menu ← Menus.CreateMenu[4];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["DisableDelayOfGame", MyCommand, $DisableDelayOfGame]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Decline", MyCommand, $Decline]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Accept", MyCommand, $Accept]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["TimeOut", MyCommand, $TimeOut]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Kick", MyCommand, $Kick]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Pass", MyCommand, $Pass]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Hike", MyCommand, $Hike]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry[name: "WritePlays", proc: PlayBook, clientData: $Write, guarded: TRUE]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["ReadPlays", PlayBook, $Read]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["SavePlay", MySave]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["SaveSetUp", MySave, $SetUp]];
Menus.InsertMenuEntry[menu, Menus.CreateEntry["Help", MyCommand, $Help]];
};
MySave: Menus.ClickProc = TRUSTED {FootballInternal.SavePlay[
side: IF game.offense = myTeam THEN offense ELSE defense,
name: ViewerTools.GetSelectionContents[],
setUp: clientData = $SetUp];
};
MyCommand: Menus.ClickProc = TRUSTED {
TipMe[NIL, LIST[clientData]];
};
PlayBook: Menus.ClickProc = TRUSTED {
name: ROPE ← ViewerTools.GetSelectionContents[];
IF name.Length[] < 2 THEN {
name ← UserCredentials.Get[].name;
name ← Rope.Substr[name, 0, Rope.Find[name, "."]];
name ← Rope.Cat[name, ".plays"]};
IF clientData = $Read
THEN FootballInternal.ReadPlayBook[name]
ELSE FootballInternal.WritePlayBook[name];
};
Create: SAFE PROC [cmd: Commander.Handle] RETURNS [result: REFNIL, msg: ROPENIL] --Commander.CommandProc-- = TRUSTED {
self: ROPE;
remote, local: ROPENIL;
failed: BOOLFALSE;
commandLineStream: IO.STREAMIO.RIS[cmd.commandLine];
self ← UserCredentials.Get[].name;
remote ← IO.GetTokenRope[commandLineStream !IO.EndOfStream => CONTINUE].token;
IF remote.Length[] # 0 THEN local ← IO.GetTokenRope[commandLineStream !IO.EndOfStream => CONTINUE].token;
IF remote.Length[] = 0 THEN remote ← self;
IF local.Length[] = 0 THEN local ← self;
IF ~imported THEN {
myTeam ← visitors;
FOR i: CARDINAL IN [0..2) DO
failed ← FALSE;
FootballMasterRpcControl.ImportInterface [
interfaceName: [instance: remote]
! RPC.ImportFailed => {failed ← TRUE; CONTINUE}];
IF failed AND Rope.Find[remote, "."] < 0
THEN remote ← Rope.Cat[remote, ".pa"] ELSE EXIT;
ENDLOOP;
IF failed THEN {
failed ← FALSE;
myTeam ← home;
FootballInternal.StartServer[local];
FootballMasterRpcControl.ImportInterface [
interfaceName: [instance: local]
! RPC.ImportFailed => {failed ← TRUE; CONTINUE}];
IF failed THEN RETURN[$Failure, "couldn't import self!!!"]};
imported ← TRUE};
IF viewer = NIL OR viewer.destroyed THEN {
newProcess: BOOL ← viewer = NIL;
viewer ← ViewerOps.CreateViewer[
flavor: $Football,
info: [
name: "Football",
iconic: FALSE,
data: NIL,
scrollable: FALSE],
paint: FALSE];
ViewerOps.SetMenu[viewer, menu];
ViewerOps.PaintViewer[viewer, all];
IF newProcess THEN Process.Detach[FORK FootballInternal.Control[]]};
RETURN[$Success];
};
******************************************************************
Viewers Class Interface
******************************************************************
MakeFootballClass: PROC = {
tipTable: TIPUser.TIPTable ← TIPUser.InstantiateNewTIPTable["Football.tip"];
viewerClass: ViewerClasses.ViewerClass ← NEW[
ViewerClasses.ViewerClassRec ←
[paint: PaintMe, -- called whenever the Viewer should repaint
notify: TipMe,  -- TIP input events
modify: Noop,  -- InputFocus changes reported through here
destroy: DestroyMe, -- called before Viewer structures freed on destroy op
scroll: NIL, -- document scrolling
icon: document, -- picture to display when small
tipTable: tipTable, -- could be moved into Viewer instance if needed
coordSys: top,
cursor: crossHairsCircle -- standard cursor when mouse is in viewer
]];
ViewerOps.RegisterViewerClass[$Football, viewerClass];
};
Noop: ViewerClasses.ModifyProc = TRUSTED {};
DestroyMe: ViewerClasses.DestroyProc = TRUSTED {
remove the player from the game
FootballInternal.StopServer[];
};
TipMe: ViewerClasses.NotifyProc = TRUSTED {
[self: Viewer, input: LIST OF REF ANY]
N.B. Called at Process.priorityForeground!
x, y: REAL ← 1000;
InputFocus.SetInputFocus[self];
FOR input ← input, input.rest DO
IF input = NIL THEN EXIT;
select out the coordinates
WITH input.first SELECT FROM
z: TIPUser.TIPScreenCoords => {[x, y] ← Translate[z.mouseX, z.mouseY]; LOOP};
ENDCASE;
select an atom
SELECT input.first FROM
$Help => [] ← ViewerTools.MakeNewTextViewer[[name: docName, file: docName, iconic: FALSE]];
$Accept => {
FootballMaster.PenaltyResponse[myTeam, TRUE];
MessageWindow.Clear[]};
$Decline => {
FootballMaster.PenaltyResponse[myTeam, FALSE];
MessageWindow.Clear[]};
$Hike => FootballMaster.Hike[myTeam];
$TimeOut => FootballMaster.TimeOut[myTeam];
$Pass => IF x = 1000
THEN SetCommand[ball, [pass, [null[]]]] -- defaults location
ELSE SetCommand[ball, [pass, [absolute[x, y]]]];
$Kick => IF x = 1000
THEN SetCommand[ball, [kick, [goal[]]]]
ELSE SetCommand[ball, [kick, [absolute[x, y]]]];
$DoIt => SELECT game.state FROM
offField => FootballMaster.KickOff[myTeam];
setUp => FootballMaster.Hike[myTeam]
ENDCASE => SetCommand[ball, [pass, [null[]]]];
$Select => selected ← Select[x, y, myTeam, 10];
$Deselect => selected ← none;
$SetUp => IF selected # none THEN SetCommand[selected, [run, [absolute[x, y]]], TRUE];
$Run => IF selected # none THEN SetCommand[selected, [run, [absolute[x, y]]]];
$Block => {
target: Position ← Select[x, y, Opponent[myTeam], 1];
IF selected = none THEN RETURN;
IF target = selected THEN target ← none;
IF (input.rest = NIL OR input.rest.first # $Deselect)
AND ABS[game.player[selected].x - x] < 3
AND ABS[game.player[selected].y - y] < 3 THEN RETURN;
IF target = none
THEN SetCommand[selected, [block, [absolute[x, y]]]]
ELSE SetCommand[selected, [block, [player[target]]]]};
$BlockDefault => IF selected # none THEN SetCommand[selected, [block, [player[none]]]];
$Stop => IF selected # none THEN SetCommand[selected, [stop, [null[]]]];
$Freeze => IF selected # none THEN SetCommand[selected, [stop, [null[]]], TRUE];
$DisableDelayOfGame => FootballMaster.DisableDelayOfGame[];
ENDCASE;
ENDLOOP;
};
Translate: PROC[x, y: REAL] RETURNS[REAL, REAL] = {
x ← x - deltaX;
y ← y - deltaY;
RETURN[x/yard, y/yard];
};
Select: PROC[x, y: REAL, team: Team, delta: REAL ← 1000] RETURNS[player: Position] = {
min, temp: REAL ← 1000;
FOR i: Position IN Players DO
IF TeamOf[i] # team THEN LOOP;
temp ← ABS[game.player[i].x - x] + ABS[game.player[i].y - y];
IF temp < min THEN {min ← temp; player ← i};
ENDLOOP;
IF min > delta THEN player ← none;
};
******************************************************************
Viewers Paint Proc
******************************************************************
PaintMe: ViewerClasses.PaintProc = TRUSTED {
[self: ViewerClasses.Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL]
new: INTEGER;
clock: INTEGERMAX[0, game.clock];
fieldErased: BOOLFALSE;
scoreboardErased: BOOLFALSE;
IF self.iconic THEN RETURN;
new ← GetDisplayLine[Real.RoundI[game.player[game.ballCarrier].x],
displayLine, self.cw/yard];
deltaX ← self.cw/2 - new*yard;
Graphics.Translate[context, deltaX, deltaY];
fieldErased ← (new # displayLine) OR whatChanged = NIL;
IF fieldErased THEN DisplayField[context, whatChanged # NIL];
scoreboardErased ← (game.state = huddle AND lastState # huddle)
OR (game.state = offField AND lastState # offField)
OR game.timeouts # timeouts OR game.penalties # penalties
OR game.score # oldScore OR game.penaltyState # penaltyState
OR fieldErased;
IF scoreboardErased THEN DisplayStats[context, ~fieldErased];
IF scoreboardErased OR (clock # lastTime)
THEN DisplayClock[context, clock, ~scoreboardErased];
[] ← Graphics.SetPaintMode[context, invert];
IF fieldErased OR (game.state = option AND lastState # option)
THEN ErasePlay[context, ~fieldErased];
IF game.state < option THEN DisplayPlay[context];
FOR i: Position IN Players DO
DisplayPlayer[context, @game.player[i], ~fieldErased];
ENDLOOP;
DisplayBall[context, ~fieldErased];
IF game.penalties # penalties AND game.penalties # [none, none]
THEN Flash[context, Graphics.GetBounds[context], grey];
IF game.penaltyState # penaltyState AND game.penaltyState = asking
AND game.penalties[Opponent[myTeam]] # none THEN {
MessageWindow.Append["Please accept or decline penalty.", TRUE];
MessageWindow.Blink[]};
lastTime ← clock;
displayLine ← new;
lastState ← game.state;
oldScore ← game.score;
timeouts ← game.timeouts;
penalties ← game.penalties;
penaltyState ← game.penaltyState;
};
Flash: ENTRY PROC[context: Graphics.Context, box: Graphics.Box, color: CARDINAL] = {
Graphics.SetStipple[context, color];
Graphics.DrawBox[context, box]; WAIT timer;
Graphics.DrawBox[context, box];
Graphics.SetStipple[context, black];
};
Erase: PROC[context: Graphics.Context, box: Graphics.Box] = {
[] ← Graphics.SetPaintMode[context, opaque];
IF viewer.column = color
THEN Graphics.SetColor[context, GraphicsColor.green]
ELSE Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, box];
Graphics.SetColor[context, Graphics.black];
[] ← Graphics.SetPaintMode[context, transparent];
};
GetDisplayLine: PROC[ballX, displayLine, screenWidth: INTEGER] RETURNS[INTEGER] = {
SELECT TRUE FROM
displayLine - ballX > screenWidth/2 =>
displayLine ← MIN[MAX[ballX, 0], 100];
displayLine - ballX > screenWidth/2 - 5 =>
displayLine ← screenWidth/2 - 5 + ballX;
ballX - displayLine > screenWidth/2 =>
displayLine ← MIN[MAX[ballX, 0], 100];
ballX - displayLine > screenWidth/2 - 5 =>
displayLine ← ballX - screenWidth/2 + 5;
ENDCASE;
IF displayLine + 10 < screenWidth/2 THEN
displayLine ← - 10 + screenWidth/2;
IF 100 - displayLine + 10 < screenWidth/2 THEN
displayLine ← 100 + 10 - screenWidth/2;
RETURN[displayLine];
};
******************************************************************
Display Procedures
******************************************************************
DisplayField: PROC[context: Graphics.Context, erase: BOOL] = {
x, y: REAL;
rope: ROPE;
IF erase THEN Erase[context, Graphics.GetBounds[context]];
IF viewer.column = color THEN {
[] ← Graphics.SetPaintMode[context, opaque];
Graphics.SetColor[context, GraphicsColor.green];
Graphics.DrawBox[context, Graphics.GetBounds[context]];
Graphics.SetColor[context, Graphics.white]};
y ← fieldWidth*yard;
DrawLine[context, -10*yard, 0, 110*yard, 0];
DrawLine[context, -10*yard, fieldWidth*yard, 110*yard, fieldWidth*yard];
DrawLine[context, -10*yard, 0, -10*yard, y];
DrawLine[context, 110*yard, 0, 110*yard, y];
FOR i: INTEGER IN [0..10] DO
x ← i*10*yard;
y ← fieldWidth*yard;
DrawLine[context, x, 0, x, y];
DrawLine[context, x-3, y/3, x+3, y/3];
DrawLine[context, x-3, 2*y/3, x+3, 2*y/3];
IF i = 0 OR i = 10 THEN rope ← Rope.FromChar['G]
ELSE rope ← Convert.RopeFromInt[MIN[100-i*10, i*10]];
DrawRope[context, rope, x-5, y+5];
DrawRope[context, rope, x-5, -15];
ENDLOOP;
IF viewer.column # color THEN {
Graphics.SetStipple[context, grey];
Graphics.DrawBox[context, [-10*yard, 0, 0, fieldWidth*yard]];
Graphics.DrawBox[context, [100*yard, 0, 110*yard, fieldWidth*yard]];
Graphics.SetStipple[context, black]};
};
DisplayStats: PROC[context: Graphics.Context, erase: BOOL] = {
   (home)
HOME* First Quarter VISITORS
7 7:14 14
4 - timeouts - 4
first down and goal on the 4
number: ROPE;
IF erase THEN Erase[context, scoreboard];
MessageWindow.Clear[]; -- remove any messages.
first line
IF myTeam = home
THEN DrawRope[context, "(home)", statsX+82, statsY+15]
ELSE DrawRope[context, "(visitors)", statsX+75, statsY+15];
context.SetCP[statsX, statsY];
IF game.offense = home
THEN Graphics.DrawRope[context, "HOME* "]
ELSE Graphics.DrawRope[context, "HOME "];
SELECT game.quarter FROM
1 => Graphics.DrawRope[context, " First Quarter "];
2 => Graphics.DrawRope[context, " Second Quarter "];
3 => Graphics.DrawRope[context, " Third Quarter "];
4 => Graphics.DrawRope[context, " Fourth Quarter "];
5 => Graphics.DrawRope[context, " Game Over "];
ENDCASE => ERROR;
IF game.offense = visitors
THEN Graphics.DrawRope[context, "VISITORS* "]
ELSE Graphics.DrawRope[context, "VISITORS "];
second line
number ← Convert.RopeFromInt[game.score[home]];
DrawRope[context, number, statsX+20, statsY-15];
(clock drawn separately)
number ← Convert.RopeFromInt[game.score[visitors]];
DrawRope[context, number, statsX+185, statsY-15];
third line
number ← Convert.RopeFromInt[game.timeouts[home]];
DrawRope[context, number, statsX+20, statsY-30];
DrawRope[context, " - timeouts - ", statsX+60, statsY-30];
number ← Convert.RopeFromInt[game.timeouts[visitors]];
DrawRope[context, number, statsX+185, statsY-30];
fourth line
SELECT game.down FROM
1 => number ← "first down and ";
2 => number ← "second down and ";
3 => number ← "third down and ";
4 => number ← "fourth down and ";
ENDCASE => number ← ">>>> down and ";
DrawRope[context, number, statsX-20, statsY-45];
IF game.firstDownMarker <= 0 OR game.firstDownMarker >= 100 THEN number ← "goal"
ELSE {toGo: INTEGER;
toGo ← Real.RoundI[ABS[game.scrimmage-game.firstDownMarker]];
number ← IF toGo < 1 THEN "inches" ELSE Convert.RopeFromInt[toGo]};
Graphics.DrawRope[context, number];
Graphics.DrawRope[context, " to go on the "];
number ← Convert.RopeFromInt[
MIN[Real.RoundI[game.scrimmage], Real.RoundI[100-game.scrimmage]]];
Graphics.DrawRope[context, number];
Graphics.DrawRope[context, " yard line."];
display status
context.SetCP[statsX - 20, statsY - 60];
IF game.penalties # [none, none] THEN {DisplayPenalties[context]; RETURN};
context.SetCP[statsX + 60, statsY - 60];
IF game.possessor # game.offense AND game.pass
THEN {Graphics.DrawRope[context, "INTERCEPTION! "]; Flash[context, scoreboard]};
SELECT game.score[home] - oldScore[home] FROM
7 => FlashMessage["TOUCHDOWN! "];
3 => FlashMessage["FIELD GOAL! "];
2 => FlashMessage["SAFETY! "];
ENDCASE;
SELECT game.score[visitors] - oldScore[visitors] FROM
7 => FlashMessage["TOUCHDOWN! "];
3 => FlashMessage["FIELD GOAL! "];
2 => FlashMessage["SAFETY! "];
ENDCASE;
};
DisplayPenalties: PROC[context: Graphics.Context] = {
DisplayPenalty[context, game.penalties[home], home];
IF game.penalties[visitors] # none THEN {
IF game.penalties[home] # none THEN Graphics.DrawRope[context, " and "];
DisplayPenalty[context, game.penalties[visitors], visitors]};
Graphics.DrawRope[context, ": "];
SELECT game.penaltyState FROM
accepted => Graphics.DrawRope[context, "ACCEPTED."];
declined => Graphics.DrawRope[context, "DECLINED."];
canceled => Graphics.DrawRope[context, "CANCELED."];
ENDCASE;
};
DisplayPenalty: PROC[context: Graphics.Context, penalty: Penalty, team: Team] = {
SELECT penalty FROM
none => RETURN;
offSides => Graphics.DrawRope[context, "offsides "];
forwardPass => Graphics.DrawRope[context, "illegal forward pass "];
grounding => Graphics.DrawRope[context, "intentional grounding "];
delayOfGame => Graphics.DrawRope[context, "delay of game "];
ENDCASE => Graphics.DrawRope[context, "illegal procedure "];
SELECT team FROM
home => Graphics.DrawRope[context, "against home"];
visitors => Graphics.DrawRope[context, "against visitors"];
ENDCASE;
};
DisplayClock: PROC[context: Graphics.Context, clock: INTEGER, erase: BOOL] = {
stream: IO.STREAMIO.ROS[];
IF erase THEN Erase[context, [statsX+89, statsY-16, statsX + 150, statsY-3]];
stream.PutF["%2d:%02d", IO.int[clock/60], IO.int[clock MOD 60]];
DrawRope[context, IO.RopeFromROS[stream], statsX+90, statsY-15];
stream.Close[];
};
DisplayPlayer: PROC[context: Graphics.Context, player: Player, erase: BOOL] = {
i: Position;
x, y: REAL;
same: BOOL;
char: CHAR;
i ← player.position;
x ← Real.RoundI[player.x*yard];
y ← Real.RoundI[player.y*yard];
char ← IF TeamOf[i] = home THEN 'O ELSE 'X;
same ← (x = shadow[i].oldX AND y = shadow[i].oldY AND char = shadow[i].char);
IF erase AND same THEN RETURN;
IF erase AND ~same THEN DrawChar[context, shadow[i].char, shadow[i].oldX, shadow[i].oldY];
DrawChar[context, char, x, y];
shadow[i].oldX ← x;
shadow[i].oldY ← y;
shadow[i].char ← char;
};
DisplayBall: PROC[context: Graphics.Context, erase: BOOL] = {
x, y: REAL;
same: BOOL;
x ← Real.RoundI[game.player[game.ballCarrier].x*yard];
y ← Real.RoundI[game.player[game.ballCarrier].y*yard];
same ← (x = shadow[ball].oldX AND y = shadow[ball].oldY);
IF erase AND same THEN RETURN;
IF erase AND ~same THEN DrawChar[context,
'*, shadow[ball].oldX, shadow[ball].oldY];
DrawChar[context, '*, x, y];
shadow[ball].oldX ← x;
shadow[ball].oldY ← y;
};
FlashMessage: PROC[message: ROPE] = {
MessageWindow.Append[message, TRUE];
MessageWindow.Blink[];
};
DrawLine: PROC[context: Graphics.Context, x1, y1, x2, y2: REAL] =
INLINE {context.SetCP[x1, y1]; context.DrawTo[x2, y2]};
DrawChar: PROC[context: Graphics.Context, char: CHAR, x, y: REAL] =
INLINE {context.SetCP[x, y]; context.DrawChar[char]};
DrawRope: PROC[context: Graphics.Context, rope: ROPE, x, y: REAL] =
INLINE {context.SetCP[x, y]; Graphics.DrawRope[context, rope]};
******************************************************************
Displaying the planned play
******************************************************************
ErasePlay: PROC[context: Graphics.Context, erase: BOOL] = {
Graphics.SetStipple[context, grey];
FOR i: CARDINAL IN [0..6] DO
IF erase THEN DrawPattern[context, old[i]];
old[i] ← [0, 0, 0, 0, [stop, [null[ ]]]];
ENDLOOP;
Graphics.SetStipple[context, black];
};
DisplayPlay: PROC[context: Graphics.Context] = {
new: Pattern;
Graphics.SetStipple[context, grey];
FOR i: CARDINAL IN [0..6) DO
new.command ← planned.commands[i];
new.x1 ← game.player[Add[IF myTeam = home THEN HQB ELSE VQB, i]].x;
new.y1 ← game.player[Add[IF myTeam = home THEN HQB ELSE VQB, i]].y;
[new.x2, new.y2] ← GetTarget[new.command.target, new.y1];
IF Equal[old[i], new] THEN LOOP;
DrawPattern[context, old[i]];
DrawPattern[context, new];
old[i] ← new;
ENDLOOP;
Graphics.SetStipple[context, black];
new.command ← planned.ball;
new.x1 ← new.y1 ← 0;
[new.x2, new.y2] ← GetTarget[planned.ball.target, game.player[ball].y];
IF Equal[old[6], new] THEN RETURN;
DrawPattern[context, old[6]];
DrawPattern[context, new];
old[6] ← new;
};
DrawPattern: PROC[context: Graphics.Context, p: Pattern] = {
assumes that the paintMode is 'invert'
r: REAL ← 1;
IF p.command.target = [player[ball]] THEN RETURN;
SELECT p.command.action FROM
stop => RETURN;
pass, kick => {DrawChar[context, '+, p.x2*yard, p.y2*yard]; RETURN};
ENDCASE => DrawLine[context, p.x1*yard+d, p.y1*yard+d, p.x2*yard+d, p.y2*yard+d];
SELECT p.command.target.type FROM
player => {
DrawLine[context, (p.x2-r)*yard+d, p.y2*yard+d, p.x2*yard+d, (p.y2+r)*yard+d];
DrawLine[context, (p.x2+r)*yard+d, p.y2*yard+d, p.x2*yard+d, (p.y2+r)*yard+d];
DrawLine[context, (p.x2-r)*yard+d, p.y2*yard+d, p.x2*yard+d, (p.y2-r)*yard+d];
DrawLine[context, (p.x2+r)*yard+d, p.y2*yard+d, p.x2*yard+d, (p.y2-r)*yard+d]};
relative, absolute => {
IF p.command.action = run THEN {
DrawChar[context, 'o, p.x2*yard+2, p.y2*yard+2]; RETURN};
IF game.offense # myTeam THEN r ← zone ELSE r ← block;
DrawLine[context, (p.x2-r)*yard+d, (p.y2-r)*yard+d, (p.x2-r)*yard+d, (p.y2+r)*yard+d];
DrawLine[context, (p.x2-r)*yard+d, (p.y2+r)*yard+d, (p.x2+r)*yard+d, (p.y2+r)*yard+d];
DrawLine[context, (p.x2+r)*yard+d, (p.y2+r)*yard+d, (p.x2+r)*yard+d, (p.y2-r)*yard+d];
DrawLine[context, (p.x2+r)*yard+d, (p.y2-r)*yard+d, (p.x2-r)*yard+d, (p.y2-r)*yard+d]};
ENDCASE;
};
GetTarget: PROC[target: Target, oldY: REAL] RETURNS[x, y: REAL] = {
goal: INTEGER ← Goal[Opponent[myTeam], game.quarter];
WITH t: target SELECT FROM
player => RETURN[game.player[t.position].x, game.player[t.position].y];
relative => RETURN[
game.scrimmage + (IF goal > 50 THEN t.x ELSE - t.x),
fieldWidth/2 + t.y];
absolute => RETURN[t.x, t.y];
goal => RETURN[goal, oldY];
ENDCASE => RETURN[0, 0];
};
Equal: PROC[a, b: Pattern] RETURNS[BOOL] = INLINE {
IF a.x1 # b.x1 THEN RETURN[FALSE];
IF a.x2 # b.x2 THEN RETURN[FALSE];
IF a.y1 # b.y1 THEN RETURN[FALSE];
IF a.y2 # b.y2 THEN RETURN[FALSE];
IF a.command.action # b.command.action THEN RETURN[FALSE];
WITH t: a.command.target SELECT FROM
null => IF b.command.target # t THEN RETURN[FALSE];
goal => IF b.command.target # t THEN RETURN[FALSE];
player => IF b.command.target # t THEN RETURN[FALSE];
relative => IF b.command.target # t THEN RETURN[FALSE];
absolute => IF b.command.target # t THEN RETURN[FALSE];
ENDCASE => ERROR;
RETURN[TRUE];
};
Add: PROC[p: Position, i: INTEGER] RETURNS[Position] =
INLINE {RETURN[LOOPHOLE[LOOPHOLE[p, INTEGER]+i]]};
Initialize[];
} . . .