FootballViewer.mesa
Last Edited by: John Maxwell on: January 28, 1983 11:20 am
Last Edited by: Spreitzer, August 20, 1984 2:36:02 pm PDT
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, IDProc, 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 =
BEGIN OPEN FootballInternal, FootballMaster;
game: PUBLIC Game;
myTeam: PUBLIC Team;
displayLine: INTEGER ← 20;
viewer: PUBLIC ViewerClasses.Viewer;
shadow: ARRAY Position OF RECORD[oldX, oldY: REAL, char: CHARACTER];
docName: Rope.ROPE ← FS.ExpandName["FootballDoc.Tioga"].fullFName;
Initialize:
PROCEDURE =
BEGIN
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."];
END;
CreateMenu:
PROCEDURE =
BEGIN
OPEN FootballInternal;
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]];
END;
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.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]};
menu: Menus.Menu;
imported: BOOLEAN ← FALSE;
Create:
SAFE
PROC [cmd: Commander.Handle]
RETURNS [result:
REF ←
NIL, msg: Rope.
ROPE ←
NIL]
--Commander.CommandProc-- =
TRUSTED BEGIN
self: Rope.ROPE;
remote, local: Rope.ROPE ← NIL;
failed: BOOLEAN ← FALSE;
commandLineStream: IO.STREAM ← IO.RIS[cmd.commandLine];
self ← UserCredentials.Get[].name;
remote ← IO.GetTokenRope[commandLineStream, IO.IDProc !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: BOOLEAN ← 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];
END;
******************************************************************
Viewers Class Interface
******************************************************************
selected: PUBLIC Position ← none;
setUp: BOOLEAN ← FALSE;
MakeFootballClass:
PROCEDURE =
BEGIN
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];
END;
Noop: ViewerClasses.ModifyProc = TRUSTED {};
DestroyMe: ViewerClasses.DestroyProc =
TRUSTED BEGIN
remove the player from the game
END;
TipMe: ViewerClasses.NotifyProc =
TRUSTED BEGIN
[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;
END;
Translate:
PROC[x, y:
REAL]
RETURNS[
REAL,
REAL] =
BEGIN
x ← x - deltaX;
y ← y - deltaY;
RETURN[x/yard, y/yard];
END;
Select:
PROC[x, y:
REAL, team: Team, delta:
REAL ← 1000]
RETURNS[player: Position] =
BEGIN
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;
END;
******************************************************************
Viewers Paint Proc
******************************************************************
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;
PaintMe: ViewerClasses.PaintProc =
TRUSTED BEGIN -- self: Viewer, context: Graphics.Context, whatChanged: REF ANY,
new: INTEGER;
clock: INTEGER ← MAX[0, game.clock];
fieldErased: BOOLEAN ← FALSE;
scoreboardErased: BOOLEAN ← FALSE;
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.score # oldScore THEN Flash[context, scoreboard, black];
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;
END;
Flash:
ENTRY
PROC[context: Graphics.Context, box: Graphics.Box, color:
CARDINAL] =
BEGIN
Graphics.SetStipple[context, color];
Graphics.DrawBox[context, box]; WAIT timer;
Graphics.DrawBox[context, box];
Graphics.SetStipple[context, black];
END;
Erase:
PROC[context: Graphics.Context, box: Graphics.Box] =
BEGIN
[] ← 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];
END;
GetDisplayLine:
PROC[ballX, displayLine, screenWidth:
INTEGER]
RETURNS[
INTEGER] =
BEGIN
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];
END;
******************************************************************
Display Procedures
******************************************************************
yard: INTEGER ← 8;
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];
DisplayField:
PROC[context: Graphics.Context, erase:
BOOLEAN] =
BEGIN
x, y: REAL;
rope: 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]};
END;
DisplayStats:
PROC[context: Graphics.Context, erase:
BOOLEAN] =
BEGIN
(home)
HOME* First Quarter VISITORS
7 7:14 14
4 - timeouts - 4
first down and goal on the 4
number: Rope.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 context.DrawRope["HOME* "]
ELSE context.DrawRope["HOME "];
SELECT game.quarter
FROM
1 => context.DrawRope[" First Quarter "];
2 => context.DrawRope[" Second Quarter "];
3 => context.DrawRope[" Third Quarter "];
4 => context.DrawRope[" Fourth Quarter "];
5 => context.DrawRope[" Game Over "];
ENDCASE => ERROR;
IF game.offense = visitors
THEN context.DrawRope["VISITORS* "]
ELSE context.DrawRope["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]};
context.DrawRope[number];
context.DrawRope[" to go on the "];
number ← Convert.RopeFromInt[
MIN[Real.RoundI[game.scrimmage], Real.RoundI[100-game.scrimmage]]];
context.DrawRope[number];
context.DrawRope[" 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 {context.DrawRope["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;
END;
DisplayPenalties:
PROC[context: Graphics.Context] =
BEGIN
DisplayPenalty[context, game.penalties[home], home];
IF game.penalties[visitors] # none
THEN {
IF game.penalties[home] # none THEN context.DrawRope[" and "];
DisplayPenalty[context, game.penalties[visitors], visitors]};
context.DrawRope[": "];
SELECT game.penaltyState
FROM
accepted => context.DrawRope["ACCEPTED."];
declined => context.DrawRope["DECLINED."];
canceled => context.DrawRope["CANCELED."];
ENDCASE;
END;
DisplayPenalty:
PROC[context: Graphics.Context, penalty: Penalty, team: Team] =
BEGIN
SELECT penalty
FROM
none => RETURN;
offSides => context.DrawRope["offsides "];
forwardPass => context.DrawRope["illegal forward pass "];
grounding => context.DrawRope["intentional grounding "];
delayOfGame => context.DrawRope["delay of game "];
ENDCASE => context.DrawRope["illegal procedure "];
SELECT team
FROM
home => context.DrawRope["against home"];
visitors => context.DrawRope["against visitors"];
ENDCASE;
END;
DisplayClock:
PROCEDURE[context: Graphics.Context, clock:
INTEGER, erase:
BOOLEAN] =
BEGIN
stream: IO.STREAM ← IO.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[];
END;
DisplayPlayer:
PROC[context: Graphics.Context, player: Player, erase:
BOOLEAN] =
BEGIN
i: Position;
x, y: REAL;
same: BOOLEAN;
char: CHARACTER;
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;
END;
DisplayBall:
PROC[context: Graphics.Context, erase:
BOOLEAN] =
BEGIN
x, y: REAL;
same: BOOLEAN;
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;
END;
FlashMessage:
PROC[message: Rope.
ROPE] =
INLINE {
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:
CHARACTER, x, y:
REAL] =
INLINE {context.SetCP[x, y]; context.DrawChar[char]};
DrawRope:
PROC[context: Graphics.Context, rope: Rope.
ROPE, x, y:
REAL] =
INLINE {context.SetCP[x, y]; context.DrawRope[rope]};
******************************************************************
Displaying the planned play
******************************************************************
old: ARRAY [0..6] OF Pattern;
Pattern: TYPE = RECORD[x1, y1, x2, y2: REAL, command: Command];
ErasePlay:
PROC[context: Graphics.Context, erase:
BOOLEAN] =
BEGIN
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];
END;
DisplayPlay:
PROC[context: Graphics.Context] =
BEGIN
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;
END;
d: REAL ← 4;
block: REAL ← 2.5;
zone: REAL ← 10;
DrawPattern:
PROC[context: Graphics.Context, p: Pattern] =
BEGIN -- 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;
END;
GetTarget:
PROC[target: Target, oldY:
REAL]
RETURNS[x, y:
REAL] =
BEGIN
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];
END;
Equal:
PROC[a, b: Pattern]
RETURNS[
BOOLEAN] =
INLINE BEGIN
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];
END;
Add:
PROCEDURE[p: Position, i:
INTEGER]
RETURNS[Position] =
INLINE {RETURN[LOOPHOLE[LOOPHOLE[p, INTEGER]+i]]};
Initialize[];
END . . .