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; 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 cursor: crossHairsCircle -- standard cursor when mouse is in viewer ]]; ViewerOps.RegisterViewerClass[$Football, viewerClass]; END; Noop: ViewerClasses.ModifyProc = TRUSTED {}; DestroyMe: ViewerClasses.DestroyProc = TRUSTED BEGIN END; TipMe: ViewerClasses.NotifyProc = TRUSTED BEGIN x, y: REAL _ 1000; InputFocus.SetInputFocus[self]; FOR input _ input, input.rest DO IF input = NIL THEN EXIT; WITH input.first SELECT FROM z: TIPUser.TIPScreenCoords => {[x, y] _ Translate[z.mouseX, z.mouseY]; LOOP}; ENDCASE; 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 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 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; 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; 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 number: Rope.ROPE; IF erase THEN Erase[context, scoreboard]; MessageWindow.Clear[]; -- remove any messages. 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 "]; number _ Convert.RopeFromInt[game.score[home]]; DrawRope[context, number, statsX+20, statsY-15]; number _ Convert.RopeFromInt[game.score[visitors]]; DrawRope[context, number, statsX+185, statsY-15]; 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]; 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."]; context.SetCP[statsX - 20, statsY - 60]; IF game.penalties # [none, none] THEN {DisplayPenalties[context]; RETURN}; context.SetCP[statsX + 60, statsY - 60]; 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]}; 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 . . . Ύ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 ****************************************************************** Viewers Class Interface ****************************************************************** coordSys: top, remove the player from the game [self: Viewer, input: LIST OF REF ANY] N.B. Called at Process.priorityForeground! select out the coordinates select an atom offField => FootballMaster.KickOff[myTeam]; 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; ****************************************************************** Viewers Paint Proc ****************************************************************** ****************************************************************** Display Procedures ****************************************************************** (home) HOME* First Quarter VISITORS 7 7:14 14 4 - timeouts - 4 first down and goal on the 4 first line second line (clock drawn separately) third line fourth line display status IF game.possessor # game.offense AND game.pass THEN {context.DrawRope["INTERCEPTION! "]; Flash[context, scoreboard]}; ****************************************************************** Displaying the planned play ****************************************************************** Κο– "cedar" style˜Icodešœ™Kšœ:™:K™9K˜šΟk œ˜ Kšœ œ!˜0Kšœœ˜K˜K˜Kšœœ˜1Kšœœ˜šœ œ˜K˜DK˜=—Kšœœ ˜Kšœ œ˜!Kš œœ7œœœ˜^Kšœœ=˜HKšœœ˜*Kšœœ˜#Kšœœ ˜Kšœœœ ˜7Kšœœ˜Kšœœ5˜BKšœœ˜šœœ˜K˜0K˜%—Kšœ œ;˜JKšœ œ+˜Kšœ ˜&Kšœ +˜;Kšœ 6˜KKšœ œ ˜#Kšœ  ˜1Kšœ 0˜DKšœ™Kšœ *˜DK˜———K˜6Kšœ˜K˜—K˜KšŸœœ˜,šŸ œ˜'Kšœ˜ Kšœ™Kšœ˜K˜—šŸœ˜"Kšœ˜ Kšœ&™&Kšœ+™+Kšœœ˜K˜šœ˜ Kšœ œœœ˜Kšœ™šœ œ˜KšœGœ˜MKšœ˜—Kšœ™šœ ˜KšœSœ˜[˜ Kšœ'œ˜-K˜—˜ Kšœ'œ˜.K˜—K˜%K˜+šœ œ ˜Kšœ$ ˜K˜=—K˜šœ˜K˜*K˜*K˜*Kšœ˜—Kšœ˜K˜—šžœœ;˜OKš˜šœ ˜Kšœœ˜K˜*K˜9K˜8K˜2Kšœ+˜2—šœ˜K˜)K˜1Kšœ˜—Kšœ˜K˜—šž œ œ#œ œ˜TKš˜Kš œœœœœ˜KšœœA˜NKšœœœ œ˜@Kšœœ,˜@K˜Kšœ˜K˜—šž œœ3œ˜PKš˜K˜ Kšœœ˜ Kšœœ˜Kšœ œ˜K˜K˜K˜Kšœœœœ˜+Kšœœœ˜MKšœœœœ˜šœœœ˜*K˜0—K˜K˜K˜K˜Kšœ˜K˜—šž œœ#œ˜>Kš˜Kšœœ˜ Kšœœ˜K˜6K˜6Kšœœ˜9Kšœœœœ˜šœœœ˜*K˜*—K˜K˜K˜Kšœ˜K˜—šž œœœœ˜1Kšœœ˜$K˜K˜—šžœœ,œ˜AKšœ1˜7K˜—šžœœ" œœ˜HKšœ/˜5K˜—šžœœ'œœ˜HKšœ/˜5K˜—K™KšœB™BKšœ™KšœB™BK˜Kšœœœ ˜Kšœ œœœ˜?K˜šž œœ#œ˜