<> <> <> <> DIRECTORY FS USING [StreamOpen, Error], FootballInternal USING [game, myTeam, Play, PlayRec, selected, viewer], FootballMaster, InputFocus USING [SetInputFocus], IO USING [Close, int, EndOf, EndOfStream, GetInt, GetLineRope, GetTokenRope, IDProc, Put, PutF, RIS, rope, STREAM], Menus USING [AppendMenuEntry, ClickProc, CreateEntry, FindEntry, Menu, MenuEntry, MenuLine, ReplaceMenuEntry], MessageWindow USING [Append, Blink, Clear], Process USING [SetTimeout], Real USING [RoundI], Rope USING [Equal, Length, ROPE], RPC USING [CallFailed], ViewerOps USING [PaintViewer, SetMenu], ViewerTools USING [MakeNewTextViewer]; FootballPlays: MONITOR IMPORTS FS, FootballInternal, FootballMaster, InputFocus, IO, Menus, MessageWindow, Process, Real, Rope, RPC, ViewerOps, ViewerTools EXPORTS FootballInternal SHARES Menus = BEGIN OPEN FootballInternal, FootballMaster; timer: CONDITION; delay: CARDINAL _ 1; Init: PROC = BEGIN current _ NEW[PlayRec _ []]; planned _ NEW[PlayRec _ []]; END; <<>> <<******************************************************************>> <> <<******************************************************************>> WritePlayBook: PUBLIC PROC[name: Rope.ROPE] = TRUSTED BEGIN OPEN IO; ENABLE FS.Error => {MessageWindow.Append["failed."]; CONTINUE}; stream: IO.STREAM; IF name.Length[] = 0 THEN RETURN; MessageWindow.Append["Writing plays on ", TRUE]; MessageWindow.Append[name]; MessageWindow.Append[". . . "]; stream _ FS.StreamOpen[name, create]; stream.Put[rope["-- "], rope[name], rope["\n"]]; stream.Put[rope["-- playname: {offensive | defensive} {setup | play}\n"]]; stream.Put[rope["-- {player}: {stop | run | block | guard} {target}\n"]]; stream.Put[rope["-- ball: {pass | kick} {target}\n"]]; stream.Put[rope["-- player = {QB | LE | RE | LG | RG | C}\n"]]; stream.Put[rope["-- target = {null | goal | ball | {player} | location x y (relative to scrimmage)}\n\n"]]; FOR i: Menus.MenuLine IN [1..viewer.menu.linesUsed) DO FOR entry: Menus.MenuEntry _ viewer.menu.lines[i], entry.link WHILE entry # NIL DO WritePlay[stream, NARROW[entry.clientData]]; ENDLOOP; ENDLOOP; stream.Close[]; MessageWindow.Append["done."]; [] _ ViewerTools.MakeNewTextViewer[[name: name, file: name, iconic: FALSE]]; END; WritePlay: PROC[stream: IO.STREAM, play: Play] = BEGIN OPEN IO, Real; action: Action; target: Target; stream.Put[rope[play.name], rope[": "]]; stream.Put[rope[IF play.side = offense THEN "offensive" ELSE "defensive"]]; stream.Put[rope[IF play.setUp THEN " setup" ELSE " play"], rope["\n"]]; FOR i: CARDINAL IN [0..6] DO IF i = 6 THEN { IF play.setUp OR play.side = defense THEN LOOP; action _ play.ball.action; target _ play.ball.target; IF action = stop THEN LOOP; stream.Put[rope["ball:"]]} ELSE { stream.Put[rope[PositionName[Add[HQB, i]]], rope[":"]]; action _ play.commands[i].action; target _ play.commands[i].target}; SELECT action FROM stop => stream.Put[rope[" stop"]]; run => stream.Put[rope[" run"]]; block => stream.Put[rope[IF play.side = offense THEN " block" ELSE " guard"]]; pass => stream.Put[rope[" pass"]]; kick => stream.Put[rope[" kick"]]; ENDCASE => ERROR; IF target.type = absolute THEN target _ Translate[target, myTeam]; WITH t: target SELECT FROM null => stream.Put[rope["\n"]]; goal => stream.Put[rope[" goal\n"]]; player => IF t.position = ball THEN stream.Put[rope[" ball\n"]] ELSE stream.Put[rope[" "], rope[PositionName[t.position]], rope["\n"]]; relative => stream.PutF[" location %g %g\n", int[RoundI[t.x]], int[RoundI[t.y]]]; ENDCASE => ERROR; ENDLOOP; stream.Put[rope["\n"]]; END; PositionName: PROC[player: Position] RETURNS[Rope.ROPE] = BEGIN SELECT player FROM VQB, HQB => RETURN["QB"]; VLE, HLE => RETURN["LE"]; VRE, HRE => RETURN["RE"]; VLG, HLG => RETURN["LG"]; VRG, HRG => RETURN["RG"]; VC, HC => RETURN["C"]; ENDCASE => RETURN["ball"]; END; ReadPlayBook: PUBLIC PROC[name: Rope.ROPE] = TRUSTED BEGIN ENABLE FS.Error => {MessageWindow.Append["failed."]; CONTINUE}; play: Play; line, token: Rope.ROPE; stream, file: IO.STREAM; IF name.Length[] = 0 THEN RETURN; MessageWindow.Append["Reading plays from ", TRUE]; MessageWindow.Append[name]; MessageWindow.Append[". . . "]; file _ FS.StreamOpen[name]; WHILE ~file.EndOf[] DO ENABLE IO.EndOfStream => LOOP; line _ file.GetLineRope[]; -- reads but doesn't return CR stream _ IO.RIS[line, stream]; token _ stream.GetTokenRope[IO.IDProc].token; SELECT TRUE FROM Rope.Equal[token, "--"] => LOOP; Rope.Equal[token, "QB"] => play.commands[0] _ ReadCommand[stream]; Rope.Equal[token, "LE"] => play.commands[1] _ ReadCommand[stream]; Rope.Equal[token, "RE"] => play.commands[2] _ ReadCommand[stream]; Rope.Equal[token, "LG"] => play.commands[3] _ ReadCommand[stream]; Rope.Equal[token, "RG"] => play.commands[4] _ ReadCommand[stream]; Rope.Equal[token, "C"] => play.commands[5] _ ReadCommand[stream]; Rope.Equal[token, "ball"] => play.ball _ ReadCommand[stream, TRUE]; ENDCASE => { IF play # NIL AND play.name # NIL THEN AddPlay[play]; play _ NEW[PlayRec _ []]; play.name _ token; token _ stream.GetTokenRope[IO.IDProc].token; IF Rope.Equal[token, "defensive"] THEN play.side _ defense; token _ stream.GetTokenRope[IO.IDProc].token; IF Rope.Equal[token, "play"] THEN play.setUp _ FALSE}; ENDLOOP; IF play # NIL AND play.name # NIL THEN AddPlay[play]; file.Close[]; MessageWindow.Append["done."]; END; ReadCommand: PROC[stream: IO.STREAM, ball: BOOLEAN_FALSE] RETURNS[Command] = BEGIN token: Rope.ROPE; action: Action _ block; target: Target _ [player[ball]]; WHILE ~stream.EndOf[] DO token _ stream.GetTokenRope[IO.IDProc].token; SELECT TRUE FROM Rope.Equal[token, "stop"] => RETURN[[stop, [null[ ]]]]; Rope.Equal[token, "block"] => action _ block; Rope.Equal[token, "guard"] => action _ block; Rope.Equal[token, "run"] => action _ run; Rope.Equal[token, "pass"] => IF ball THEN action _ pass; Rope.Equal[token, "kick"] => IF ball THEN action _ kick; Rope.Equal[token, "goal"] => target _ [goal[]]; Rope.Equal[token, "kick"] => IF ball THEN action _ kick; Rope.Equal[token, "ball"] => target _ [player[ball]]; Rope.Equal[token, "QB"] => target _ [player[IF myTeam = home THEN VQB ELSE HQB]]; Rope.Equal[token, "LE"] => target _ [player[IF myTeam = home THEN VLE ELSE HLE]]; Rope.Equal[token, "RE"] => target _ [player[IF myTeam = home THEN VRE ELSE HRE]]; Rope.Equal[token, "LG"] => target _ [player[IF myTeam = home THEN VLG ELSE HLG]]; Rope.Equal[token, "RG"] => target _ [player[IF myTeam = home THEN VRG ELSE HRG]]; Rope.Equal[token, "C"] => target _ [player[IF myTeam = home THEN VC ELSE HC]]; Rope.Equal[token, "location"] => {x, y: INTEGER; x _ stream.GetInt[]; y _ stream.GetInt[]; target _ [relative[x, y]]}; ENDCASE; ENDLOOP; RETURN[[action, target]]; END; <<>> <<******************************************************************>> <> <<******************************************************************>> SavePlay: PUBLIC PROC[side: Side, name: Rope.ROPE, setUp: BOOL] = BEGIN new: Play _ NEW[PlayRec _ []]; new^ _ IF setUp THEN current^ ELSE planned^; new.setUp _ setUp; new.side _ side; new.name _ name; AddPlay[new]; END; AddPlay: PROC[new: Play] = BEGIN line: Menus.MenuLine; entry: Menus.MenuEntry; IF new.name = NIL THEN new.name _ "Play"; FOR i: CARDINAL IN [0..6) DO IF new.commands[i].target.type # absolute THEN LOOP; new.commands[i].target _ Translate[new.commands[i].target, myTeam]; ENDLOOP; IF new.ball.target.type = absolute THEN new.ball.target _ Translate[new.ball.target, myTeam]; line _ IF new.setUp THEN 1 ELSE IF new.side = offense THEN 2 ELSE 3; entry _ FindEntry[viewer.menu, new.name, line]; IF entry # NIL THEN {entry.clientData _ new; RETURN}; entry _ Menus.CreateEntry[new.name, SetPlay, new]; Menus.AppendMenuEntry[viewer.menu, entry, line]; ViewerOps.SetMenu[viewer, viewer.menu]; END; FindEntry: PROC[menu: Menus.Menu, name: Rope.ROPE, line: Menus.MenuLine] RETURNS[entry: Menus.MenuEntry] = BEGIN FOR entry _ menu.lines[line], entry.link WHILE entry # NIL DO IF Rope.Equal[entry.name, name] THEN RETURN; ENDLOOP; END; Translate: PROC[target: Target, team: Team] RETURNS[Target] = BEGIN x, y: REAL _ 0; WITH t: target SELECT FROM absolute => { y _ t.y - fieldWidth/2; x _ (t.x - game.scrimmage)*Direction[team, game.quarter]}; ENDCASE; RETURN[[relative[x, y]]]; END; SetPlay: Menus.ClickProc = TRUSTED BEGIN old: Play; new: Play _ NARROW[clientData, Play]; IF new = NIL THEN RETURN; IF control THEN { entry: Menus.MenuEntry; entry _ Menus.FindEntry[viewer.menu, new.name]; IF entry = NIL THEN RETURN; IF entry.clientData # new THEN RETURN; Menus.ReplaceMenuEntry[viewer.menu, entry, NIL]; ViewerOps.SetMenu[viewer, viewer.menu]; RETURN}; InputFocus.SetInputFocus[viewer]; old _ IF new.setUp OR game.state > setUp THEN current ELSE planned; old^ _ new^; IF game.state < option THEN FootballMaster.SetUp[myTeam]; IF old.setUp THEN RETURN; MessageWindow.Clear[]; IF (new.side = defense) AND (myTeam = game.offense) THEN { MessageWindow.Append["You have the ball. Select an offensive play.", TRUE]; MessageWindow.Blink[]}; IF (new.side = offense) AND (myTeam # game.offense) THEN { MessageWindow.Append["You don't have the ball. Select a defensive play.", TRUE]; MessageWindow.Blink[]}; END; <<>> <<******************************************************************>> <> <<******************************************************************>> current: Play; planned: PUBLIC Play; SetCommand: PUBLIC PROC[player: Position, command: Command, setUp: BOOL _ FALSE] = BEGIN old: Play _ IF game.state > setUp OR setUp THEN current ELSE planned; <> IF player = ball AND old = planned THEN {old.ball _ command; RETURN}; <> IF player = ball THEN { player _ game.ballCarrier; IF command.target = [null[]] THEN IF old.ball.action # stop THEN command _ old.ball ELSE RETURN}; <> IF player = ball OR TeamOf[player] # myTeam THEN RETURN; IF command.action IN [pass..kick] AND player # game.ballCarrier THEN RETURN; <> IF command.action = kick THEN FOR i: CARDINAL IN [0..6) DO old.commands[i] _ [run, [player[ball]]]; ENDLOOP; <> old.commands[Index[player]] _ command; END; Index: PROC[player: Position] RETURNS[CARDINAL] = BEGIN IF TeamOf[player] = home THEN RETURN[LOOPHOLE[Add[player, -1]]] ELSE RETURN[LOOPHOLE[Add[player, -7]]]; END; <<>> <<******************************************************************>> <> <<******************************************************************>> carrier: Position _ ball; state: State _ offField; Wait: ENTRY PROCEDURE[i: CARDINAL] = BEGIN DO IF i = 0 THEN EXIT ELSE i _ i - 1; WAIT timer; ENDLOOP; END; Control: PUBLIC PROC = BEGIN ENABLE RPC.CallFailed => CHECKED {CONTINUE}; player: Player; commands: Commands _ ALL[[stop, [null[]]]]; Process.SetTimeout[@timer, 1]; InitializePlays[]; FootballMaster.KickOff[myTeam]; DO Wait[delay]; game^ _ FootballMaster.SetCommands[myTeam, commands]; IF game.state # state THEN SELECT game.state FROM offField => current.commands _ GetOffField[myTeam]; huddle => { current.commands _ GetHuddle[myTeam]; IF game.offense = myTeam THEN planned.commands _ ALL[[block, [player[all]]]] ELSE planned.commands _ ALL[[run, [player[ball]]]]}; option => current^ _ planned^; ENDCASE; FOR i: CARDINAL IN [0..6) DO player _ @game.player[Add[IF myTeam = home THEN HQB ELSE VQB, i]]; [commands[i].action, commands[i].target] _ GetCommand[player, game]; ENDLOOP; state _ game.state; carrier _ game.ballCarrier; IF viewer # NIL THEN ViewerOps.PaintViewer[viewer, client, FALSE, $Players]; ENDLOOP; END; GetCommand: PROC[player: Player, game: Game] RETURNS[action: Action, target: Target] = BEGIN x, y: REAL; OQB: Position; index: CARDINAL; <> OQB _ IF game.offense = home THEN HQB ELSE VQB; IF player.position = OQB AND game.state = option AND game.ballCarrier = none THEN RETURN[stop, [null[]]]; <> IF game.state # state AND game.state IN [pass..run] AND state NOT IN [pass..run] THEN FOR i: Position IN Players DO IF i = game.ballCarrier THEN LOOP; IF TeamOf[game.player[i].position] # game.possessor THEN SetCommand[i, [run, [player[ball]]]] ENDLOOP; IF game.state # state AND game.state = turnover THEN FOR i: Position IN Players DO IF i = game.ballCarrier THEN LOOP; IF TeamOf[game.player[i].position] = game.possessor THEN SetCommand[i, [block, [player[all]]]] ELSE SetCommand[i, [run, [player[ball]]]]; ENDLOOP; IF game.state # state AND game.state = liveBall THEN FOR i: Position IN Players DO SetCommand[i, [run, [player[ball]]]] ENDLOOP; <> IF game.ballCarrier # carrier AND player.position = game.ballCarrier AND game.state # option THEN SetCommand[player.position, [run, [goal[]]]]; index _ Index[player.position]; action _ current.commands[index].action; target _ current.commands[index].target; IF selected # none AND player.position = selected THEN RETURN; <> WITH t: target SELECT FROM relative => { y _ t.y + fieldWidth/2; x _ game.scrimmage + t.x*Direction[TeamOf[player.position], game.quarter]}; absolute => {x _ t.x; y _ t.y}; ENDCASE => RETURN; <> IF game.state < option AND myTeam # game.offense AND current.commands[Index[player.position]].action # stop THEN WITH t: planned.commands[index].target SELECT FROM player => { dy: REAL; p2: Player = @game.player[t.position]; IF ~CenterReady[] OR t.position = ball THEN RETURN; IF ABS[player.x - game.scrimmage] > 3.5 THEN RETURN; dy _ ABS[player.y - p2.y]; IF dy < 3.5 AND ABS[player.dy] < .05 THEN RETURN; -- don't move IF dy < 10 AND p2.dy > .5 AND p2.y < player.y THEN RETURN; -- moving toward me IF dy < 10 AND p2.dy < -.5 AND p2.y > player.y THEN RETURN; -- moving toward me IF Blocked[player, p2.y] THEN IF ABS[player.x - game.scrimmage] < 2.9 THEN target _ [relative[-3, player.y - fieldWidth/2]] ELSE target _ [relative[-3, p2.y - fieldWidth/2]] ELSE target _ [relative[-1, p2.y - fieldWidth/2]]; current.commands[Index[player.position]] _ [run, target]}; ENDCASE; IF action # run OR game.state < option THEN RETURN; <> IF ABS[player.x - x] < .5 AND ABS[player.y - y] < .5 THEN SELECT TRUE FROM player.position = game.ballCarrier AND player.position = OQB => NULL; player.position = game.ballCarrier => SetCommand[player.position, [run, [goal[]]]]; TeamOf[player.position] # game.possessor => SetCommand[player.position, [run, [player[ball]]]]; game.state = run => SetCommand[player.position, [block, [player[ball]]]]; game.state = pass => SetCommand[player.position, [run, [player[ball]]]]; ENDCASE; RETURN[action, [absolute[x, y]]]; END; Blocked: PROC[player: Player, p2y: REAL] RETURNS[BOOLEAN] = INLINE BEGIN FOR i: Position IN Players DO IF i = player.position THEN LOOP; IF TeamOf[i] # TeamOf[player.position] THEN LOOP; IF ABS[game.player[i].x - game.scrimmage] > 1.5 THEN LOOP; IF player.y+1 < game.player[i].y AND p2y+1 < game.player[i].y THEN LOOP; IF player.y > game.player[i].y+1 AND p2y > game.player[i].y+1 THEN LOOP; RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; CenterReady: PROC RETURNS[BOOLEAN] = INLINE BEGIN center: Position = IF game.offense = home THEN HC ELSE VC; RETURN[ABS[game.player[center].x - game.scrimmage] < 2]; END; Convert: PROC[pos: Position] RETURNS[Position] = INLINE BEGIN IF pos IN HomeTeam THEN RETURN[Add[pos, 6]]; IF pos IN VisitorsTeam THEN RETURN[Add[pos, -6]]; RETURN[pos]; END; Add: PROCEDURE[p: Position, i: INTEGER] RETURNS[Position] = INLINE {RETURN[LOOPHOLE[LOOPHOLE[p, INTEGER]+i]]}; <<>> <<******************************************************************>> <> <<******************************************************************>> OHuddle: Commands = [ [run, [relative[-18, 0]]], [run, [relative[-16, -4]]], [run, [relative[-16, 4]]], [run, [relative[-14, -4]]], [run, [relative[-14, 4]]], [run, [relative[-12, 0]]]]; DHuddle: Commands = [ [run, [relative[-18, 0]]], [run, [relative[-16, -4]]], [run, [relative[-16, 4]]], [run, [relative[-14, -4]]], [run, [relative[-14, 4]]], [run, [relative[-14, 0]]]]; LOEndZoneHuddle: Commands = [ [run, [absolute[-8, 26]]], [run, [absolute[-6, 22]]], [run, [absolute[-6, 30]]], [run, [absolute[-4, 22]]], [run, [absolute[-4, 30]]], [run, [absolute[-2, 26]]]]; LDEndZoneHuddle: Commands = [ [run, [absolute[-8, 26]]], [run, [absolute[-6, 22]]], [run, [absolute[-6, 30]]], [run, [absolute[-4, 22]]], [run, [absolute[-4, 30]]], [run, [absolute[-4, 26]]]]; ROEndZoneHuddle: Commands = [ [run, [absolute[108, 26]]], [run, [absolute[106, 22]]], [run, [absolute[106, 30]]], [run, [absolute[104, 22]]], [run, [absolute[104, 30]]], [run, [absolute[102, 26]]]]; RDEndZoneHuddle: Commands = [ [run, [absolute[108, 26]]], [run, [absolute[106, 22]]], [run, [absolute[106, 30]]], [run, [absolute[104, 22]]], [run, [absolute[104, 30]]], [run, [absolute[104, 26]]]]; offField1: Commands = [ [run, [absolute[23, 55]]], [run, [absolute[26, 55]]], [run, [absolute[29, 55]]], [run, [absolute[32, 55]]], [run, [absolute[35, 55]]], [run, [absolute[38, 55]]]]; offField2: Commands = [ [run, [absolute[77, -5]]], [run, [absolute[74, -5]]], [run, [absolute[71, -5]]], [run, [absolute[68, -5]]], [run, [absolute[65, -5]]], [run, [absolute[62, -5]]]]; GetHuddle: PROC[team: Team] RETURNS[commands: Commands] = BEGIN SELECT TRUE FROM Direction[team, game.quarter] = 1 AND game.scrimmage < 10 => RETURN[IF team = game.offense THEN LOEndZoneHuddle ELSE LDEndZoneHuddle]; Direction[team, game.quarter] =-1 AND game.scrimmage > 90 => RETURN[IF team = game.offense THEN ROEndZoneHuddle ELSE RDEndZoneHuddle]; ENDCASE => RETURN[IF team = game.offense THEN OHuddle ELSE DHuddle]; END; GetOffField: PROC[team: Team] RETURNS[Commands] = INLINE {RETURN[IF team = home THEN offField1 ELSE offField2]}; InitializePlays: PROC = BEGIN current.commands _ [ [run, [relative[-3, 0]]], [run, [relative[-1, -8]]], [run, [relative[-1, 8]]], [run, [relative[-1, -4]]], [run, [relative[-1, 4]]], [run, [relative[-1, 0]]]]; SavePlay[offense, "5-1", TRUE]; current.commands _ [ [run, [relative[-1, -2]]], [run, [relative[-6, -2]]], [run, [relative[-6, 2]]], [run, [relative[-1, -6]]], [run, [relative[-1, 6]]], [run, [relative[-1, 2]]]]; SavePlay[offense, "4-2", TRUE]; current.commands _ [ [run, [relative[-45, 0]]], [run, [relative[-1, -8]]], [run, [relative[-1, 8]]], [run, [relative[-1, -4]]], [run, [relative[-1, 4]]], [run, [relative[-1, 0]]]]; SavePlay[offense, "punt-reception", TRUE]; <> planned.commands _ [ [run, [relative[5, -4]]], [block, [player[all]]], [block, [player[all]]], [block, [player[all]]], [block, [player[all]]], [block, [player[all]]]]; SavePlay[offense, "UpTheMiddle", FALSE]; planned.commands _ [ [run, [relative[-10, 3]]], [block, [player[all]]], [run, [relative[10, 10]]], [block, [player[all]]], [block, [player[all]]], [block, [player[all]]]]; planned.ball _ [pass, [relative[10, 10]]]; SavePlay[offense, "ShortPassTop", FALSE]; <> planned.commands _ [ [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]; SavePlay[defense, "Rush", FALSE]; IF myTeam = home THEN planned.commands _ [ [run, [player[ball]]], [run, [player[VLE]]], [run, [player[VRE]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]; IF myTeam = visitors THEN planned.commands _ [ [run, [player[ball]]], [run, [player[HLE]]], [run, [player[HRE]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]; SavePlay[defense, "GuardEnds", FALSE]; IF myTeam = home THEN planned.commands _ [ [run, [player[VQB]]], [run, [player[VLE]]], [run, [player[VRE]]], [run, [player[VLG]]], [run, [player[VRG]]], [run, [player[VC]]]]; IF myTeam = visitors THEN planned.commands _ [ [run, [player[HQB]]], [run, [player[HLE]]], [run, [player[HRE]]], [run, [player[HLG]]], [run, [player[HRG]]], [run, [player[HC]]]]; SavePlay[defense, "ManToMan", FALSE]; planned.commands _ [ [run, [player[ball]]], [block, [relative[-10, -15]]], [block, [relative[-10, 15]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]; SavePlay[defense, "Zone", FALSE]; planned.commands _ [ [stop, [null[]]], [block, [player[ball]]], [block, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]; SavePlay[defense, "punt-reception", FALSE]; current.commands _ GetOffField[myTeam]; END; Init[]; END . . .