<> <> <> DIRECTORY BasicTime, FastBreak, Graphics USING [black, Context, DrawBox, NewContext, SetColor, SetCP, SetPaintMode, white], GraphicsOps USING [BitmapRef, BitmapRep, Context, DrawBitmap, NewBitmap, NewContextFromBitmap], Icons USING [DrawIcon], PrincOps USING [BytePC, ControlLink, CSegPrefix, GlobalFrameHandle, NullLink, StateVector], PrincOpsUtils USING [GetReturnLink, GlobalFrame], Process USING [GetPriority, Priority, priorityClient3, SetPriority], Rope USING [ROPE], Terminal USING [Current, WaitForBWVerticalRetrace], ViewerClasses USING [Viewer], ViewerOps USING [DisablePainting, EnablePainting, FindViewer, GreyScreen, OpenIcon, PaintViewer, WaitForPaintingToFinish]; LutherWatcherSpecialCache: MONITOR -- Russ's SpecialCache, cloned. IMPORTS BasicTime, FastBreak, Graphics, GraphicsOps, Icons, PrincOpsUtils, Process, Terminal, ViewerOps SHARES ViewerOps = BEGIN OPEN Rope; MapSeq: TYPE = RECORD [SEQUENCE len: NAT OF Map]; Map: TYPE = RECORD [ x0, y0: CARDINAL _ 0, xpos,ypos: INTEGER _ 0, xvel,yvel: INTEGER _ 0]; CARD: TYPE = LONG CARDINAL; base: GraphicsOps.BitmapRef _ NIL; blinks: INTEGER _ 4; ctx: Graphics.Context _ NIL; enabled: BOOL _ TRUE; hack: REF GraphicsOps.BitmapRep _ NIL; iconCtx: Graphics.Context _ NIL; iconSize: INTEGER = 64; invG: INTEGER _ 2; -- number of steps between gravity increment lockup: BOOL _ TRUE; -- TRUE for production, FALSE for debug maps: REF MapSeq _ NEW[MapSeq[64]]; maxTime: INT _ 32; -- don't hog the world for more than about this many seconds minYvel: INTEGER _ 3; pieces: BOOL _ TRUE; pieceSize: NAT _ 24; scale: INTEGER _ 1; scanLines: INTEGER _ 0; seedMod: INTEGER _ 64; swapPiecesFlag: BOOL _ FALSE; setList: LocalData _ NIL; initialSteps: INTEGER = 80; bouncingBottom: BOOL _ TRUE; bouncingTop: BOOL _ TRUE; bouncingSides: BOOL _ TRUE; absorbing: NAT _ 7; LocalData: TYPE = REF LocalDataRep; LocalDataRep: TYPE = RECORD [ next: LocalData _ NIL, id: FastBreak.FastBreakId _ NIL, viewer: ViewerClasses.Viewer _ NIL, oneShot: BOOL _ TRUE]; FrameType: TYPE = RECORD [w0,w1,w2,w3: CARDINAL, viewer: ViewerClasses.Viewer]; HandleCacheChange: FastBreak.FastBreakProc = TRUSTED { <<[data: FastBreakData, frame: PrincOps.FrameHandle, sv: PrincOps.SVPointer]>> <> mine: LocalData _ LOOPHOLE[data]; ptr: POINTER TO FrameType _ LOOPHOLE[frame]; current: BasicTime.Pulses _ BasicTime.GetClockPulses[]; viewer: ViewerClasses.Viewer _ ptr.viewer; IF viewer = NIL OR NOT viewer.iconic THEN RETURN; IF mine # NIL AND enabled AND mine.id # NIL AND (mine.viewer = NIL OR viewer = mine.viewer) THEN { IF mine.oneShot THEN { [] _ FastBreak.ClearFastBreak[mine.id, HandleCacheChange, data]; mine.id _ NIL; }; DoIt[viewer]; }; }; ProcToCodeAndPC: PROC [proc: UNSPECIFIED] RETURNS [code: LONG POINTER _ NIL, pc: PrincOps.BytePC] = { gfh: PrincOps.GlobalFrameHandle _ LOOPHOLE[PrincOpsUtils.GlobalFrame[proc]]; pc _ [0]; IF gfh # NIL THEN { ep: CARDINAL _ LOOPHOLE[proc, PrincOps.ControlLink].ep; ev: LONG POINTER TO PrincOps.CSegPrefix _ LOOPHOLE[gfh.code.longbase]; pc _ [ev.entry[ep].initialpc*2]; code _ ev; }; }; FindAny: PROC [oneShot: BOOL _ FALSE] = { ENABLE ANY => GO TO dont; proc: UNSPECIFIED _ ViewerOps.OpenIcon; code: LONG POINTER; pc: PrincOps.BytePC; data: LocalData _ NIL; Setup[]; [code, pc] _ ProcToCodeAndPC[proc]; pc _ [pc+5]; -- crock! to get at frame after arguments data _ NEW[LocalDataRep _ [ next: NIL, id: NIL, viewer: NIL, oneShot: oneShot]]; data.id _ FastBreak.SetFastBreak[code, pc, HandleCacheChange, LOOPHOLE[data]]; data.next _ setList; setList _ data; EXITS dont => {}; }; FindIt: PROC [name: ROPE, oneShot: BOOL _ FALSE] = { ENABLE ANY => GO TO dont; viewer: ViewerClasses.Viewer _ ViewerOps.FindViewer[name]; proc: UNSPECIFIED _ ViewerOps.OpenIcon; code: LONG POINTER; pc: PrincOps.BytePC; data: LocalData _ NIL; Setup[]; IF viewer = NIL OR NOT viewer.iconic THEN RETURN; [code, pc] _ ProcToCodeAndPC[proc]; pc _ [pc+5]; -- crock! to get at frame after arguments data _ NEW[LocalDataRep _ [ next: NIL, id: NIL, viewer: viewer, oneShot: oneShot]]; data.id _ FastBreak.SetFastBreak[code, pc, HandleCacheChange, LOOPHOLE[data]]; data.next _ setList; setList _ data; EXITS dont => {}; }; NameIt: PROC [name: ROPE] = { viewer: ViewerClasses.Viewer _ ViewerOps.FindViewer[name]; IF viewer = NIL OR NOT viewer.iconic THEN RETURN; DoIt[viewer]; ViewerOps.PaintViewer[viewer, all]; }; DoIt: PROC [viewer: ViewerClasses.Viewer] = { x: INTEGER _ viewer.wx; midWay: INTEGER _ Terminal.Current[].bwWidth/2; vx: INTEGER _ (midWay - x)/initialSteps; y: INTEGER _ viewer.wy; oldPriority: Process.Priority _ Process.GetPriority[]; nextG: INTEGER _ invG; useIcon: BOOL _ viewer.icon < private; startTime: BasicTime.GMT _ BasicTime.Now[]; last: BOOL _ FALSE; maxX: INTEGER = Terminal.Current[].bwWidth - (IF pieces THEN pieceSize ELSE iconSize); minY: INTEGER = IF pieces THEN pieceSize/2 ELSE iconSize/2; maxY: INTEGER = Terminal.Current[].bwHeight; lastTime: INT _ maxTime; needErase: BOOL _ FALSE; IF NOT viewer.iconic THEN RETURN; <> Setup[]; IF useIcon THEN Icons.DrawIcon[viewer.icon, iconCtx, 0, 0, viewer.name] ELSE viewer.class.paint[viewer, iconCtx, NIL, FALSE]; Process.SetPriority[Process.priorityClient3]; ViewerOps.WaitForPaintingToFinish[]; ViewerOps.GreyScreen[x, y, iconSize, iconSize, FALSE, TRUE]; -- wipe out the old icon IF lockup THEN { ViewerOps.DisablePainting[]; ViewerOps.WaitForPaintingToFinish[]}; <> SELECT vx FROM < 0 => vx _ vx - 1; > 0 => vx _ vx + 1; ENDCASE; DO oldX: INTEGER _ x; oldY: INTEGER _ y; last: BOOL _ FALSE; <<>> <> x _ x + vx; y _ y + 2; SELECT vx FROM > 0 => IF x > midWay THEN x _ midWay; < 0 => IF x < midWay THEN x _ midWay; ENDCASE; IF x = midWay OR y > 500 THEN last _ TRUE ELSE { <> Graphics.SetCP[ctx, x, y+iconSize]; GraphicsOps.DrawBitmap[ctx, base, iconSize, iconSize, 0, 0]; }; IF needErase THEN { <> Graphics.SetCP[ctx, oldX, oldY+iconSize]; GraphicsOps.DrawBitmap[ctx, base, iconSize, iconSize, 0, 0]; }; needErase _ TRUE; Terminal.WaitForBWVerticalRetrace[Terminal.Current[]]; IF last THEN EXIT; ENDLOOP; <<>> <> FOR i: NAT IN [0..maps.len) DO map: Map _ maps[i]; map.xpos _ map.xpos + x; map.ypos _ map.ypos + y + iconSize; maps[i] _ map; ENDLOOP; <> FOR i: NAT IN [0..4800) WHILE NOT last DO current: BasicTime.GMT _ BasicTime.Now[]; delta: INT _ BasicTime.Period[from: startTime, to: current]; moving: NAT _ maps.len; above: NAT _ moving; last _ delta > lastTime; <> FOR j: NAT IN [0..moving) DO map: Map _ maps[j]; oldX: INTEGER _ map.xpos; oldY: INTEGER _ map.ypos; <> IF nextG = i THEN <> map.yvel _ map.yvel - 1; map.xpos _ oldX + map.xvel; map.ypos _ oldY + map.yvel; IF bouncingSides THEN SELECT map.xpos FROM <= 0 => {map.xvel _ -(map.xvel*3)/absorbing; map.xpos _ 0}; >= maxX => {map.xvel _ -(map.xvel*3)/absorbing; map.xpos _ maxX}; ENDCASE; IF bouncingBottom THEN IF map.ypos <= minY THEN { map.yvel _ -(map.yvel*3)/absorbing; map.ypos _ minY; IF map.yvel = 0 AND map.ypos = minY AND (i MOD 16) = 0 THEN <> SELECT map.xvel FROM < 0 => map.xvel _ map.xvel + 1; > 0 => map.xvel _ map.xvel - 1; ENDCASE; IF map.yvel = 0 AND map.xvel = 0 THEN moving _ moving - 1; }; IF bouncingTop THEN IF map.ypos >= maxY THEN { map.yvel _ -(map.yvel*3)/absorbing; map.ypos _ maxY}; IF map.ypos < minY THEN above _ above - 1; maps[j] _ map; <> IF NOT last THEN { Graphics.SetCP[ctx, map.xpos, map.ypos]; IF pieces THEN GraphicsOps.DrawBitmap[ ctx, base, pieceSize, pieceSize, map.x0, map.y0, map.x0, map.y0] ELSE GraphicsOps.DrawBitmap[ctx, base, iconSize, iconSize, 0, 0]}; IF i # 0 THEN { <> Graphics.SetCP[ctx, oldX, oldY]; IF pieces THEN GraphicsOps.DrawBitmap[ ctx, base, pieceSize, pieceSize, map.x0, map.y0, map.x0, map.y0] ELSE GraphicsOps.DrawBitmap[ctx, base, iconSize, iconSize, 0, 0]}; ENDLOOP; IF nextG = i THEN nextG _ nextG + invG; THROUGH [0..scanLines) DO Terminal.WaitForBWVerticalRetrace[Terminal.Current[]]; ENDLOOP; IF moving = 0 OR above = 0 THEN lastTime _ 0; ENDLOOP; <> IF swapPiecesFlag THEN pieces _ NOT pieces; IF lockup THEN ViewerOps.EnablePainting[]; Process.SetPriority[oldPriority]; ViewerOps.WaitForPaintingToFinish[]; }; FlushCache: PROC = { <> v: RECORD [a,b: UNSPECIFIED, state: PrincOps.StateVector]; v.state _ STATE; v.state.dest _ PrincOpsUtils.GetReturnLink[]; v.state.source _ PrincOps.NullLink; v.state.stkptr _ 0; RETURN WITH v.state; }; Setup: ENTRY PROC = { ENABLE UNWIND => NULL; IF ctx = NIL THEN { ctx _ Graphics.NewContext[]; Graphics.SetColor[ctx, Graphics.black]; [] _ Graphics.SetPaintMode[ctx, invert]}; IF base = NIL THEN { base _ GraphicsOps.NewBitmap[iconSize, iconSize]; hack _ LOOPHOLE[base]}; iconCtx _ GraphicsOps.NewContextFromBitmap[base]; [] _ Graphics.SetPaintMode[iconCtx, opaque]; Graphics.SetColor[iconCtx, Graphics.white]; Graphics.DrawBox[iconCtx, [0, 0, 64, 64]]; Graphics.SetColor[iconCtx, Graphics.black]; FOR i: NAT IN [0..maps.len) DO row: INTEGER _ i / 8; col: INTEGER _ i MOD 8; sum: INTEGER _ row + col; xvel: INTEGER _ (seedMod/2 - GenRandom[]) * scale; yvel: INTEGER _ (GenRandom[] + minYvel) * scale; x0: INTEGER _ col * 8; y0: INTEGER _ row * 8; maps[i] _ [x0: x0, y0: y0, xpos: x0, ypos: y0, xvel: xvel, yvel: yvel]; ENDLOOP; }; seed: CARDINAL _ 123456B; GenRandom: PROC RETURNS [INTEGER] = { <> seed _ seed * 17 + 174653B; RETURN [(seed / 256) MOD seedMod]; }; END.