commit c064619e4d7b3c71a820d36716cc041d779732c5 Author: pete gamache Date: Sat Oct 15 10:52:32 2016 -0400 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ca0973 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store + diff --git a/Duane Blehm's Code.zip b/Duane Blehm's Code.zip new file mode 100644 index 0000000..2de5814 Binary files /dev/null and b/Duane Blehm's Code.zip differ diff --git a/Duane Blehm's Code/Animation Æ’/About Animation.txt b/Duane Blehm's Code/Animation Æ’/About Animation.txt new file mode 100755 index 0000000..e10bbbd --- /dev/null +++ b/Duane Blehm's Code/Animation Æ’/About Animation.txt @@ -0,0 +1,24 @@ +About the Animation Demo files: +{note: TML is now shipping version 2.5 of their Pascal, believe it is +free to version 2.0 owners. Fixes some stuff for the new System. We +didn't have any trouble with 2.0 until we moved up to System 4.1. The +package includes 'Edit' version 2.1. 'Edit' is also available from APDA..see +the MoreInfo.sit files. +**note: Watch out for the new print stuff and systems before 4.1!} + +'Animation Steps' is a 'FullPaint' document that illustrates the steps +in getting graphics from a Paint program to animation on the screen... +document can be opened from most Paint type programs. + +'Animation.Pas' is an 'Edit' text file containing pascal source +code to be compiled by the Pascal compiler. + +'Anim.R' is an 'Edit' text file containing RMaker source code to +be compiled by RMaker. + +'Anim/Rsrc' is a resource document containing resources built with +ResEdit, the resource editor. These resources are 'included' when +Anim.R is compiled by RMaker. + +'Animation' is our application... the result of Link. + diff --git a/Duane Blehm's Code/Animation Æ’/Anim.R b/Duane Blehm's Code/Animation Æ’/Anim.R new file mode 100755 index 0000000..abf2d0c --- /dev/null +++ b/Duane Blehm's Code/Animation Æ’/Anim.R @@ -0,0 +1,16 @@ +* Anim.R + +Anim.rsrc + +TYPE MENU + ,1 +\14 + + ,256 +File + OffScreen.. + Quit + +* will include Pict resource file built with ResEdit and ScrapBook +INCLUDE Anim/rsrc + diff --git a/Duane Blehm's Code/Animation Æ’/Anim:rsrc b/Duane Blehm's Code/Animation Æ’/Anim:rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Animation Æ’/Animation b/Duane Blehm's Code/Animation Æ’/Animation new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Animation Æ’/Animation Steps b/Duane Blehm's Code/Animation Æ’/Animation Steps new file mode 100755 index 0000000..6e1fce3 Binary files /dev/null and b/Duane Blehm's Code/Animation Æ’/Animation Steps differ diff --git a/Duane Blehm's Code/Animation Æ’/Animation.Pas b/Duane Blehm's Code/Animation Æ’/Animation.Pas new file mode 100755 index 0000000..e695a01 --- /dev/null +++ b/Duane Blehm's Code/Animation Æ’/Animation.Pas @@ -0,0 +1,534 @@ +Program Anim; +{this is a simple animation program.. in CreatePictures we load several +pictures. These pictures can be created in any Paint or Drawing type +program.. then copied to Scrapbook DA. Using ResEdit, the resource editor, +these Pictures can be copied from the Scrapbook into 'PICT' type resources. +Note that in our Anim.R source file we have an INCLUDE Anim.rsrc... when +RMaker compiles the Anim.R file it 'includes' any resources in the file +Anim.rsrc. That's how our Paint pictures get into the SimpleAnimation +resources. + +Once we have them loaded into our various picHandles we then +CreateOffScreenBitMap so we have a place to DrawPicture() them. The +OffScreen bitmap is a section of memory just like the memory being +displayed on the screen... we just can't see it! at least not until we +CopyBits() some of it over to the visible screen Bitmap. We 'Draw' into +the OffScreen by setting our window^.portbits to our OffScreen bitmap.. +watch out for 'clipping' if your OffScreen is bigger than your window. +Now for the fun! We have everything we need for the CopyBits() ROM call. +We have two Rectangles... one (sRect) that frames the shape to be copied +from OffScreen, the other (dRect) frames the location in our window where +we want the shape to be drawn. If dRect is not same size as sRect then +CopyBits will scale the shape to fit. This program uses the srcCopy mode +to 'erase' any previous stuff in the dRect... by using a 3 pixel white +border on the left side of our wagon we can offset it by 3 pixels per +animation loop and still 'erase' the previous wagon just by drawing over it. +Internal Animation (wheels turning, horse running,etc.) is achieved by +alternately drawing 3 different shapes in sequence as the dRect moves across +the screen... this is easily done by having an array of offscreen sRect's +and an index to step thru the array...} + +{note: to move a given rectangle 'myRect' from its present location +(Current) to another location (Destination) the following can be used... + +OffsetRect(myRect,Destination.h - Current.h,Destination.v - Current.v); + +or... + +OffsetRect(myRect,DestRect.left - myRect.left,DestRect.top - myRect.top);} + +USES MacIntf; + +{$L Anim.rsrc}{link resources...just our menu stuff} + +CONST + lastMenu = 2; {Number of Menus} + appleMenu = 1; + fileMenu = 256; + + WagonOne = 128;{Three wagon shapes loaded from resource file} + WagonTwo = 129; + WagonThree = 130; + {Resource Picts pasted from Scrapbook into Resource file using ResEdit} + +VAR {global program stuff} + myDialog: DialogPtr; + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu, + theItem: integer; + Finished: Boolean;{used to terminate the program} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; + Screen,DragArea, + GrowArea: Rect; + + HomeTown: PicHandle;{handle for our Logo pic} + HomeRect: Rect;{destination rect for our Logo} + Wagon: Array[1..3] of PicHandle;{we got three wagon pics} + WagonRect: Rect;{size of one wagon, onScreen destination for Copybits} + OffWagonRect: Array[1..3] of Rect;{wagon shape rects in offScreen} + WagonNdx: integer;{which of 3 wagon shapes is to be drawn} + + {here's all the fly/regions stuff} + Fly: array[1..2] of PicHandle;{for loading 2 fly pictures} + FlyRect: Rect;{destination rect for drawing fly} + OffFly: array[1..2] of Rect;{source rects in offscreen} + FlyNdx: integer;{which offFly to draw} + Flower: PicHandle;{load picture from resource} + FlowerRect: Rect;{for locating the flower} + FlowerRgn: RgnHandle;{For 'clipping' fly and flower} + FlyLimits: Rect;{for fly border} + FlightRect: Rect;{For limiting fly flight} + CursorIsOn: Boolean; + MouseRect: Rect;{size of FlightRect, slides with mouse to smooth movement} + + OffScreen,OldBits: BitMap; {for drawing into offscreen} + SizeOfOff: Size; {Size offscreen bitmap} + OffRowBytes: Integer; + +{----------------------------------------------} +procedure CreatePictures; {get 3 Wagon PICT's from resource file} +var + i:integer; +Begin + HomeTown := GetPicture(131);{HomeTown logo} + HomeRect := HomeTown^^.picFrame;{size dest.Rest for drawing pic} + + {we'll draw logo into upper right corner of window so relocate} + OffSetRect(HomeRect,myWindow^.portRect.right - 20 - + HomeRect.right,20 - HomeRect.top); + + Wagon[1] := GetPicture(WagonOne); {load Wagon shapes from resources} + Wagon[2] := GetPicture(WagonTwo); + Wagon[3] := GetPicture(WagonThree); + WagonRect := Wagon[1]^^.picFrame; { i-159, size our WagonRect } + {Size Rects for OffScreen shapes...will locate them later} + For i := 1 to 3 do OffWagonRect[i] := WagonRect; + + {load flystuff to demonstrate regions} + Fly[1] := GetPicture(132); + Fly[2] := GetPicture(133); + {size the fly rectangles} + For i := 1 to 2 do OffFly[i] := Fly[i]^^.picFrame;{they're both same size} + FlyRect := OffFly[1]; + + Flower := GetPicture(134); + FlowerRect := Flower^^.picFrame; + + {load the region resource and coerce it to RgnHandle} + FlowerRgn := RgnHandle(GetResource('RGN ',128));{flower region... outline} + {note this region created with our 'Regions' utility} +end; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +const + OffLeft = 0; + OffTop = 0; + OffRight = 115; + OffBottom = 95; {size bitmap to contain three wagon Picts} +var + bRect: Rect; +Begin + {find size/rows/bounds of bitimage} + SetRect(bRect,Offleft,OffTop,OffRight,OffBottom); { drawing area } + with bRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(bRect,-left,-top); {local coordinates...0,0 = topleft } + end; { of with } + + with OffScreen do begin; { create new BitMap record } + {create new bitimage and make baseAddr point to it} + baseAddr := QDPtr(NewPtr(SizeOfOff)); + rowbytes := OffRowBytes;{number of bytes/row,can extend beyond bounds} + bounds := bRect;{limits of bitimage drawing?} + end; { of with OffScreen } +End; + +procedure DrawPicsIntoOffScreen; +var + i: integer; +Begin + OldBits := myWindow^.portBits; {preserve old myWindow BitMap} + SetPortBits(OffScreen); {our new myWindow BitMap } + + {note: If offscreen bitmap is bigger than myWindow bitmap watchout for + clipping caused by ClipRgn and VisRgn fields of grafport record, you + can set cliprgn with ClipRect procedure and use CopyRgn procedure + to store old visrgn in temporary rgn... etc.} + + FillRect(myWindow^.PortRect,white); {erase our new BitMap to white} + + {locate wagon shape Rectangles in OffScreen Bitmap} + OffSetRect(OffWagonRect[1],-OffWagonRect[1].left, + -OffWagonRect[1].top);{topleft corner..0,0} + OffSetRect(OffWagonRect[2],-OffWagonRect[2].left, + OffWagonRect[1].bottom - OffWagonRect[2].top);{below 1} + OffSetRect(OffWagonRect[3],-OffWagonRect[3].left, + OffWagonRect[2].bottom - OffWagonRect[3].top);{below 2} + + {draw the wagons into the offscreen bitmap} + For i := 1 to 3 do DrawPicture(Wagon[i],OffWagonRect[i]); + + {locate the flys in the offscreen bitmap} + OffSetRect(OffFly[1],OffWagonRect[1].right - OffFly[1].left, + -OffFly[1].top); {right of wagons, at top} + OffSetRect(OffFly[2],OffWagonRect[1].right - OffFly[2].left, + OffFly[1].bottom-OffFly[2].top); {right of wagons, below fly[1]} + + {draw the flys into offscreen} + For i := 1 to 2 do DrawPicture(Fly[i],OffFly[i]); + + {could do a 'ReleaseResource' here if you're done with the Pictures} + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure DrawWindowContents(WhichWindow:WindowPtr);{response to Update event} +var + trect:Rect; + i:integer; +Begin + DrawPicture(HomeTown,HomeRect);{draw our logo} + + {draw groundline under wagon} + MoveTo(myWindow^.portRect.left,WagonRect.bottom); + LineTo(myWindow^.portRect.right,WagonRect.bottom); + + {copy offScreen Wagons & flys into Window..upperleft corner,as in bitmap} + CopyBits(OffScreen,myWindow^.portBits,OffScreen.bounds, + OffScreen.bounds,srcCopy,nil); + + {draw current wagon Shape being animated into WagonRect} + CopyBits(OffScreen,myWindow^.portBits,OffWagonRect[WagonNdx], + WagonRect,srcCopy,nil); + + {all the fly stuff} + DrawPicture(Flower,FlowerRect); + CopyBits(OffScreen,myWindow^.portBits,OffFly[FlyNdx], + FlyRect,srcCopy,FlowerRgn); + FrameRoundRect(FlyLimits,48,32);{border around the fly area} +End; + +Procedure InitialAnimation;{locate everything to begin animation} +var + tRgn:RgnHandle; +Begin + {locate first WagonRect,left side of window on groundline (250)} + OffSetRect(WagonRect,-WagonRect.left,250-WagonRect.bottom); + WagonNdx := 1;{set to first Wagon shape} + + {locate the flower} + OffSetRect(FlowerRect,160-FlowerRect.left,90-FlowerRect.top); + + {locate the FlowerRgn in sync with Flower} + OffSetRgn(FlowerRgn,FlowerRect.left-FlowerRgn^^.rgnBBox.left, + FlowerRect.top-FlowerRgn^^.rgnBBox.top); + + {size the FlyLimits} + FlyLimits := FlowerRect; + InsetRect(FlyLimits,-18,0);{expand left/right for border} + FlyLimits.top := FlyLimits.top - 18;{also top.. leave bottom for stem} + + {create the region to exclude drawing in the flower} + tRgn := NewRgn; + OpenRgn; + FrameRoundRect(FlyLimits,48,32); + CloseRgn(tRgn);{region of limits rectangle} + + DiffRgn(tRgn,FlowerRgn,FlowerRgn);{take out flower,put result in FlowerRgn} + + {expand limits by 1 so we can have a frame border} + InSetRect(FlyLimits,-1,-1); + + FlightRect := FlyLimits;{FlightRect will compensate for using fly.topleft} + InSetRect(FlightRect,-20,-20);{expand it so fly will be outside border} + OffSetRect(FlightRect,-20,-20);{compensate for using flyRect.topleft} + MouseRect := FlightRect;{MouseRect moves with cursor,& map into FlightRect} + + {locate fly in upperleft of FlightRect} + OffSetRect(FlyRect,FlightRect.left-FlyRect.left, + FlightRect.top-FlyRect.top); + FlyNdx := 1;{set to first Fly shape} +end; + +procedure AnimateWagon; +var tPoint:Point; + tRect:Rect; +Begin + {locate next WagonRect for wagon shape} + If WagonRect.left > myWindow^.portRect.right then + OffSetRect(WagonRect,-WagonRect.right,0) {back to left side} + else OffSetRect(WagonRect,1,0); {move one pixel to right} + + {draw Current WagonNdx shape into WagonRect} + CopyBits(OffScreen,myWindow^.portBits,OffWagonRect[WagonNdx], + WagonRect,srcCopy,nil); + + {next wagon shape to be drawn} + If WagonNdx < 3 then inc(WagonNdx) {next shape} + else WagonNdx := 1; {back to first shape} + + {now animate the fly} + GetMouse(tPoint);{get current mouse coordinates} + + {hide cursor if its over the fly area} + If PtInRect(tPoint,FlyLimits) then begin + If CursorIsOn then begin {hide cursor if its on} + HideCursor; + CursorIsOn := False; + end; + end + else If not(CursorIsOn) then begin {show cursor if its off} + ShowCursor; + CursorIsOn := True; + end; + + {limit fly image (FlyRect) to FlightRect extremes..} + {to keep fly from wondering off visible area} + {mouseRect moves with the cursor and tPoint is mapped into FlightRect} + + If not(PtInRect(tPoint,MouseRect)) then begin + {a coordinate is outside mouseRect so slide mouseRect to tPoint} + If tPoint.h < MouseRect.left then begin {slide MouseRect to left} + MouseRect.right := MouseRect.right - + (MouseRect.left - tPoint.h); + MouseRect.left := tPoint.h; + end + else If tPoint.h > MouseRect.right then begin + MouseRect.left := MouseRect.left + tPoint.h - MouseRect.right; + MouseRect.right := tPoint.h; + end; + If tPoint.v < MouseRect.top then begin + MouseRect.bottom := MouseRect.bottom - + (MouseRect.top - tPoint.v); + MouseRect.top := tPoint.v; + end + else If tPoint.v > MouseRect.bottom then begin + MouseRect.top := MouseRect.top + tPoint.v - MouseRect.bottom; + MouseRect.bottom := tPoint.v; + end; + end;{if not(ptinRect)} + + {tPoint is to Mouse as FlyRect.topleft is to FlightRect} + MapPt(tPoint,MouseRect,FlightRect); + + {determine horizontal offset if needed} + If tPoint.h > FlyRect.left + 2 then begin + FlyRect.left := FlyRect.left + 3;{offsetRect to right} + FlyRect.right := FlyRect.right + 3; + end + else if tPoint.h < FlyRect.left - 2 then begin + FlyRect.left := FlyRect.left - 3;{offsetRect to left} + FlyRect.right := FlyRect.right - 3; + end; + {vertical offset?} + If tPoint.v > FlyRect.top + 2 then begin + FlyRect.top := FlyRect.top + 3; + FlyRect.bottom := FlyRect.bottom + 3; + end + else if tPoint.v < FlyRect.top - 2 then begin + FlyRect.top := FlyRect.top - 3; + FlyRect.bottom := FlyRect.bottom - 3; + end; + + CopyBits(OffScreen,myWindow^.portBits,OffFly[FlyNdx], + FlyRect,srcCopy,FlowerRgn); + + If FlyNdx = 1 then inc(FlyNdx) {next shape, there are 2} + else FlyNdx := 1; {back to first shape} +end; + + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups to finish} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; {startup the fonts manager} + InitWindows; {startup the window manager} + InitMenus; {startup the menu manager} + TEInit; {startup the text edit manager} + InitDialogs(Nil); {startup the dialog manager} + + Finished := False; {set program terminator to false} + FlushEvents(everyEvent,0); {clear events from previous program} + { set up screen size stuff } + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do { Screen.Left, etc. } + Begin + SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); + SetRect(GrowArea,Left,Top+24,Right,Bottom); + End; +End; + +procedure CreateWindow; +var + Wrect: Rect; + TypeWind: Integer; + Visible: Boolean; + GoAway: Boolean; + RefVal: Longint; +Begin + SetRect(Wrect,10,40,500,300); + TypeWind := 0; + Visible := True; + GoAway := True; + + myWindow := NewWindow(Nil,Wrect,'Simple Animation',Visible, + TypeWind,Nil,GoAway,RefVal); + SetPort(myWindow); + ClipRect(myWindow^.portRect);{set clipping area as per Inside Mac} +End; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetItem(myMenus[1],theItem,name); + refNum := OpenDeskAcc(name); + End; + fileMenu: + Case theItem of + 1:{display offScreen bitmap in dialog} + Begin + myDialog := GetNewDialog(128,nil,myWindow);{from resource} + GetPort(tPort); + ShowWindow(myDialog);{invisible in resource} + SelectWindow(myDialog); + SetPort(myDialog); {so we can draw into our window} + + CopyBits(OffScreen,myDialog^.portBits,OffScreen.bounds, + OffScreen.bounds,srcOr,Nil);{the whole thing} + FrameRect(OffScreen.bounds);{frame it } + MoveTo(OffScreen.bounds.left + 10,OffScreen.bounds.bottom + 20); + DrawString('^ copy of OffScreen Bitmap'); + + ModalDialog(Nil,theItem);{we'll close no matter what hit} + + HideWindow(myDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort); + end;{1:} + 2:Finished := True; + end;{case theItem} + End; + HiliteMenu(0); +End; + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + MouseLoc: Point; + WindowLoc: integer; +Begin + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {I-441} + inContent: + If WhichWindow <> FrontWindow then + SelectWindow (WhichWindow); + inGoAway: + If TrackGoAway(WhichWindow,MouseLoc) then + Finished := True;{end the game} + end; {case of} +end; { TakeCareMouseDown } + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); +End; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect);{ or UpdateWindow^.VisRgn^^.rgnBBox } + DrawWindowContents(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure MainEventLoop; +var + myEvent: EventRecord; +Begin + InitCursor; + CursorIsOn := True;{keep track of hide/show cursor} + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: Finished:= True; + ActivateEvt:TakeCareActivates(myEvent); + UpDateEvt: TakeCareUpdates(myEvent); + End + + Else AnimateWagon;{animate one loop of our wagon shape} + + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + For i := 1 to lastMenu do + begin + InsertMenu(myMenus[i],0); + end; + DrawMenuBar; +End; + +procedure CloseStuff; +Begin + {be sure to kill any sound I/O before quitting!} +End; + +{Main Program begins here} +BEGIN + InitThings; + SetUpMenus; + CreateWindow; + CreatePictures;{load picts from resource file} + CreateOffScreenBitMap;{for use with copyBits procedure} + DrawPicsIntoOffScreen;{OffScreen holds all our wagon/fly shapes} + InitialAnimation;{set up stuff for start of animation} + MainEventLoop;{will animate wagon/fly if nothing else is going on} + CloseStuff; +END. diff --git a/Duane Blehm's Code/Cairo Æ’/About Cairo files.. b/Duane Blehm's Code/Cairo Æ’/About Cairo files.. new file mode 100755 index 0000000..4e955e1 --- /dev/null +++ b/Duane Blehm's Code/Cairo Æ’/About Cairo files.. @@ -0,0 +1 @@ +about the Cairo files.... 'Cairo Shapes' is a 'FullPaint' document containing the shapes that are used for animation, score, etc... can be opened from MacPaint. 'Cairo Animation' is a 'FullPaint' document illustrating the way animation is done in the game. 'CairoFont.pro' is an 'Edit' text file containing source code for procedure used to access the Cairo FontRecord. 'CairoShootOut.Pas' is an 'Edit' text file containing pascal source code to be compiled by the Pascal compiler. 'aCairo.R' is an 'Edit' text file containing RMaker source code to be compiled by RMaker. 'aCairo.Rsrc' is a resource document containing resources built with ResEdit, the resource editor. These resources are 'included' when 'aCairo.R' is compiled by RMaker. 'CairoShootOut' is our application... the result of Link. \ No newline at end of file diff --git a/Duane Blehm's Code/Cairo Æ’/Cairo Animation b/Duane Blehm's Code/Cairo Æ’/Cairo Animation new file mode 100755 index 0000000..fab5e2a Binary files /dev/null and b/Duane Blehm's Code/Cairo Æ’/Cairo Animation differ diff --git a/Duane Blehm's Code/Cairo Æ’/Cairo Shapes b/Duane Blehm's Code/Cairo Æ’/Cairo Shapes new file mode 100755 index 0000000..757000f Binary files /dev/null and b/Duane Blehm's Code/Cairo Æ’/Cairo Shapes differ diff --git a/Duane Blehm's Code/Cairo Æ’/Cairo ShootOut v1.2L b/Duane Blehm's Code/Cairo Æ’/Cairo ShootOut v1.2L new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Cairo Æ’/CairoFont.pro b/Duane Blehm's Code/Cairo Æ’/CairoFont.pro new file mode 100755 index 0000000..95f0e46 --- /dev/null +++ b/Duane Blehm's Code/Cairo Æ’/CairoFont.pro @@ -0,0 +1 @@ + {... here's how we copied the bitImage and locTable of Cairo font...} TYPE {GLOBAL types used for this procedure} FintPtr = ^FontRec; FontHandle = ^FintPtr;{FintPtr???, because FontPtr is already declared!} var {GLOBAL varibles used in this procedure} myFont: FontHandle;{so we can load resource and access FontRec} FontMap: BitMap;{will point to copy of font bitimage} RowWords: integer; RowBytes: integer; tPtr: Ptr; tLong: Longint; BitPtr: Ptr;{pointer to our Copy of BitImage} Locate: Array[0..130] of integer;{'partial' Cairo location table} LocatePtr: Ptr; procedure CreateCairoStuff; var trect:Rect; Begin {load Cairo font and make copies of Character image and locate table} myFont := FontHandle(GetResource('FONT',1426)); HLock(Handle(myFont));{lock it so it won't move... pointers!} RowWords := myFont^^.rowWords;{number of words in row} RowBytes := RowWords * 2;{number of bytes in row} {make tPtr point to start of fontRec bitimage} tPtr := @myFont^^.rowWords;{field just before the bitimage} tLong := ORD(tPtr);{coerce to longint,i-80} tLong := tLong + 2;{2 bytes to get to beginning of bit image,i-234} tPtr := Pointer(tLong);{coerce back to pointer} {create BitPtr size of the bitimage} BitPtr := NewPtr(RowBytes * myFont^^.fRectHeight); {bytes to move = bytes per row * number of rows....} BlockMove(tPtr,BitPtr,RowBytes * myFont^^.fRectHeight);{make the move!} {make tPtr point to start of fontRec locTable.. just beyond bitimage} tPtr := @myFont^^.rowWords;{find start of LocTable for font} tLong := ORD(tPtr);{coerce to longint,i-80} tLong := tLong + (RowBytes * myFont^^.fRectHeight) + 2;{bitimage + integer} tPtr := Pointer(tLong);{coerce back to pointer} {make LocatePtr point to start of our Locate table} LocatePtr := @Locate[0]; {bytes to move = (last char + 1) * 2.. in our case (130+1)*2 = 262} {or Locate[] contains 131 integers (zero based) = 262 bytes} BlockMove(tPtr,LocatePtr,262);{copy LocTable to our Locate array} {for Cairo: Locate[130] = 1656.. offset to rightside of last character, use ResEdit to look at last char of font... will give this OffSet} SetRect(tRect,0,0,Locate[130],myFont^^.fRectHeight); FontMap.baseAddr := QDPtr(BitPtr);{make FontMap point to BitPtr image} FontMap.rowbytes := RowBytes;{row width in bytes, must be even no.} FontMap.bounds := tRect; HUnlock(Handle(myFont));{unlock it, we're done with Ptr stuff} ReleaseResource(Handle(myFont));{if you're done with it} End; {once you have the FontMap ....to draw the char '33' from the BitImage:} var p,q:integer; SourceRect,DestRect:Rect; Begin SourceRect := FontMap.Bounds;{set top and bottom of Rect} SourceRect.left := Locate[33];{coordinate offset to leftside of char 33} SourceRect.right := Locate[33 + 1];{offset to rightside of char 33} DestRect := SourceRect;{size the destination rect} OffSetRect(DestRect,p,q);{locate the DestRect where you want drawn} {now do the transfer!} CopyBits(FontMap,myWindow^.portBits,SourceRect,DestRect,srcCopy,nil); End; \ No newline at end of file diff --git a/Duane Blehm's Code/Cairo Æ’/CairoShootOut.Pas b/Duane Blehm's Code/Cairo Æ’/CairoShootOut.Pas new file mode 100755 index 0000000..1bbecd8 --- /dev/null +++ b/Duane Blehm's Code/Cairo Æ’/CairoShootOut.Pas @@ -0,0 +1,2811 @@ +Program CairoShootOut;{Copyright © 1987 by Duane Blehm, All Rights Reserved, +Distribution of this SourceCode is strictly prohibited!} + +{version 1.2, fixes bug in formula to find random Char's for targets} +{disclaimer: We make not claims about the 'elegance' of this code. We are +relatively new to Pascal and to Mac specific memory management, etc. Our +trial and error code will seem crude to anyone experienced in these matters. +... but we just keep on learning and having fun! If you find any 'for sure' +Errors, let us know.} + +{A couple of procedures dealing with the 'Double KeyCode Lock' scheme +have been removed from this code. Also the game resulting +from compiling this code will not execute very well beyond the first +few levels; however as an example of how the game works the code +is otherwise complete. Please do not distribute any copys of the +game produced by compiling this code! The Copy of 'Cairo ShootOut!' +on this disk is complete in every way.} + +{we have a growing library of 'crude' utilitys for creating the various +'custom' resources used in this and our other programs. For instance +we've written one to create our 'RGN ' resources which are 'tracings' of +picture outlines used to mask irregular shapes for use with CopyBits. We +have a couple that take resources created with MacroMind's Music to +Video utility (from MusicWorks data) and create our 'SONG' resources. +Also one that reads text from a text file data fork and creates the +'TBUF' text resources we use with the TextBox() rom call. We noticed the +'bmap' resource type in ResEdit and came up with procedures to save and +'get' this type resource which can result in significant savings of space +and code in the late stages of program developement. Including all this +stuff is beyond the scope of this code. However, if you are interested +in specific (not elegant) code, it is available. Drop us a line about +what you'd like to see plus $15 and we'll send you a disk on +'how we did it'.} + +{note this version executes GameLoop level#1 at approx. 4 ticks/loop +or 15 loops/ second} +{also does away with Cairo font,had to write myTextBox() to draw clues +between levels with myCFPtr (Cairo font bitimage)} + +USES MacIntf,MacPrint;{we may have to print the order form} + +{$T APPL NILE set the TYPE and CREATOR}{TML Pascal Directives} +{$B+ set the application's bundle bit } +{$L aCairo.rsrc}{link all our resource stuff} + +CONST + WindResId = 128; {Window Resource} + lastMenu = 5; {Number of Menus} + appleMenu = 1; + fileMenu = 256; {Menu Resource Id's} + optionMenu = 257; + worksMenu = 258; + messageMenu = 259; + {Control Resources} + pressResume = 130; + pressEnd = 131; + LastTarget = 7;{number of targets in list} + MaxInteger = 32767; + +TYPE + {type Dialogs and Sounds so we can access them by name} + DialogList = (Help,About,ClueDial,BMap,Tomb,Guard,What, + UnLock,Source,Scores,Name,SetSpeed,PrintDial); + SoundList = (GunShot,GoodHit,BadHit,Miss,BonusSnd,TimeOut, + SnakeCharmer,Walk,Silence); + + {type bmap for loading previously created 'bmap' resources} + BmapRec = Record + baseAddr:Ptr; + rowBytes:integer; + bounds:Rect; + bitStart:array[1..1] of byte;{so we can Ptr to start of BitImage} + end; + BmapPtr = ^BmapRec; + BmapHand = ^BmapPtr; + + {to get bmap's we created the BitMaps as in the Animation Example, then + drew the pictures,etc... then called the following to make the bmap: + + procedure CreateBMaps(addBits:Boolean); + var + theErr:OsErr; + myBMap:BmapHand; + Begin + (*make a copy of the Bitmap with a Handle*) + myBMap := BmapHand(NewHandle(SizeOf(BitMap))); + myBMap^^.rowBytes := OffScreen.rowBytes; + myBMap^^.bounds := OffScreen.bounds; + (*now stick a copy of the BitImage on the end of the Handle*) + If addBits then theErr := PtrAndHand(OffScreen.baseAddr, + Handle(myBMap),GetPtrSize(OffScreen.baseAddr)); + (*watch out for memory errors!*) + AddResource(Handle(myBmap),'bmap',128,'offscreen'); + If theErr <> noErr then SysBeep(1);(*our PtrAndHand probably failed*) + End; + + this is more efficient than a lot of pictures, creating Bitmaps, + drawing, locating rects, etc... however locking all this down too soon + in development process could hinder making changes later on....} + + ScoreNText = Record {record for hiscores} + nameStr:Str255;{we'll pack 4 names into NameStr} + level:array [1..4] of Longint; + score:array [1..4] of Longint; + end; + ScorePtr = ^ScoreNText; + + {at one time we included the Cairo font in resources, loaded it and made + copies of Bitimage and locate table... but came up with this record to + store only the parts we needed!.. see 'CairoFont.pro' file for more info} + CFRec = Record {resource contains only bitimage and locate table of Cairo} + bitImage:Array[1..105,1..24] of integer;{char. bitimage} + locate:Array[0..130] of integer;{partial location table} + end; + CFPtr = ^CFRec; + CFHandle = ^CFPtr; + + {note: it is easy to create a resource... just create the data with a + handle to it... do an AddResource()..copy/paste it into application + resource file and reverse the procedure to load it into application.} + + nrctRec = Record {so we can load a list of rects from resource} + num:integer;{Number of records} + theRects:array[1..100] of Rect;{limits on # of rects??} + end; + nrctPtr = ^nrctRec; + nrctHandle = ^nrctPtr; + + ClueRec = Record{record of stuff for drawing clues between levels} + message:Str255; {message for this clue} + messRect:Rect; {box to contain message.. TextBox()} + hint:Str255; {clue word} + hintRect:Rect; + just:integer; {justification for hint textbox,all messages centered} + glyphPic:PicHandle;{heiroglyph picture from resource} + dialHand:Handle;{handle to textitem in dialog} + dialWord:Str255;{string contains user input} + end; + + SongRec = Record {import a song record from our snake 'SONG' resource} + noteCount:integer;{how many notes in this song} + pitch:array[1..48,1..2] of Longint;{48 notes,2 voices} + duration:array[1..48] of integer;{how many ticks will pitch play} + end; + SongPtr = ^SongRec; + SongHandle = ^SongPtr; + + WSongRec = Record {import a song record from our Walk 'SONG' resource} + noteCount:integer;{how many notes in this song} + pitch:array[1..68,1..2] of Longint;{68 notes,2 voices} + duration:array[1..68] of integer;{how many ticks will pitch play} + end; + WSongPtr = ^WSongRec; + WSongHandle = ^WSongPtr; + {note:could have used same song type for both songs except for # of notes, + We'll change the Record to be note 'independent' next time.} + + TimeBonusRec = Record {stuff for display of Time-to-Score at end of level} + frame:Rect; {size window for Timebonus} + hourmove:Point; {offset for hourglass...move from normal position} + numbox:Rect; {frame for numbers} + arrowDest:Rect; {destination for copybits of arrow} + arrowSource:Rect;{source in Cairo bitmap} + end; + + TargetRec = Record {record of individual targets on display} + theChar:integer;{character in the font} + head:integer; {pixOff when the target begins in Wings} + wide:integer; {width of target} + end; + + ValidRec = Record {record for scorebox and score targets} + which:integer; {which char in font} + points:integer; {points for hitting this target} + remain:integer; {how many of this target left to hit} + remainPt:Point; {for finding moveto point for drawString} + blankRect:Rect; {erase rect for drawing remain # into scorebox} + centerPt:Point; {top,centerline of box in scoreframe} + end; + + StatusRec = Record {4 boxes for score,hiscore,etc} + blankRect:Rect; {rect to erase before drawing new number} + centerPt:Point; {for finding MoveTo point before DrawString} + end; + +VAR + {local var's made global.. for speed of execution of code} + unNames: array[1..4] of Str255; {unpack our hiscore names} + RListHand: nrctHandle; + si,sj: integer;{StartOne and StaggerOne} + di,dj,dk,dm: integer;{DetectAHit and HandlAHit} + HitAValidTarget: boolean; + + GameOver: Boolean; {set True if hourglass empty} + myRects: nrctHandle; {access to Rects in nrct resource} + TarWindPic: PicHandle; {pic of our collective windows} + myCFPtr: CFPtr; {access to Cairo font stuff in resources} + SpeedTrapOn: Boolean; {if true slow down the game} + SpeedFactor, + SpeedTicks: Longint; {rate of delay / slow down} + theHighs: ScorePtr; {access to hiscore record/handle} + ScoreHandle: Handle; {handle to hiscore resource} + NameString: Str255; {store our players name for hiscore} + BuffSize: Longint; {size buffers} + PagePrint: THPrint; { handle to the page setup record } + ResRefNum: integer; {reference number for appl.resource file} + tBuf: Handle; {text from TBUF resource for TextBox} + ClueNdx: integer; {point to next clueword} + ClueWords: Array[1..10] of Str255; {hold various possible clues} + DialNdx: DialogList; {will step through our dialogs array} + myDialog: Array[Help..PrintDial] of DialogPtr; + myDialogRec: Array[Help..PrintDial] of DialogRecord; + Tut: Array[168..171] of PicHandle; {for drawing tomb} + ClueShadow: PicHandle; {shadow the booth to hilight clue} + ClueErase: Rect; {rect for erase before Clues} + Clues: Array[1..4] of ClueRec; {our awards..} + FetchChar: Rect; {used in startOne... constant top/bottom} + CursorNdx: integer; {which cursor to use} + myCursor: Array[1..3] of CursHandle;{our three cursors} + StartPitch: Array[1..4] of Longint;{pitches to start targetsound} + UpDateMap: BitMap; {keep a copy of targetwindows during pause} + UpDateRect: Rect; {for copybits of bitmap} + ResumeButton, + EndButton: ControlHandle; { handles to controlbuttons } + RedrawRect: Rect; {area for invalRect for levels} + TimeCount: integer; {interval between hourglass moves} + TBone: TimeBonusRec;{hold all our timebonus stuff} + ShadowRect: Rect; {rect used to shadow booth interior} + LastValid: integer; {number of Score targets..max of 6} + Score,HiScore, + Level,MissCount: Longint; + StatPic: PicHandle; {Status is pic of Score/HiScore frame} + StatList: Array[1..4] of StatusRec;{stuff for drawing Scores,etc} + ValidDone: Boolean; {flag is all valids have 0 remain} + Direction: Array[1..3] of integer;{scroll right or left} + Vector: Array[1..3] of integer;{will be product of speed and direction} + TarWindRgn: RgnHandle; {region to mask drawing eye in TarWindow[4]} + BonusMap: BitMap; {load from bmap resource, contains BTarget pics} + OffBonus: Array[1..6,1..2] of Rect;{Rects to shapes in BonusMap} + BonusNdx: integer; {pointer to OffBonus rects, 1-6} + BonusShape: integer; {pointer to 1 of two shapes,internal anim.} + BonusRect: Rect; {onScreen Rect for CopyBits of Bonus Shapes} + BonusUnderWay: Boolean; {is bonus underway?} + BonusLoops, + PrevBonusLoops: integer; {count for # of loops} + BonusSpeed: integer; {-4 to +4 lateral movement...left or right} + + MouseRect, + GunLimit: Rect; {limit movement of site during game} + GameUnderWay: Boolean; + PauseStatus: integer; {0 no pause,1 game pause, 2 clue pause} + NowValid: Array[1..6] of integer;{list of Valids with targets remaining} + NowNdx: integer; {index for NowValid} + ScoreNumRect, + ScoreFrameRect: Rect; + ScoreNum, + ScoreFrame: PicHandle; {for drawing ScoreBox frame} + tPt: Point; + tFlag: Boolean; + tRect,sRect,dRect: Rect; {global temporary varibles} + LeftSide,RightSide, + BackSide: PicHandle; {our shooting gallery pics} + Valid: Array[1..6] of ValidRec;{list of valid targets} + + OffHour,Hour, {hourglass} + OffBotSand,OffTopSand, {frame complete top and bottom sand} + OBSand,BotSand, {partial top and bottom sand,for animation} + OTSand,TopSand: Rect; + TrickleTop, + TrickleBot: Point; {left sand trickle (line) top and bottom} + TrickTopR, + TrickBotR: Point; {points for drawing right sand trickle line} + TrickleLeft: Boolean; {true if trickle currently leftside} + BotSandRgn, + TopSandRgn: RgnHandle; {regions for masking sand into hourglass} + + {for drawing spot targets in side windows} + SpotSpeed: Array[5..6] of integer;{window 5&6,speed of target} + OffSpotTarget: Rect; {whole spot} + OffSpot: Array[5..6] of Rect;{partial spot...} + SpotRect: Array[5..6] of Rect;{locate spot on screen} + TopSpot,BotSpot: Array[5..6] of integer;{turn around point for spots,speed} + + TarList: Array[1..3,1..LastTarget] Of TargetRec;{targets on Screen} + NxtTar, {points to TarList element} + Speed,PixOff: Array[1..3] of integer;{pixoff= # of pixels target moves} + HitWhich, {which target in the window target list} + HitWindow: integer; {which window hit was made in} + Hit: Boolean; {true if a target was hit} + TargetCenter: Point; {point at center of Rect pointed to by mouse/site} + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu, + theItem: integer; + Finished: Boolean; {used to terminate the program,quit} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; + wRecord: WindowRecord;{we alocate window on the stack} + Screen,DragArea: Rect; {set limits of drag area} + myMouse: Point; {used with GetMouse in animateoneloop} + aTick: Longint; {usually with TickCount but sometimes just temp.} + + ExplodeRect, + OffExplode: Rect; {Rects to explode shape in offscreen} + WhichGun: integer; {which gun to draw for gray backgound} + GunRect: Rect; {destination rect for drawing Gun} + OffGun,OffSite: Array[0..1] of Rect;{two guns, for drawing over gray} + TarWindow: Array[1..6] of Rect;{our targets windows..6 of them} + ShotFired: Boolean; + Piece,OffPiece: Array[1..3] of Rect;{rects to tiny area left after scroll} + SiteRect:Rect; + SiteRgn: Array[0..1] of RgnHandle;{for drawing site into OffGun rects} + myPattern, + myDiagPat: Pattern; {patterns for drawing shading} + + FontMap: BitMap; {Cairo font bitimage from resource,myCFPtr} + Wings: Array[1..3] of BitMap;{new targets just coming into window} + + tStr: Str255; + tPtr: Ptr; + tLong: Longint; + + OffScreen,OldBits: BitMap; {for drawing into offscreen} + tRgn: RgnHandle; + Counter: integer; {count loops for hourglass/time} + i,j,k,m,n: integer; + + {Sound varibles} + Buff: Array[GunShot..TimeOut] of Longint;{for Sound buffers} + myPtr: Ptr; + myHandle: Handle; + Sound: Array[GunShot..SnakeCharmer] of FFSynthPtr;{FreeForm synthesizer sound} + SnakeSynth: FTSynthPtr; {Snake is a FourTone sound/use for Walk too} + SnakeSound: FTSndRecPtr; {two FourTone Sounds} + SoundParmBlk: ParmBlkPtr; {used for PBWrite instead of StartSound} + WhichSound: SoundList; {which sound is being played?} + err: OSerr; + SawToothWave: wavePtr; + SquareWave: wavePtr; + mySongPtr: SongPtr; {snake song} + myWSongPtr: WSongPtr; {walk song.. different # of notes} + ToneTicks, + ToneDelay: Longint; {# of ticks each song pitch will play} + NoteCount: Longint; {# of notes played for song} + SoundOn: Boolean; {user request for sound off/on} +{----------------------------------------------} +procedure SetCursorMenus(StartGame:Boolean); +var i:integer; +Begin + If StartGame then begin {starting the game} + InsertMenu(myMenus[5],0); {display 'any key to exit' message} + For i := 1 to 4 do DisableItem(myMenus[i],0);{disable (gray) others} + DrawMenuBar; {draw the menus} + GameUnderWay := True; {we're back into game mode} + HideCursor; + FlushEvents(mDownMask,0); {clear all mouseDowns} + end + Else Begin {interrupt the game} + If CursorNdx < 3 then inc(CursorNdx) else CursorNdx := 1; + SetCursor(myCursor[CursorNdx]^^);{display one of our 3 cursors} + ShowCursor; + GameUnderWay := False; + DeleteMenu(MessageMenu); {remove exit message,i-354} + For i := 1 to 4 do EnableItem(myMenus[i],0);{show other menu options} + DrawMenuBar; + end; +End; + +procedure DrawStatus(which:integer;amount:Longint); +{draw score,missCount, etc. into Stat frame} +Begin + NumToString(amount,tStr);{convert number to string} + MoveTo(StatList[which].centerPt.h-(StringWidth(tStr) div 2), + StatList[which].centerPt.v);{center it} + EraseRect(StatList[which].blankRect);{erase last} + DrawString(tStr);{draw it!} +end; + +{myTextBox is similar to TextBox() rom but uses Cairo BitImage in myCFPtr} +procedure myTextBox(text:Ptr;length:Longint;box:Rect;just:integer); +var + StartLine:Ptr; + EndLine,TestEnd:Ptr; + LineWidth,TestWidth:integer; + Done:Boolean; + which:byte; +procedure DrawLine; +var + index:Longint; +Begin + Case just of {justification....find starting point for first dRect} + -1: dRect.left := box.right - LineWidth; {right} + 0: dRect.left := box.left;{left} + 1: dRect.left := box.left + ((box.right-box.left-LineWidth) div 2);{center} + end;{case} + For index := ord(StartLine) to ord(EndLine) do begin + If Ptr(index)^ <> 32 then begin {Ptr(index)^ points to which} + sRect.left := myCFPtr^.locate[Ptr(index)^]; + sRect.right := myCFPtr^.locate[Ptr(index)^+1]; + dRect.right := dRect.left + sRect.right-sRect.left; + CopyBits(FontMap,myWindow^.portBits,sRect,dRect,srcCopy,nil); + dRect.left := dRect.right + 2;{move over for next char} + end{if Ptr(} + else dRect.left := dRect.right + 10;{for space char} + end;{for index} +End; + +procedure InitialLine; +Begin + StartLine := EndLine; + TestEnd := Endline; + LineWidth := 0; + TestWidth := 0; +end; + +procedure NewLine; +Begin + OffSetRect(dRect,0,28);{move down a line,font height + 4} + EndLine := Ptr(ord(EndLine)+1);{end currently a space so move one} + {could test here for more spaces!} + InitialLine; +End; + +Begin + SetRect(sRect,0,0,0,24);{height of 24 for Cairo} + If length > 0 then length := length -1;{we're zero based} + {initialize varibles} + EndLine := text; + InitialLine; + dRect.top := box.top; + dRect.bottom := dRect.top + 24;{fHeight := 24 for Cairo} + Done := false; + EraseRect(box); + Repeat + which := TestEnd^; + If which <> 32 then begin {break lines on spaces} + TestWidth := TestWidth + myCFPtr^.locate[which+1] - + myCFPtr^.locate[which] + 2; + {test for end of text here} + If ord(TestEnd) >= (ord(text) + length) then Begin {end of text} + If TestWidth > (Box.right-Box.left) then begin {line too long} + DrawLine; + NewLine;{set TestEnd back to Last EndLine and continue} + end + else begin {line width ok so draw it all} + EndLine := Ptr(ord(text)+length);{set to end of text} + LineWidth := TestWidth;{approx width?} + DrawLine; + Done := True; {we're finished} + end;{else} + end;{if ord} + end{if which} + else begin {current which is a space} + TestWidth := TestWidth + 10;{width of my space} + If TestWidth > (Box.right-Box.left) then begin {word won't fit} + DrawLine;{draw the line without Test stuff on end} + NewLine; + end + else begin {move actual end of line up to testend} + EndLine := TestEnd; + LineWidth := TestWidth; + end;{else } + end;{else} + TestEnd := Ptr(ord(TestEnd)+1);{move TestEnd up one} + + Until(Done); +End; + +procedure DrawAClue(index:integer);{clues are displayed on even levels 2-8} +Begin + DrawPicture(ClueShadow,ClueShadow^^.picFrame);{shadow on booth} + EraseRect(ClueErase);{erase the background in target area} + {draw one of 4 glyph pictures} + DrawPicture(Clues[index].glyphpic,Clues[index].glyphpic^^.picframe); + {myTextBox uses Cairo bitimage in FontMap to display text} + myTextBox(Pointer(ord(@Clues[index].message)+1),Length(Clues[index].message), + Clues[index].messRect,teJustCenter);{draw message} + myTextBox(Pointer(ord(@Clues[index].hint)+1),Length(Clues[index].hint), + Clues[index].hintRect,Clues[index].just);{draw clueword} +End; + +procedure DrawRemainIntoScoreBox(which:integer); +{draw # of targets remaining for 'which' Valid target} +Begin + EraseRect(Valid[which].blankRect);{erase last} + NumToString(Valid[which].remain,tStr); + MoveTo(Valid[which].remainPt.h-(StringWidth(tStr) div 2), + Valid[which].remainPt.v);{center it} + DrawString(tStr); +end; + +procedure PaintAShadow(aRect:Rect);{used to shade booth and Valid targets} +Begin + PenMode(patOr);{ will combine pattern with existing background} + PenPat(myDiagPat);{diagonal lines} + PaintRect(aRect);{fill aRect with the pattern... mode 'or'} + PenNormal; {reset the Pen stuff to default} +end; + +procedure DrawHourGlass;{draw hourglass and top/bottom sand} +Begin + CopyBits(OffScreen,myWindow^.portBits,OffHour,Hour,srcCopy,nil);{hourglass} + {OTSand & OBSand rects are moving over Sand pics on OffScreen} + CopyBits(OffScreen,myWindow^.portBits,OTSand, + TopSand,srcCopy,TopSandRgn);{top Sand} + CopyBits(OffScreen,myWindow^.portBits,OBSand, + BotSand,srcCopy,BotSandRgn);{Bottom sand} +end; + +procedure SizeRectsForBox(whichchar,whichbox:integer); +{for drawing char/targets into scorebox each level and updates} +Begin + {sRect.top and bottom are constant.. fontMap.bounds} + sRect.left := myCFPtr^.locate[whichchar];{left point from locate table} + sRect.right := myCFPtr^.locate[whichchar+1];{right point from locate table} + {center tRect in its ScoreBox} + tRect.left := Valid[whichBox].centerPt.h - ((sRect.right-sRect.left) div 2); + tRect.right := tRect.left + (sRect.right-sRect.left); +End; + +procedure DrawTargetsInScoreBox; +Begin + {draw the targets into the scoreBox} + sRect := FontMap.bounds;{bounds rect of FontMap bitmap,top/bottom constant} + For i := 1 to LastValid do begin {4-6 scoreboxes} + tRect.top := Valid[i].centerPt.v;{topcenter of box} + tRect.bottom := tRect.top + 24;{height of font} + + SizeRectsForBox(Valid[i].which,i);{size sRect/tRect left and right} + + CopyBits(FontMap,myWindow^.portBits,sRect,tRect,srcCopy,nil); + DrawRemainIntoScoreBox(i);{# remaining, draw just below target} + if Valid[i].remain = 0 then begin {disabled shading over box} + tRect := Valid[i].blankrect; + tRect.top := Valid[i].centerPt.v;{tRect now is whole box} + PaintAShadow(tRect); + end;{if valid remain} + end; +End; + +procedure DrawWindowContents(WhichWindow:WindowPtr);{update events} +Begin + DrawPicture(BackSide,BackSide^^.picFrame);{back/center of booth} + DrawPicture(LeftSide,LeftSide^^.picFrame);{left side of booth} + DrawPicture(RightSide,RightSide^^.picFrame);{right side of booth} + DrawPicture(ScoreFrame,ScoreFrameRect); {score box frame} + DrawPicture(ScoreNum,ScoreNumRect); {points in score box} + DrawPicture(StatPic,StatPic^^.picFrame); {status/score box frame} + DrawHourGlass; {hourglass stuff} + DrawStatus(1,Level);{four varibles in status box, score etc.} + DrawStatus(2,Score); + DrawStatus(3,MissCount); + DrawStatus(4,HiScore); + + Case PauseStatus of + {PauseStatus tells us 'how' the screen 'looks',what to draw} + 0:Begin {no pause.. usually in between games} + DrawPicture(TarWindPic,TarWindPic^^.picFrame); + If not(GameUnderWay) then begin + PaintAShadow(ShadowRect);{shadow the booth interior} + DrawTargetsInScoreBox; + end; + end;{case 0} + 1,2:Begin {game under way pause...1 = keydown interrupt?, 2 = clue} + If PauseStatus = 1 then CopyBits(OffScreen,mywindow^.portBits, + OffGun[whichGun],GunRect,srcCopy,nil);{redraw gun} + DrawTargetsInScoreBox; + If PauseStatus = 2 then DrawAClue(Level div 2) {clue on display} + else {Copy from UpDateMap... contains copy of targets, when pause began} + CopyBits(UpDateMap,myWindow^.portBits,UpDateMap.bounds,UpDateRect, + srcCopy,nil); + end;{case 1,2} + end;{case pauseStatus} + + DrawControls(myWindow);{draw Continue/End buttons if they're not hidden} +End; + +procedure UpdateMyWindow; +Begin + BeginUpDate(myWindow); + EraseRect(myWindow^.portRect);{will be clipped to update rgn} + DrawWindowContents(myWindow); + EndUpDate(myWindow); +End; + +procedure DrawBoldButton(WhichDialog:DialogList);{bold button in dialogs} +var + tHand:Handle;{j:integer,tRect:Rect} +Begin + PenSize(3,3); + GetDItem(myDialog[WhichDialog],1,j,tHand,tRect);{frame Item #1} + InsetRect(tRect,-4,-4); + FrameRoundRect(tRect,16,16); + PenSize(1,1); +End; + +procedure DisplayWhatDialog(WhichDialog:DialogList);{called from Guard dialog} +var + tRect,fRect: Rect; + itemHit,i,j: integer; + tPort: GrafPtr; +Begin + GetPort(tPort); + ShowWindow(myDialog[WhichDialog]);{all dialogs are hidden} + SelectWindow(myDialog[WhichDialog]);{bring it to the front, activate} + SetPort(myDialog[WhichDialog]); {so we can draw into our dialog window} + tBuf := GetResource('TBUF',127);{load our text from resource} + HLock(tBuf);{lock it down, so we can use a ptr, don't want it moving on us} + TextFont(3); + TextSize(9); + {draw text into dialog window} + TextBox(tBuf^,GetHandleSize(tBuf),myDialog[what]^.portRect,teJustLeft); + HUnLock(tBuf);{done} + DrawBoldButton(WhichDialog); + ModalDialog(Nil,itemHit); + + HideWindow(myDialog[WhichDialog]); + SetPort(tPort);{restore port} +end; + +procedure DrawOrderForm(frame:Rect);{for printing} +var + tRect:Rect; + tStr:Str255; + myHandle:Handle; +Begin + {all drawing is being done into the printer port/bit map} + TextFont(geneva); + TextSize(10); + tRect := frame;{size of printing output} + tRect.top := tRect.top + 130;{reserve 130 pixels for our header} + InsetRect(tRect,20,0);{shink the page size} + {draw page stuff here} + HLock(tBuf);{tbuf loaded in the DoPrint procedure} + TextBox(tBuf^,GetHandleSize(tBuf),tRect,teJustCenter);{draw it } + HUnLock(tBuf); + tRect := Clues[3].glyphpic^^.picframe;{Chariot glyph pic, upper left} + OffSetRect(tRect,frame.left+20-tRect.left,frame.top+10-tRect.top); + DrawPicture(Clues[3].glyphpic,tRect); + + GetIndString(tStr,1000,7);{source of copy of program} + tRect.top := tRect.top + 10;{relocate tRect from pic above} + tRect.left := tRect.right + 10; + tRect.right := tRect.left + 100; + tRect.bottom := 129; + TextBox(Pointer(ord(@tStr)+1),Length(tStr),tRect,teJustLeft); + + TextSize(12); + GetIndString(tStr,1000,5);{logo text stuff,upper right} + tRect := frame; + tRect.top := tRect.top + 10; + tRect.left := tRect.right - 180; + tRect.bottom := 129;{just above completed textbox!} + TextBox(Pointer(ord(@tStr)+1),Length(tStr),tRect,teJustLeft); + + OffSetRect(tRect,-186,0); + tRect.bottom := tRect.top + 32; + tRect.left := tRect.right - 32;{for drawing logo icon} + myHandle := GetIcon(130); + PlotIcon(tRect,myHandle); + + MoveTo(Frame.left + 20,Frame.top + 120);{just below glyph pic} + TextSize(12); + TextFace([Bold,Shadow]); + DrawString('Cairo #1 KeyCode = '); +End; + +procedure DoPrintUpdates;{Printer dialogs erase the windows behind them so +update myWindow,Guard dialog,display PrintDialog} +{there has got to be a better way of updating while dialogs are on display.. +perhaps by using Modeless dialogs and handling all events in the program rather +than calling ModalDialog()} +var + EventFlag,Result:Boolean; + tPort:GrafPtr; + myEvent:EventRecord; + itemHit:integer; + Digit:Char; +Begin + GetPort(tport); + SetPort(myWindow); + UpdateMyWindow;{dialogs have erased our window so do updates} + SetPort(myDialog[Guard]);{do updates pending for dialog} + Repeat + Result := GetNextEvent(everyEvent,myEvent);{mask only Updates?} + EventFlag := IsDialogEvent(myEvent); + If EventFlag then + Result := DialogSelect(myEvent,myDialog[guard],itemHit) + Until(not(EventAvail(EveryEvent,myEvent))); + + ShowWindow(myDialog[PrintDial]);{our 'cancel printing' dialog} + SelectWindow(myDialog[PrintDial]); + SetPort(myDialog[PrintDial]); {so we can draw into our dialog window} + Digit := chr(17);{this is the 'Commandkey symbol'} + tStr := concat(Digit,' .'); + ParamText(tStr,'','','');{install command key-period into dialog ^0} + Repeat + Result := GetNextEvent(everyEvent,myEvent);{do updates} + EventFlag := IsDialogEvent(myEvent); + If EventFlag then + Result := DialogSelect(myEvent,myDialog[PrintDial],itemHit) + Until(not(EventAvail(EveryEvent,myEvent))); + SetPort(tPort);{restore port and get back to printing stuff} +End; + +procedure DoPrint;{print the order form} +var + myPrPort:TPPrPort; + myStRec:TPrStatus; + tPort:GrafPtr; +begin + GetPort(tPort);{we'll be changing to printer port, so get copy of current} + tBuf := GetResource('TBUF',128);{order form text,had error during OpenPage?} + PagePrint := THPrint(NewHandle(SizeOf(TPrint)));{create our print record} + PROpen;{open the print manager!! ii-157} + If PrError = noErr then begin + PrintDefault(PagePrint);{fill with default values ii-158} + {tFlag := PrStlDialog(PagePrint);this is page setup stuff} + If PrJobDialog(PagePrint) then begin {dialog with user} + DoPrintUpDates;{clean up our windows,display 'cancel' dialog} + myPrPort := PrOpenDoc(PagePrint,nil,nil); + PrOpenPage(myPrPort,nil); { start new page} + If PrError = noErr then + DrawOrderForm(PagePrint^^.prInfo.rPage);{draw my page} + + PrClosePage(myPrPort); + + PrCloseDoc(myPrPort); + If (PagePrint^^.prJob.bjDocLoop = bSpoolLoop) AND + (PrError = noErr) then + PrPicFile(PagePrint,nil,nil,nil,myStRec); + + {If PrError <> noErr then alert user of printing error} + end;{if user says then open the printing job} + end;{if PROpen successfull} + PRClose;{close the print manager} + HideWindow(myDialog[PrintDial]);{close our cancel/command-period dialog} + SetPort(tPort); +End; + +procedure DisplayAlert(WhichAlert:integer;Var result:integer); +var + tPort:GrafPtr; +Begin + GetPort(tPort); + result := NoteAlert(WhichAlert,nil);{display the alert} + ResetAlrtStage;{i-423} + SetPort(tPort); +End; + +procedure SetControlValue(which:integer); +{for SetSpeed dialog,which item#, the radio controls are numbered 2 thru 4} +var + i,h:integer; +Begin + For i := 2 to 4 do begin {reset controls to user selected control,which} + GetDItem(myDialog[SetSpeed],i,h,myHandle,tRect); + If i = which then SetCtlValue(ControlHandle(myHandle),1) + else SetCtlValue(ControlHandle(myHandle),0); + end; + Case which of + 2: SpeedTrapOn := False;{normal mode... no delays} + 3,4:begin + SpeedTrapOn := True;{flag that delays are needed} + If which = 3 then SpeedFactor := 2 + else SpeedFactor := 3;{how much delay.. 2 or 3 ticks} + end; + end;{case which} +End; + +procedure DisplayDialog(WhichDialog:DialogList);{from dialog list} +var + tRect,fRect,messFrame,itemRect: Rect; + itemHit,i,j,theType: integer; + tPort: GrafPtr; + tHandle: Handle; + index:integer; +Begin + GetPort(tPort); + ShowWindow(myDialog[WhichDialog]);{dialogs are hidden in resource} + SelectWindow(myDialog[WhichDialog]);{bring to front, activate} + SetPort(myDialog[WhichDialog]); {so we can draw into our dialog window} + + FlushEvents(mDownMask,0); {clear all mouseDowns} + + DrawBoldButton(whichDialog);{draw item one button bold, Enter key default} + Case WhichDialog of + Help: + ModalDialog(Nil,itemHit); {close it no matter what was hit} + About:begin + aTick := 1;{for a delay} + Repeat + ModalDialog(Nil,itemHit); + If itemHit = 8 then begin {animate the chariot from OffScreen} + SetRect(tRect,0,0,43,19);{size of one chariot} + fRect := tRect; + OffSetRect(tRect,OffScreen.bounds.right-tRect.right, + OffScreen.bounds.bottom-tRect.bottom);{in offScreen} + OffSetRect(fRect,myDialog[About]^.portRect.left-fRect.right, + myDialog[About]^.portRect.bottom - 2 -fRect.bottom); + MoveTo(fRect.right,fRect.bottom); + LineTo(500,fRect.bottom);{baseline} + {animate until chariot is off right side} + While (fRect.left < myDialog[About]^.portRect.right) do begin + CopyBits(OffScreen,myDialog[About]^.portBits,tRect,fRect, + srcCopy,nil);{draw a chariot} + OffSetRect(fRect,3,0);{move destination to right 3 pixels} + {make tRect point to one of chariot shapes in ChariotMap} + If tRect.right < OffScreen.bounds.right then + OffSetRect(tRect,43,0) {move right to next shape} + else OffSetRect(tRect,-86,0);{move back to first} + Delay(aTick,SpeedTicks);{delay one tick} + end;{while fRect} + end;{if itemhit} + + Until(itemHit = 1); + end; + ClueDial:{get 4 clue words from user to open tomb} + Begin + For i := 1 to 4 do SetIText(Clues[i].dialHand,'');{empty strings} + SelIText(myDialog[ClueDial],6,0,0);{select first box} + Repeat + ModalDialog(Nil,itemHit);{Modal does an Update event for the dialog box} + + Until((itemHit = 1) or (itemHit = 11));{ok button or Pass} + + {exit with Pausestatus = 3 for tomb or 0 for continue} + If itemHit = 1 then begin {check the user input against hints} + PauseStatus := 3; + For i := 1 to 4 do begin {check user input against clues} + GetIText(Clues[i].dialHand,Clues[i].dialWord); + If Clues[i].hint <> Clues[i].dialWord then PauseStatus := 0; + end;{for i} + + If PauseStatus = 0 then DisplayAlert(134,i);{sorry, bad clues} + end{If itemhit} + else If itemHit = 11 then PauseStatus := 0;{user wants to pass} + End;{cluedial} + + BMap:begin{peek offscreen bitmap} + CopyBits(OffScreen,myDialog[BMap]^.portBits,OffScreen.bounds, + OffScreen.bounds,srcCopy,nil); {copy bitmap to dialog box} + tRect := BonusMap.bounds; + OffSetRect(tRect,myDialog[BMap]^.portRect.right - 3 - trect.right, + myDialog[BMap]^.portRect.bottom - 3 - trect.bottom); + CopyBits(BonusMap,myDialog[BMap]^.portBits,BonusMap.bounds, + tRect,srcCopy,nil); {bonus pics to bottom right} + DrawBoldButton(whichDialog); + ModalDialog(Nil,itemHit); {close it no matter what was hit} + end;{BMap} + + Tomb:begin + SetOrigin(70,6);{shrink tut to fit in dialogue window} + + {draw inside of tomb} + DrawPicture(Tut[170],Tut[170]^^.picFrame);{background} + tRect := Tut[169]^^.picFrame; + For i := 1 to 11 do begin + DrawPicture(Tut[169],tRect);{pics along back wall} + OffSetRect(tRect,36,0); + end; + DrawPicture(Tut[171],Tut[171]^^.picFrame);{tutplatform} + DrawPicture(Tut[168],Tut[168]^^.picFrame);{tut} + + SetOrigin(0,0); + GetDItem(myDialog[Tomb],1,j,myHandle,tRect);{erase under button} + InsetRect(tRect,-2,-2); + EraseRect(tRect); + + {frame the text box, item #2} + DrawBoldButton(WhichDialog); + GetDItem(myDialog[Tomb],2,j,myHandle,tRect); + InsetRect(tRect,-1,-1);{frame the text item} + EraseRect(tRect); + FrameRect(tRect); + + ModalDialog(Nil,itemHit); + end;{tomb} + Guard:begin {this is the protection dialog} + GetDItem(myDialog[Guard],9,theType,tHandle,itemRect); + tStr := 'disabled'; + GetDItem(myDialog[Guard],13,theType,tHandle,tRect); + SetIText(tHandle,tStr); + InsetRect(tRect,-3,-3); + GetDItem(myDialog[Guard],10,theType,tHandle,messFrame); + InsetRect(messFrame,-4,-8);{message frame} + Repeat + FrameRect(tRect);{this is around our #1keycode} + FrameRoundRect(messFrame,10,10);{around our message} + DrawBoldButton(whichDialog);{draw each time in case updates} + ModalDialog(Nil,itemHit); + Case itemHit of + 2: DisplayWhatDialog(What);{reinterate dialogs? don't work} + 4: begin + DoPrint;{print out our order form stuff} + InvalRect(myDialog[Guard]^.portRect);{redraw our Dialog} + FlushEvents(KeyDownMask,0);{case of key commands or such} + end;{4:} + 5: begin {lock the game} + {display alert... consequences} + DisplayAlert(135,i); + If i = 2 then begin {item LOCKIT} + Finished := True;{quit the game after lock!} + itemHit := 1;{will close the Guard dialog} + end; {if i = 2} + end; + 6: begin {unlock all the features?} + GetDItem(myDialog[Guard],7,theType,tHandle,fRect); + GetIText(tHandle,tStr); + SetIText(tHandle,'disabled'); + DisplayAlert(136,i);{sorry invalid keycode2} + end;{6:} + end;{case itemHit} + InvalRect(itemRect);{always redraw the Messages:} + Until(itemHit = 1);{done button} + end; + UnLock:begin {at end of Locked levels,about more levels,etc.} + tBuf := GetResource('TBUF',129);{get text from resource} + HLock(tBuf);{locked for Ptr} + TextFont(3); + TextSize(10); + TextBox(tBuf^,GetHandleSize(tBuf),myDialog[UnLock]^.portRect,teJustLeft); + HUnLock(tBuf); + DrawBoldButton(WhichDialog); + ModalDialog(Nil,itemHit); + end;{Unlock:} + Source:{source code message} + ModalDialog(Nil,itemHit); {close it no matter what was hit} + Scores:begin {hiscore stuff} + TextFont(3); + Repeat + For i := 1 to 4 do begin + TextSize(9); + {unpack 4 names stored in theHighs^.nameStr,etc. + and draw into dialog} + unNames[i] := copy(theHighs^.nameStr,((i-1) * 23)+1,23);{short} + MoveTo(7,87+16*i); + DrawString(unNames[i]);{draw all 4 names} + TextSize(12); + Moveto(180,87+16*i); + NumToString(theHighs^.level[i],tStr);{draw all 4 levels} + DrawString(tStr); + NumToString(theHighs^.score[i],tStr); + MoveTo(myDialog[Scores]^.portRect.right - + StringWidth(tStr) - 7,87+16*i); + DrawString(tStr);{draw all 4 scores} + PenPat(gray); + move(0,3); + lineto(7,90+16*i);{draw the lines in between scores} + PenPat(black); + end; + ModalDialog(Nil,itemHit); + if itemHit = 4 then begin {reset the hiscore stuff} + For i := 1 to 4 do begin + theHighs^.level[i] := 0; + theHighs^.score[i] := 0; + end; + theHighs^.nameStr := ''; + While Length(theHighs^.nameStr) < 102 do + theHighs^.nameStr := Concat(theHighs^.nameStr,' '); + ChangedResource(ScoreHandle);{write changes on quit} + InvalRect(myDialog[Scores]^.portRect);{modalDialog will redraw items} + EraseRect(myDialog[Scores]^.portRect); + HiScore := 0; + DrawBoldButton(WhichDialog);{redraw after erase} + end;{if itemhit} + Until(itemhit = 1) + end;{Score} + Name:Begin {get hiscorer's name/sort into hiscore record} + GetDItem(myDialog[Name],3,theType,tHandle,tRect); + SetIText(tHandle,NameString);{put default in text box} + SelIText(myDialog[Name],3,0,maxint);{select the text} + + Repeat + ModalDialog(Nil,itemHit);{Modal handles Update events for dialog} + Until(itemHit = 1);{ok button} + + GetIText(tHandle,NameString);{must be happy with text..so use it} + + For i := 1 to 4 do {copy packed strings into temp full strings} + unNames[i] := copy(theHighs^.nameStr,((i-1) * 23)+1,23); + {sort the New LoScore} + index := 4;{we know that score is more than 4 already} + For i := 3 Downto 1 do + If Score > theHighs^.score[i] then index := i;{how how is it,#1?} + + i := 4; + While (i > index) do begin {switch old for new scores} + unNames[i] := unNames[i-1]; + theHighs^.score[i] := theHighs^.score[i-1]; + theHighs^.level[i] := theHighs^.level[i-1]; + dec(i); + end;{While} + theHighs^.score[index] := Score;{make 'index' score = new high} + theHighs^.level[index] := Level; + unNames[index] := copy(NameString,1,23);{just first 23 chars of name} + + theHighs^.namestr := ''; + For i := 1 to 4 do begin {repack the strings into namestr} + theHighs^.nameStr := concat(theHighs^.nameStr,unNames[i]); + While Length(theHighs^.nameStr)< (i*23) do + theHighs^.nameStr := concat(theHighs^.nameStr,' '); + end;{for i} + + ChangedResource(ScoreHandle);{flag, write out changes on Quit} + end;{Name:} + SetSpeed:Begin + Repeat + ModalDialog(Nil,itemHit); + Case itemHit of + 2,3,4: SetControlValue(itemHit);{our radio controls} + end;{case itemhit} + Until(itemHit = 1); + end;{SetSpeed} + end;{Case WhichDialog} + + HideWindow(myDialog[WhichDialog]); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} +end; + +procedure GetIndRect(Var theRect:Rect;rectListID:integer;index:integer); +{similar to GetIndString RomCall for use with 'nrct' resource type} +Begin + {make sure we've got a copy of resource in memory} + RListHand := nrctHandle(GetResource('nrct',rectListID)); + If (index > RListHand^^.num) or (RListHand = Nil) {watch out for errors} + then SetRect(theRect,0,0,0,0) + else + theRect := RListHand^^.theRects[index];{copy the rect...} +End; + +procedure CreateStrings;{messages and hints} +Begin + For i := 1 to 4 do begin + GetIndString(Clues[i].message,1000,i);{4 messages} + GetIndRect(Clues[i].messRect,1000,i);{box for textbox messages} + GetIndRect(Clues[i].hintRect,1001,i);{box to contain hints} + end; + For i := 1 to 10 do GetIndString(ClueWords[i],1001,i);{10 cluewords} + + Clues[1].just := teJustRight;{justification for hint rect... next to glyph} + Clues[2].just := teJustCenter; + Clues[3].just := teJustLeft; + Clues[4].just := teJustCenter; +end; + +procedure NextTrgtSound(Trgt:integer);{we'll use our snake stuff to play sound} +Begin + SnakeSound^.sound1Rate := StartPitch[1] * Trgt;{*2 is one octave} + SnakeSound^.sound2Rate := StartPitch[2] * Trgt; + SnakeSound^.sound3Rate := StartPitch[3] * Trgt; + SnakeSound^.sound4Rate := StartPitch[4] * Trgt; + SnakeSound^.duration := 120;{so tone will keep playing} +end; + +procedure WriteASoundBlock(aSoundPtr:Ptr;BuffSize:Longint;LastTick:Longint); +{make changes to our SoundParmBlk and then start the sound,the lasttick pause +is included due to occassional buzz/click/or worse if sound is written +immediately.. might be because Sound Driver set ioResult to zero before +the other soundstuff was complete.. and we happened to peek at it at just +the wrong moment} +Begin + SoundParmBlk^.iobuffer := aSoundPtr;{set pointer to our sound} + SoundParmBlk^.ioreqcount := BuffSize;{size of our sound buffer} + Repeat Until(TickCount > LastTick);{we'll wait a tick before writing sound} + err := PBWrite(SoundParmBlk,true);{start the sound, asynch} +end; + +procedure SetSnakeSoundStuff;{for whichsound} +Begin + Case WhichSound of {our 2 voice FourTone sounds} + SnakeCharmer:Begin + SnakeSound^.sound1Rate := mySongPtr^.pitch[NoteCount,1]; + SnakeSound^.sound2Rate := mySongPtr^.pitch[NoteCount,2]; + ToneDelay := mySongPtr^.duration[NoteCount] + TickCount; + end; + Walk:Begin + SnakeSound^.sound1Rate := myWSongPtr^.pitch[NoteCount,1]; + SnakeSound^.sound2Rate := myWSongPtr^.pitch[NoteCount,2]; + ToneDelay := myWSongPtr^.duration[NoteCount] + TickCount; + end; + end;{case whichsound} + SnakeSound^.duration := 80;{none of our notes is over 60 ticks long} + +end; + +procedure InitialSnakeSong(BeginNote:integer);{use for 'Walk' also, just use myWSongPtr stuff} +Begin + NoteCount := BeginNote;{set to first note in our song array} + SetSnakeSoundStuff; + SnakeSound^.sound3Rate := 0;{no 3 and 4 voices sound} + SnakeSound^.sound4Rate := 0; + + If SoundOn then WriteASoundBlock(ptr(Sound[SnakeCharmer]), + SizeOf(Sound[SnakeCharmer]^),TickCount); +End;{of Procedure} + +procedure CreateSound;{i,j,k = global} +{we're writing direct to the sound driver with PBWrite...this procedure sets +up all the various buffers, Parm blocks, and such for the different +types of sounds used in this game. You can determine if a sound is finished +by checking the ioresult field of the Parmblock, if it is less than 1 +then the sound is done. In the mainloop we check it and start another sound +if the last one is done. Sometimes the Driver has changed ioresult but is not +done with the ParmBlock so writing another sound can be messed up.. buzzing, +this can be avoided by waiting for Tickcount to increment once,or by doing +a PBKillIO. We use the PBKill when we want to switch sounds before they +are complete... this avoids the problem of our program trying to write to +the Driver buffers at the same time the driver is trying to access them. +To avoid system errors always be sure to Kill sound I/O before exiting the +program!!.. and remember freeform sound slows the program by about 20%, +fourTone sound by 50%.} +Begin + myHandle := (GetResource('SONG',522));{snake charmer notes} + {HLock(myHandle);locked in resource file} + mySongPtr := SongPtr(myHandle^); + + myHandle := (GetResource('SONG',523));{'Walk like' notes} + {HLock(myHandle);locked in resource file} + myWSongPtr := WSongPtr(myHandle^); + + new(SawToothWave); {wavePtr...a SawToothWave form for Blue sound} + new(SquareWave); {wavePtr...a squarewave form for Blue sound} + for i := 0 to 127 do {build our four tone waves} + begin + SawToothWave^[i] := 2*i;{sort of a raspy sound....} + {SawToothWave^[i+128] := 0;} + SquareWave^[i] := 0; + SquareWave^[i+128] := 255;{height of Square determines Loudness} + end; + + {we'll coerce Snake to FreeForm pointer throughout the program just so we + can use WhichSound and our SoundList type later on....} + new(SnakeSynth); {my FTSynthPtr, FourTone Synthesizer} + SnakeSynth^.mode := ftMode; + + new(SnakeSound);{our fourtone sound} + SnakeSound^.sound1Phase := 0; + SnakeSound^.sound2Phase := 128; + SnakeSound^.sound3Phase := 128; {out of phase just for fun} + SnakeSound^.sound4Phase := 0; + SnakeSound^.sound1Wave := SawToothWave; + SnakeSound^.sound2Wave := SquareWave; + SnakeSound^.sound3Wave := SawToothWave; + SnakeSound^.sound4Wave := SquareWave; + + {must make sndRec point to our Sound} + SnakeSynth^.sndRec := SnakeSound; + Sound[SnakeCharmer] := FFSynthPtr(SnakeSynth);{its just a pointer.. so coerce it} + + {note: We're using Snake FourTone stuff also for both Walk and Trgt sounds} + + Buff[GunShot] := 376;{size of all the freeform buffers,keep multiples of 370} + Buff[GoodHit] := 1486;{!!! remember to set range below} + Buff[BadHit] := 746; + Buff[Miss] := 746; + Buff[BonusSnd] := 746; + + {now create all the FreeForm sounds....} + For WhichSound := GunShot to BonusSnd do begin {they all have these in common} + Sound[WhichSound] := FFSynthPtr(NewPtr(Buff[WhichSound]));{sound block} + Sound[WhichSound]^.mode := ffMode; + {knock off 6 bytes for mode & count.. plus 1 for switch to Zero base} + Buff[WhichSound] := Buff[WhichSound] - 7;{size of waveBytes} + end; + + {GunShot Stuff, played when a shot is fired, mousedown} + Sound[GunShot]^.count := FixRatio(1,6);{see the Sound Driver stuff} + j := 0; + While j<= Buff[GunShot] do Begin + If j > 30 then i := abs(Random) div (j*3) {just fading noise} + else begin + If (j mod 5) = 0 then i := 255;{SquareWave with Period of 10} + If (j mod 10) = 0 then i := 0; + end;{else} + Sound[GunShot]^.WaveBytes[j] := i; {fill up the buffer} + inc(j); + end; { of while} + + {GoodHit Stuff, sound played if valid target hit} + {set .count field by target value} + j := 0; + While j<= Buff[GoodHit] do Begin + If j > 370 then begin + If (j mod 64) = 0 then i := 255 - (j div 12); + If (j mod 128) = 0 then i := (j div 12); + end + else begin + If (j mod 64) = 0 then + If (j mod 128) = 0 then i := 0 else i := 255; + end;{else} + Sound[GoodHit]^.WaveBytes[j] := i; {fill up the buffer} + inc(j); + end; { of while} + +{BadHit Stuff, hit a target but not a valid one} + Sound[BadHit]^.count := FixRatio(1,4); + j := 0; + While j<= Buff[BadHit] do Begin + if i < 127 then inc(i) else i := 0;{sawtooth} + Sound[BadHit]^.WaveBytes[j] := i; {fill up the buffer} + inc(j); + end; { of while} + + {Miss Stuff, missed everything! splat} + Sound[Miss]^.count := FixRatio(1,2);{see the Sound Driver stuff} + j := 0; + i := 0; + While j<= Buff[Miss] do Begin + if i < 255 then inc(i) else i := 0; + Sound[Miss]^.WaveBytes[j] := i; + inc(j); + end; { of while} + + {BonusSnd Stuff, sound played if bonus target underway} + Sound[BonusSnd]^.count := FixRatio(1,4); + j := 0; + While j<= Buff[BonusSnd] do Begin + If (j mod 37) = 0 then + If (j mod 74) = 0 then i := 0 else i := 63; + Sound[BonusSnd]^.WaveBytes[j] := i; {fill up the buffer} + inc(j); + end; { of while} + + {create our Sound Parameter block for use with PBWrite} + new(SoundParmBlk);{create record the Ptr will point to!} + with SoundParmBlk^ do begin {see Apple tech note 19, PBWrite vs. StartSound} + iocompletion := nil; + iorefnum := -4; + {we'll poke in the pointers and size stuff in WriteASoundBlock} + ioresult := 0; {will Start sound ,MainEventLoop} + end; {of with} +end; + +procedure PauseThisGame(index:integer); {called if a keydown during game} +var + i: integer; +Begin + err := PBKillIO(SoundParmBlk,false);{kill sound} + PauseStatus := index;{determines update drawing} + + {copy current screen to UpDateMap, so update events are redrawn correctly} + CopyBits(myWindow^.portBits,UpDateMap,UpDateRect, + UpDateMap.bounds,srcCopy,nil); + + ShowControl(ResumeButton); + ShowControl(EndButton); + SetCursorMenus(false); +End; + +procedure NewHiScore;{check for new hiscore/update HiScore??} +Begin + If Score > HiScore then begin + HiScore := Score; + DrawStatus(4,HiScore); + end; +End; + +procedure MoveHourGlass(Horz,Vert:integer);{move for timescore after each level} +Begin + OffSetRect(Hour,Horz,Vert); + OffSetRect(TopSand,Horz,Vert); + OffSetRgn(TopSandRgn,Horz,Vert); + OffSetRect(BotSand,Horz,Vert); + OffSetRgn(BotSandRgn,Horz,Vert); + + TrickleTop.h := TrickleTop.h + Horz; + TrickleBot.h := TrickleTop.h; + TrickTopR.h := TrickleTop.h + 1;{right side line trickle} + TrickBotR.h := TrickTopR.h; + TrickleTop.v := BotSand.top; + TrickleBot.v :=TrickleBot.v + Vert; + TrickBotR.v := TrickleBot.v; +end; + +procedure BuildValidNow; +{list of Valids which have targets remaining, we step thru NowValid to find +the next valid target to display in StartOne(), so we have a good mix of +all the remaining valid targets in the windows, for instance if we have 6 valid +targets numbered 1..6 but the user has completed numbers 2,5 and 6 (they +have no targets remaining) then after this procedure NowValid will contain +the following... 1,3,4,0,0,0 NowNdx is inc'd until NowValid = 0 +then its reset to 1} +Var + i:integer; +Begin + For i := 1 to 6 do NowValid[i] := 0;{initialize all to zero} + NowNdx := 1; + For i := 1 to LastValid do + If Valid[i].remain > 0 then begin {has targets remaining so Valid} + NowValid[NowNdx] := i; + inc(NowNdx);{inc to point to next now} + end; + NowNdx := 1;{reset to first in list} +End; + +procedure BuildValidTargets; +{create list of targets for scorebox & initial them} +Var + i,j:integer; +Begin + randSeed := TickCount;{longint seed Random Function} + {our font bitimage has shapes for chars 33 thru 129} + Valid[1].which := trunc(abs(Random) / MaxInteger * 96) + 33;{pick one} + For i := 2 to LastValid do begin + Repeat + Valid[i].which := trunc(abs(Random) / MaxInteger * 96) + 33;{33 to 129} + tFlag := True; + For j := 1 to i-1 do {check against ones already picked} + If (Valid[i].which = Valid[j].which) then tFlag := False; + Until(tFlag = True); + end;{for i} + If LastValid < 6 then begin {create #of each target to be hit} + For i := 1 to LastValid do Valid[i].remain := LastValid+1-i;{4,3,2,1} + end + else For i := 1 to LastValid do Valid[i].remain := Level+4-i; + BuildValidNow; +End; + +procedure CreatePictures; {get PICT's from resource file} +Begin + {note: we've either created our pics in FullPaint or by doing 'openPicture' + type stuff. In the case of FullPaint we 'copy' the picture from a document + where the pic is located in same position relative to upperleft of page + as it will be relative to upperleft of window origin.. this way when we + draw the picture in its picFrame it appears in proper place in window, to + move a picture.. just set a rect = to picframe and OffsetRect it, then + draw the picture into the rect instead of the picframe} + + LeftSide := GetPicture(138); + RightSide := GetPicture(139);{of gallery walls} + BackSide := GetPicture(140); + + ScoreFrame := GetPicture(142); + ScoreFrameRect := ScoreFrame^^.picFrame; + ScoreNum := GetPicture(141); + ScoreNumRect := ScoreNum^^.picFrame; + OffSetRect(ScoreFrameRect,8,3); + OffSetRect(ScoreNumRect,ScoreFrameRect.left +2-ScoreNumRect.left, + ScoreFrameRect.top + 4-ScoreNumRect.top); + + StatPic := GetPicture(145);{status display box} + {locate all the stuff for drawing our scores, etc. into Status} + StatList[1].centerPt := StatPic^^.picFrame.topleft; + StatList[1].centerPt.h := StatList[1].centerPt.h + 44; + StatList[1].centerPt.v := StatList[1].centerPt.v + 26; + StatList[2].centerPt.h := StatList[1].centerPt.h + 84; + StatList[2].centerPt.v := StatList[1].centerPt.v; + StatList[3].centerPt.h := StatList[1].centerPt.h; + StatList[3].centerPt.v := StatList[1].centerPt.v + 27; + StatList[4].centerPt.h := StatList[2].centerPt.h; + StatList[4].centerPt.v := StatList[3].centerPt.v; + For i := 1 to 4 do SetRect(StatList[i].blankRect, + StatList[i].centerPt.h - 38,StatList[i].centerPt.v - 13, + StatList[i].centerPt.h + 38,StatList[i].centerPt.v); + + For i := 1 to 4 do Clues[i].glyphPic := GetPicture(160 + i);{load glyphs} + ClueShadow := GetPicture(165); + For i := 168 to 171 do Tut[i] := GetPicture(i);{load tut pics} + + TarWindPic := GetPicture(166); + + BotSandRgn := RgnHandle(GetResource('RGN ',356));{mask sand into glass} + TopSandRgn := RgnHandle(GetResource('RGN ',357)); + {these rgns are a tracing of the outline of the top and bottom half of + the Hourglass. We wrote a little utility that can import a picture, trace + its outline into a RgnHandle then save the new rgn into our 'RGN ' resource, + the rgn resource can then be used to mask drawing done by CopyBits().. both + our GunSite and the sand in the hourGlass are examples of this. Some truly + stunning effects can be created with Rgn masks and CopyBits()!} +end; + +{bmap Type resources contain a Bitmap 'and' bitimage. we created our bmaps by +using PtrAndHand to stick our bitimage (already containing pictures +shapes,etc.) onto the end of its bitmap and store as bmap... +Getbmap() loads the resource, locks it down, and makes 'theMap' point to +the BitImage. In some cases there is no bitimage (empty) so we create a +ptr in the conventional way} +procedure Getbmap(Var theMap:Bitmap;resID:integer); +var + myBmap:BmapHand; +Begin + myBMap := BmapHand(GetResource('bmap',resID)); + {HLock(Handle(myBMap));locked in resource file} + theMap.rowBytes := myBMap^^.rowBytes; + theMap.bounds := myBMap^^.bounds; + theMap.baseAddr := Ptr(@myBMap^^.bitStart[1]);{point to bitimage} +End; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +Begin + {offScreen contains Gun/explode/spot/hourglass/sand and chariot pics} + Getbmap(OffScreen,128);{bmaps come with complete BitImages!} + Getbmap(BonusMap,129); + Getbmap(UpDateMap,131); + {we saved no bitimage for UpDateMap, so create it} + UpDateMap.baseAddr := QDPtr(NewPtr((UpdateMap.bounds.bottom- + UpdateMap.bounds.top)*UpdateMap.rowBytes)); + + For i := 1 to 3 do begin + GetbMap(Wings[i],131+i); + Wings[i].baseAddr := QDPtr(NewPtr((Wings[i].bounds.bottom- + Wings[i].bounds.top)*Wings[i].rowBytes)); + end; +End; + +procedure CreateCairoStuff; +{we're only using bitimage and locate table of Cairo font, to access the +'parts' of the Font we did some pointer math and made copies of only +bitImage and locate table, then saved the new records in our 'MYCF' resource, +the bitImage contains shapes for chars 33 thru 129.. see CairoFont.pro file +for more information on how we accessed the Cairo Font.} +var + trect,bRect:Rect; + i:integer; +Begin + {load font Character image and locate table} + myHandle := GetResource('MYCF',128); + {HLock(myHandle);locked in resource} + myCFPtr := CFPtr(myHandle^);{lock it down, used as ptr block} + + FontMap.baseAddr := QDPtr(myCFPtr);{make FontMap point to bit image} + FontMap.rowbytes := 210;{row width in bytes, must be even number} + SetRect(FontMap.bounds,0,0,1656,24);{looked at with ResEdit}; +End; + +procedure InitialHourGlass;{position hourglass stuff for start of game} +Begin + OffSetRect(OBSand,0,OffBotSand.top-OBSand.top);{at top, will move down} + OffSetRect(OTSand,0,OffTopSand.bottom-OTSand.bottom);{at bottom, will move up} + TrickleBot.v := BotSand.bottom-1;{trickle to top of pile} +End; + +procedure SizeLocateSpots(offset:integer); +Begin + For i := 5 to 6 do begin + OffSpot[i].bottom := OffSpot[i].bottom - (5 + offset); + SpotRect[i] := OffSpot[i];{size SpotRect} + end; + OffSetRect(OffSpot[5],0,OffSpotTarget.top+offset-OffSpot[5].top); + OffSetRect(OffSpot[6],0,OffSpotTarget.bottom-offset-OffSpot[6].bottom); +End; + +procedure InitialSpotTargets; +Var + i:integer; +Begin + {locate and size all the Spot target rectangles, we're doing some + shifting/sizing of OffSpots to minimize the work of CopyBits()} + For i := 5 to 6 do OffSpot[i] := OffSpotTarget; + Case SpotSpeed[5] of + 2: SizeLocateSpots(2); + 4: SizeLocateSpots(0); + End;{Case Spotspeed} + + OffSetRect(SpotRect[5],TarWindow[5].left+7-SpotRect[5].left, + TarWindow[5].top-SpotRect[5].top);{top moving down} + OffSetRect(SpotRect[6],TarWindow[6].left+7-SpotRect[6].left, + TarWindow[6].bottom-SpotRect[6].bottom);{bottom moving up} +End; + +procedure OneTimeGameStuff; +Begin + {when we created the bmaps we saved the rects we'd already calculated + in a 'nrct' resource so we wouldn't have to rewrite the code} + GetIndRect(OffGun[0],2000,1);{Rects for shapes in OffScreen bmap} + GunRect := OffGun[0];{size} + GetIndRect(OffGun[1],2000,2); + GetIndRect(OffExplode,2000,3); + ExplodeRect := OffExplode; + GetIndRect(OffSpotTarget,2000,4); + GetIndRect(OffHour,2000,5); + GetIndRect(OffBotSand,2000,6); + GetIndRect(OffTopSand,2000,7); + + {find all individual bonus shapes in the map... 6 in a row, 2 rows} + with BonusMap.bounds do SetRect(tRect,left,top,left+37,top+23);{size of one} + For i := 1 to 6 do begin + OffBonus[i,1] := tRect;{top row} + OffSetRect(tRect,0,23); + OffBonus[i,2] := tRect;{bottom row} + OffSetRect(tRect,37,-23); + end; + BonusRect := OffBonus[1,1];{size BonusRect} + + GetIndPattern(myPattern,sysPatListID,25);{see i-473} + GetIndPattern(myDiagPat,sysPatListID,26);{used to disable valid} + + For i := 1 to 3 do begin {size 3 scrolling windows} + SetRect(TarWindow[i],125,39 + 36*i,125+251,66 + 36*i);{font - 1} + OffPiece[i] := TarWindow[i];{establish Height of OffPiece} + Piece[i] := TarWindow[i];{establish Height of Piece} + OffSetRect(OffPiece[i],0,1-OffPiece[i].top);{ready for startone} + end; + + SetRect(SiteRect,0,0,25,25);{circular site in synch with gun} + + For i := 0 to 1 do begin + OffSite[i] := SiteRect;{two offSites for two offguns} + OffSetRect(OffSite[i],OffGun[i].left+ 4 -OffSite[i].left, + OffGun[i].top+ 13-OffSite[i].top); + SiteRgn[i] := NewRgn;{create the site regions for use with CopyBits} + OpenRgn; + FrameOval(OffSite[i]);{used to mask background drawn into Offsite} + CloseRgn(SiteRgn[i]); + end; + + SetRect(TarWindow[5],0,0,37,105);{top is even,bottom odd due to pattern} + OffSetRect(TarWindow[5],TarWindow[1].left-TarWindow[5].right-10, + TarWindow[1].top-TarWindow[5].top-5);{locate to left of others} + TarWindow[6] := TarWindow[5];{6 is exactly like 5,except on right side} + OffSetRect(TarWindow[6],TarWindow[1].right-TarWindow[5].left+10,0);{right} + For i := 5 to 6 do begin + TopSpot[i] := TarWindow[i].top + 4;{used in SpotStatus for speed} + BotSpot[i] := TarWindow[i].bottom - 4; + end; + + {locate and size #4/bottom window.. bonus targets} + TarWindow[4].top := TarWindow[3].top + 38; + TarWindow[4].bottom := TarWindow[3].bottom + 38; + TarWindow[4].left := TarWindow[5].left +1; + TarWindow[4].right := TarWindow[6].right -1; + OffSetRect(BonusRect,TarWindow[4].left-BonusRect.right, + TarWindow[4].top-BonusRect.top); + + TarWindRgn := NewRgn;{region - frame of bonus target window} + OpenRgn; + FrameRect(TarWindow[4]);{will mask the bonuspics} + CloseRgn(TarWindRgn); + + Hour := OffHour;{hourglass stuff} + OffSetRect(Hour,280-Hour.left,3-Hour.top);{position in scorebox} + TopSand := TopSandRgn^^.rgnBBox;{size of rgn or of sand pic????} + OTSand := TopSand; + OffSetRect(TopSand,Hour.left-TopSand.left,Hour.top+4-TopSand.top); + OffSetRect(TopSand,(Hour.right-TopSand.right) div 2,0);{center it} + OffSetRgn(TopSandRgn,TopSand.left -TopSandRgn^^.rgnBBox.left, + TopSand.top-TopSandRgn^^.rgnBBox.top); + + BotSand := BotSandRgn^^.rgnBBox;{size of rgn or of sand pic????} + OBSand := BotSand; + OffSetRect(BotSand,Hour.left -BotSand.left,Hour.bottom-4-BotSand.bottom); + OffSetRect(BotSand,(Hour.right-BotSand.right) div 2,0);{center it} + OffSetRgn(BotSandRgn,BotSand.left -BotSandRgn^^.rgnBBox.left, + BotSand.bottom-BotSandRgn^^.rgnBBox.bottom); + + OffSetRect(OBSand,OffBotSand.left-OBSand.left, + OffBotSand.bottom-OBSand.bottom); + OffSetRect(OTSand,OffTopSand.left-OTSand.left, + OffTopSand.top-OTSand.top); + + TrickleTop.h := Hour.left + ((Hour.right-Hour.left)div 2)-1; + TrickleBot.h := TrickleTop.h; + TrickTopR.h := TrickleTop.h + 1;{right side line trickle} + TrickBotR.h := TrickTopR.h; + TrickleTop.v := BotSand.top; + + {all the Score box stuff} + tPt.h := ScoreFrameRect.left + 2 + 22;{center of first box} + tPt.v := ScoreFrameRect.top + 16;{to top of draw area} + j := ScoreFrameRect.bottom - 5;{to bottom of blank area} + For i := 1 to 6 do begin + Valid[i].centerPt := tPt; + Valid[i].remainPt.h := tPt.h; + Valid[i].remainPt.v := j; + SetRect(Valid[i].blankRect,tPt.h-20,tPt.v +28,tPt.h+20,j); + tPt.h := tPt.h + 44; + end;{for i} + Valid[6].points := 500;{scoring for our valid targets} + Valid[5].points := 100; + Valid[4].points := 50; + Valid[3].points := 20; + Valid[2].points := 10; + Valid[1].points := 5; + + {set up all the Timebonus stuff.. TBone topleft := 179,85} + SetRect(TBone.frame,179,85,328,154); + SetRect(TBone.numBox,267,110,319,130); + SetPt(TBone.hourMove,-93,88); + SetRect(TBone.arrowSource,myCFPtr^.locate[104],FontMap.bounds.top, + myCFPtr^.locate[105],FontMap.bounds.bottom); + TBone.arrowDest := TBone.arrowSource; + OffsetRect(TBone.arrowDest,234-TBone.arrowDest.left, + 108-TBone.arrowDest.top); + + StartPitch[1] := 29316;{Long integers for notes,played when targets} + StartPitch[2] := 78264;{are displayed,doubling long integers is an octive} + StartPitch[3] := 98607; + StartPitch[4] := 117264; + + {locate gun and site} + OffSetRect(GunRect,100-GunRect.left, + 300-GunRect.bottom); + OffSetRect(SiteRect,GunRect.left+ 4-SiteRect.left, + GunRect.top+ 13-SiteRect.top);{synch Site with gun} + + SetRect(MouseRect,LeftSide^^.picFrame.right,ScoreFrameRect.bottom, + RightSide^^.picFrame.left - 32,GunRect.top - 32); + GunLimit := MouseRect;{GunLimit is fixed... MouseRect will slide with mouse} + SetRect(ShadowRect,LeftSide^^.picFrame.left,ScoreFrameRect.bottom+3, + RightSide^^.picFrame.right,297); + + {shadow rect extends down to edge of front counter in booth} + RedrawRect := ShadowRect; + RedrawRect.bottom := GunRect.bottom;{redraw extends farther,to bottom of gun} + + SetRect(ClueErase,LeftSide^^.picFrame.right+1,64, + RightSide^^.picFrame.left-1,219);{erase tarwindows for Clues} + + UpDateRect := ClueErase;{update area to redraw windows from CopyBits copy} + {our UpDateMap.bounds is equal to UpDateRect} + + FetchChar := FontMap.bounds;{Used to speed up StartOne, top/bottom coords.} + + For i := 1 to 4 do {get handle to text items in clue dialog} + GetDItem(myDialog[ClueDial],5 + i,j,Clues[i].dialHand,tRect); + + RandSeed := TickCount; + ClueNdx := (abs(Random) div 3277) + 1;{ndx to cluewords,1-10} + tRgn := NewRgn;{used tRgn in the ScrollRect call} + ShotFired := False;{flag a shot fired... mousedown} + NameString := ''; + GameUnderWay := False;{game is not under way on start up} + PauseStatus := 0;{no pause} + HiScore := theHighs^.score[4];{lowest of the hiscores} + Score := 0; + Level := 0; + MissCount := 0;{number of misses} + CursorNdx := 1;{point to first of three cursors} + LastValid := 6;{we'll draw 6 targets on start up} + Level := 4; {for BuildValidTargets to work} + BuildValidTargets;{so that update will work correctly} +End; + +procedure StaggerOne(Which:integer); +{fill wings with pattern and OffSet, so our targets aren't lined up vertically} +Begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(Wings[Which]); { our new BitMap } + + FillRect(Wings[Which].bounds,myPattern);{fill with horz.lines} + + si := abs(Random div 1368) + 10;{10 to 33,random offset for start} + If Direction[Which] > 0 then begin + OffPiece[Which].right := Wings[Which].bounds.right - si; + OffPiece[Which].left := OffPiece[Which].right - Speed[Which]; + end + Else begin + OffPiece[Which].left := si; + OffPiece[Which].right := si + Speed[Which]; + end; + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure StartOne(Which:integer); {which TarWindow[1..3]} +{get a char from either valid targets or not-valid targets and draw it into +a Wings Bitmap and initial stuff for scrolling it onto the screen via +OffPiece/Piece and CopyBits()} +Begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(Wings[Which]); { our new BitMap } + + FillRect(Wings[Which].bounds,myPattern);{erase previous with horiz.line patt} + {FetchChar := FontMap.bounds;}{bounds rect of FontMap bitmap} + + {find which char to draw... one from Valid targets or not} + If ((abs(Random) div 3277) > 5) and not(ValidDone) then begin {get a Valid.which} + si := Valid[NowValid[NowNdx]].which;{NowValid points to Valid with remain} + If NowNdx < 6 then begin + inc(NowNdx); + If NowValid[NowNdx] = 0 then NowNdx := 1;{reset to first} + end{if NowNdx} + else NowNdx := 1;{6 is last so reset to first} + end {if ((abs...} + Else {put up one that's not on our list} + Repeat {find i not in our Valid list} + si := trunc(abs(Random) / MaxInteger * 96) + 33;{si := 33 to 129} + tFlag := True; + For sj := 1 to LastValid do + If (si = Valid[sj].which) then tFlag := False;{in our list} + Until(tFlag = True); + + FetchChar.left := myCFPtr^.locate[si];{locate[i] is offset in Bitimage} + FetchChar.right := myCFPtr^.locate[si+1];{FetchChar frames Char image in Fontmap} + dRect := FetchChar; + + {dRect.top := 0;}{FetchChar.top is already 0} + dRect.bottom := 29; + + {expand dRect make white border/frame around target} + If Direction[Which] > 0 then begin {offset to rightside} + {resize dRect to frame target and offset to proper side} + dRect.left := 42 - (FetchChar.right - FetchChar.left);{grow by 8 pixels} + dRect.right := 50;{Wings[Which].bounds.right} + OffPiece[Which].right := Wings[Which].bounds.right; + {'Speed' := width of OffPiece/Piece and vice versa} + OffPiece[Which].left := OffPiece[Which].right - Speed[Which]; + end + Else begin {offset to leftside} + dRect.left := 0; + dRect.right := 8 + FetchChar.right - FetchChar.left; + OffPiece[Which].left := 0; + OffPiece[Which].right := Speed[Which];{size based on speed} + end; + + EraseRect(dRect);{to erase pattern...inside target area} + FrameRect(dRect);{frame around target} + + {TarList stuff...install this new target in TarList for this window} + TarList[Which,NxtTar[Which]].theChar := si; + TarList[Which,NxtTar[Which]].head := PixOff[Which] - Speed[Which]; + TarList[Which,NxtTar[Which]].wide := dRect.right - dRect.left; + + If NxtTar[Which] < LastTarget then inc(NxtTar[Which]) + Else NxtTar[Which] := 1;{index of TarList} + + {now shrink relocated dRect back to size of FetchChar before copybits} + dRect.top := dRect.top + 3; + dRect.bottom := dRect.bottom - 2; + dRect.left := dRect.left + 4; + dRect.right := dRect.right - 4; + CopyBits(FontMap,Wings[Which],FetchChar,dRect,srcCopy,nil);{char into target} + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure InitialBonus;{set up bonus for drawing..movement} +Begin + BonusUnderWay := True; + BonusRect.left := TarWindow[4].left - (BonusRect.right-BonusRect.left); + BonusRect.right := TarWindow[4].left;{move BonusRect to left side window} + BonusSpeed := 4;{max right, pixels per loop} + BonusLoops := 20;{20 * 4 = 80 pixels....} + WhichSound := BonusSnd;{will start the BonusSnd} +End;{of procedure} + +Procedure InitialAnimation; +Var + i,j:integer; + myErase:Rect; +Begin + BuildValidTargets;{pick out Cairo chars for targets,etc.} + + {draw the scorebox} + EraseRect(ScoreFrameRect);{erase old} + DrawPicture(ScoreFrame,ScoreFrameRect); + DrawPicture(ScoreNum,ScoreNumRect); + + If SoundOn then begin + NextTrgtSound(1);{initialize pitches for sound during drawing} + WriteASoundBlock(ptr(Sound[SnakeCharmer]), + SizeOf(Sound[SnakeCharmer]^),TickCount);{use snake for target} + end;{if soundon} + {draw the targets into the scoreBox, for each level} + SpeedTicks := 1;{for our delay} + sRect := FontMap.bounds;{bounds rect of FontMap bitmap,top/bottom constant} + For i := 1 to LastValid do begin + NextTrgtSound(i);{change pitch for each box} + with Valid[i].centerPt do SetRect(myErase,h-21,v,h+21,v+24);{erase area} + tRect.top := Valid[i].centerPt.v; + tRect.bottom := tRect.top + 24; + For j := 32 to Valid[i].which do begin + SystemTask; + If SpeedTrapOn then Delay(SpeedTicks,aTick);{slow down for MacII's} + SizeRectsForBox(j,i);{sRect and tRect} + EraseRect(myErase);{erase the previous} + CopyBits(FontMap,myWindow^.portBits,sRect,tRect,srcCopy,nil); + end;{for j := 32} + DrawRemainIntoScoreBox(i);{# remaining, draw just below target} + end; + + err := PBKillIO(SoundParmBlk,false);{kill sound} + + randSeed := TickCount; + For i := 1 to 3 do begin {initilize for each of scrolling windows} + For j := 1 to LastTarget do TarList[i,j].theChar := 0;{no targets} + NxtTar[i] := 1;{point to first target} + + PixOff[i] := -32766;{will determine offsets to targets in list} + If odd(Random) then Direction[i] := 1 else Direction[i] := -1; + Vector[i] := Direction[i] * Speed[i];{used to speed up animateoneloop} + {locate Piece} + If Direction[i] > 0 then begin {moving right, Piece is on left side} + Piece[i].left := TarWindow[i].left; + Piece[i].right := Piece[i].left + Speed[i]; + end + else begin + Piece[i].right := TarWindow[i].right;{Piece is on right side} + Piece[i].left := Piece[i].right - Speed[i]; + end; + end; + + StartOne(1);{start a target in top window} + StaggerOne(2);{stagger wings, so targets out of synch} + StaggerOne(3); + + Counter := 1;{number of loops before sand in hourglass moves} + + InitialSpotTargets; + ValidDone := False; + MissCount := 0; + DrawStatus(3,MissCount); + BonusNdx := 0;{will be inc'd by first bonus to point to first} + BonusShape := 1;{point to first shape} + If SoundOn then begin {start the Walk song just before start of level} + WhichSound := Walk; + InitialSnakeSong(57);{start the walk song...just the last bit...} + Repeat + SystemTask; + If (TickCount > ToneDelay) then begin {start another note} + inc(NoteCount); + SetSnakeSoundStuff;{get next sound,etc.} + end; {if TickCount} + Until(NoteCount = myWSongPtr^.noteCount);{our last walk note} + err := PBKillIO(SoundParmBlk,false); + WhichSound := Silence; + end;{if soundOn} + InitialHourGlass;{position and draw hourglass} + TrickleLeft := True; + CopyBits(OffScreen,myWindow^.portBits,OffHour, + Hour,srcCopy,nil); + CopyBits(OffScreen,myWindow^.portBits,OTSand, + TopSand,srcCopy,TopSandRgn); +end; + +procedure BeginTheGame; +Begin + SetCursorMenus(True);{do all the cursor and menus stuff} + DisableItem(myMenus[2],1);{disable 'begin' until end of game} + ClipRect(RedrawRect);{so drawing will only affect RedrawREct} + EraseRect(RedrawRect);{erase the shadow in booth} + DrawWindowContents(myWindow);{redraw shaded area (gameunderWay)} + ClipRect(myWindow^.portRect);{reset clipRgn to whole window} + + For i := 1 to 4 do begin {list of clues for this game} + Clues[i].hint := ClueWords[ClueNdx]; + If ClueNdx < 10 then inc(ClueNdx) else ClueNdx := 1; + end; + + Score := 0; + DrawStatus(2,Score); + DrawStatus(4,HiScore); + Level := 1; + DrawStatus(1,Level); + LastValid := 4;{only 4 valid targets for now} + For i := 1 to 3 do Speed[i] := 1;{start out at slowest speed, 1 pix/loop} + SpotSpeed[5] := 2;{start at top moving down} + SpotSpeed[6] := -2;{start at bottom moving up} + TimeCount := 45;{# of loops for each move of hourglass} + InitialAnimation; + BonusUnderWay := False; +end; + +procedure DetectAHit;{search targets for a hit} +Begin + Hit := False; + For dj := 1 to 6 do begin {check all 6 TarWindows} + If PtInRect(TargetCenter,TarWindow[dj]) then {is hit in the Window Rect?} + Case dj of + 1,2,3:{check the three scrolling windows} + Case Direction[dj] of + 1: begin {scrolling right} + dk := TarWindow[dj].left + PixOff[dj];{locate head constant} + di := LastTarget;{search TarList in reverse} + Repeat + If TarList[dj,di].theChar <> 0 then + If TargetCenter.h < (dk - TarList[dj,di].head) then + if TargetCenter.h > (dk - TarList[dj,di].head - + TarList[dj,di].wide) then begin + Hit := True; + HitWhich := di;{which target in List is hit} + HitWindow := dj;{which window} + end; + dec(di); + Until((di = 0) or (Hit = True)); + end;{case 1:} + -1: begin {scrolling left} + dk := TarWindow[dj].right - PixOff[dj];{Locate head constant} + di := LastTarget; + Repeat + If TarList[dj,di].theChar <> 0 then + If TargetCenter.h > (dk + TarList[dj,di].head) then + if TargetCenter.h < (dk + TarList[dj,di].head + + TarList[dj,di].wide) then begin + Hit := True; + HitWhich := di;{which target in List is hit} + HitWindow := dj;{which window} + end; + dec(di); + Until((di = 0) or (Hit = True)); + end;{Case -1:} + end;{Case Direction} + 4: If BonusUnderWay then begin {this is bonus window} + tRect := BonusRect; + tRect.left := tRect.left + 4; + tRect.right := tRect.right - 4;{shrink the rect border for hittest} + If PtInRect(TargetCenter,tRect) then begin + Hit := True; + HitWindow := dj; + WhichSound := Silence;{kill bonus sound} + end;{if PtInRect} + end;{ If BonusUnderWay } + 5,6: begin + tRect := SpotRect[dj]; + tRect.top := tRect.top + abs(SpotSpeed[dj]); + tRect.right := tRect.right - abs(SpotSpeed[dj]);{shrink the rect for hittest} + If PtInRect(TargetCenter,tRect) then begin + Hit := True; + HitWindow := dj; + end;{if PtInRect} + end; {these are spot windows} + end;{Case dj} + end;{for dj:= 1} + If not(Hit) then begin {hit will be true if a hit was detected} + + If SoundOn then begin {play miss sound} + err := PBKillIO(SoundParmBlk,false);{kill current sound} + WriteASoundBlock(ptr(Sound[Miss]),Buff[Miss],TickCount); + end;{if soundOn} + + inc(MissCount); + DrawStatus(3,MissCount);{draw in new missCount} + end;{if not(Hit)} +End; + +procedure EraseTargetInTarWindow(Window,Index:integer); +{remove a target from a window... watch out for targets on window borders} +Begin + {erase the target in window} + tRect.top := TarWindow[Window].top; + tRect.bottom := TarWindow[Window].bottom; + If Direction[Window] > 0 then begin{moving right} + tRect.right := TarWindow[Window].left + PixOff[Window] + - TarList[Window,Index].head; + tRect.left := tRect.right - TarList[Window,Index].wide; + {in case the target is not fully in the window yet} + If tRect.left < TarWindow[Window].left then begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(Wings[Window]); { our new BitMap } + FillRect(Wings[Window].bounds,myPattern); + SetPortBits(OldBits); {restore old bitmap} + + tRect.left := TarWindow[Window].left;{limit to left} + end{if tRect.left} + else If tRect.right > TarWindow[Window].right then + tRect.right := TarWindow[Window].right; + end + Else begin + tRect.left := TarWindow[Window].right - + (PixOff[Window] - TarList[Window,Index].head); + tRect.right := tRect.left + TarList[Window,Index].wide; + {in case the target is not fully in the window yet} + If tRect.right > TarWindow[Window].right then begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(Wings[Window]); { our new BitMap } + FillRect(Wings[Window].bounds,myPattern); + SetPortBits(OldBits); {restore old bitmap} + + tRect.right := TarWindow[Window].right;{limit to left} + end{if tRect.left} + else If tRect.left < TarWindow[Window].left then + tRect.left := TarWindow[Window].left; + end; + + FillRect(tRect,myPattern);{fill what is showing with myPattern} +End; + +procedure HandleAHit;{Hit = True... process it} +Begin + Case HitWindow of {one of our 6 windows} + 1,2,3:begin {scrolling windows} + EraseTargetInTarWindow(HitWindow,HitWhich);{erase the hit target} + HitAValidTarget := False;{initialize to false} + {is the target hit on our Valid list?} + dj := TarList[HitWindow,HitWhich].theChar;{this is target char we hit} + For di := 1 to LastValid do begin {is it a valid target?} + If (Valid[di].which = dj) and + (Valid[di].remain > 0) then begin {this is a valid target} + + If SoundOn then begin {start the hit sound} + err := PBKillIO(SoundParmBlk,false);{kill current sound} + Sound[GoodHit]^.count := FixRatio(di+3,2);{tone by value of target} + WriteASoundBlock(ptr(Sound[GoodHit]),Buff[GoodHit],TickCount); + end;{if soundOn} + + Score := Score + Valid[di].points;{update score} + DrawStatus(2,Score); + NewHiScore; + HitAValidTarget := True; + dec(Valid[di].remain);{one less of this target} + DrawRemainIntoScoreBox(di);{draw into scorebox} + + If Valid[di].remain = 0 then begin {disable this valid target} + If not(BonusUnderWay) then begin {start only if not already going} + InitialBonus;{start a bonus target} + BonusUnderWay := True; + If BonusNdx < 6 then inc(BonusNdx); + end + else If BonusNdx < 6 then inc(BonusNdx);{get next bonus} + Sound[BonusSnd]^.count := FixRatio(BonusNdx,10); + + BuildValidNow; {rebuild NowValid list} + + {draw disabled Validtarget in scorebox} + tRect := Valid[di].blankrect; + tRect.top := Valid[di].centerPt.v; + PenPat(myDiagPat); + PenMode(patOr);{black force black,white leave alone} + PaintRect(tRect);{use penPat and mode to fill rect,i-177} + PenNormal;{i-170} + {erase other targets of this char already on the screen} + For dk := 1 to 3 do {three tarWindows} + For dm := 1 to LastTarget do {search thru list for each window} + If TarList[dk,dm].theChar = Valid[di].which then begin + TarList[dk,dm].theChar := 0; + EraseTargetInTarWindow(dk,dm); + end;{if TarList} + + {check to see if all Valids are disabled/complete} + If NowValid[NowNdx] = 0 then ValidDone := True; + end;{if Valid.remain} + + end;{if (Valid.which} + end;{for di} + If not(HitAValidTarget) then begin {target hit was not a Valid one} + If SoundOn then begin + err := PBKillIO(SoundParmBlk,false);{kill current sound} + WriteASoundBlock(ptr(Sound[BadHit]),Buff[BadHit],TickCount); + end;{if soundOn} + inc(MissCount); + DrawStatus(3,MissCount); + end; + TarList[HitWindow,HitWhich].theChar := 0;{reset TarList} + end;{Case 1,2,3} + + 4:begin {bonus window} + If SoundOn then begin + err := PBKillIO(SoundParmBlk,false);{kill current sound} + Sound[GoodHit]^.count := FixRatio(8,2); + WriteASoundBlock(ptr(Sound[GoodHit]),Buff[GoodHit],TickCount); + end;{if soundOn} + Score := Score + (Level * 100); + DrawStatus(2,Score); + NewHiScore; + FillRect(TarWindow[4],myPattern);{erase the BonusRect} + BonusUnderWay := False; + end;{bonus window} + 5,6:begin {spot targets on sides} + If MissCount > 0 then begin {if there are misses then reduce count} + If SoundOn then begin + err := PBKillIO(SoundParmBlk,false);{kill current sound} + Sound[GoodHit]^.count := FixRatio(8,2); + WriteASoundBlock(ptr(Sound[GoodHit]),Buff[GoodHit],TickCount); + end;{if soundOn} + dec(MissCount); + DrawStatus(3,MissCount); + end;{if Misscount} + end;{spot windows on sides} + end;{Case HitWindow} +End;{procedure} + +procedure CheckSpotStatus;{move spot targets to next position} +Begin + For si := 5 to 6 do begin {both targets} + If SpotSpeed[si] < 0 then begin {moving up...} + If SpotRect[si].top > TopSpot[si] then begin {move up by speed pixels} + SpotRect[si].top := SpotRect[si].top + SpotSpeed[si]; + SpotRect[si].bottom := SpotRect[si].bottom + SpotSpeed[si]; + end + else begin {upper limit,reverse direction} + OffSpot[si].top := OffSpot[si].top + SpotSpeed[si]-1;{speed is minus} + OffSpot[si].bottom := OffSpot[si].bottom + SpotSpeed[si]-1; + SpotSpeed[si] := -SpotSpeed[si];{reverse direction} + dec(SpotRect[si].top); + dec(SpotRect[si].bottom); + end; + end {If SpotSpeed} + Else begin {moving down} + If SpotRect[si].bottom < BotSpot[si] then begin + SpotRect[si].top := SpotRect[si].top + SpotSpeed[si]; + SpotRect[si].bottom := SpotRect[si].bottom + SpotSpeed[si]; + end + else begin {at lower limit, reverse direction} + OffSpot[si].top := OffSpot[si].top + SpotSpeed[si]+1;{speed is plus} + OffSpot[si].bottom := OffSpot[si].bottom + SpotSpeed[si]+1; + SpotSpeed[si] := -SpotSpeed[si];{reverse direction} + inc(SpotRect[si].top); + inc(SpotRect[si].bottom); + end; + end; + CopyBits(OffScreen,myWindow^.portBits,OffSpot[si], + SpotRect[si],srcCopy,nil); {draw the spot} + end;{for si:=} +End; + +procedure DrawSandStuff;{hourglass... GameOver will flag end of level} +Begin + GameOver := False; + {set up for drawing on sand trickle} + If TrickleLeft then begin {draw left trickle in gray} + PenPat(Gray); + MoveTo(TrickleTop.h,TrickleTop.v); + LineTo(TrickleBot.h,TrickleBot.v);{gray left trickle} + PenPat(White); + TrickleLeft := False; + end + else begin {erase left trickle} + PenPat(White); + MoveTo(TrickleTop.h,TrickleTop.v);{white left trickle} + LineTo(TrickleBot.h,TrickleBot.v); + PenPat(Gray); + TrickleLeft := True; + end; + + {draw right trickle either gray or white (erase) set above} + MoveTo(TrickTopR.h,TrickleTop.v);{right side line,opposite of left} + LineTo(TrickBotR.h,TrickleBot.v); + PenPat(Black);{back to normal penPat} + + If Counter < TimeCount then inc(Counter) {move sand shapes yet?} + else begin {animate the hour glass} + dec(OTSand.top); + dec(OTSand.bottom);{top sand lower means off Rect goes higher} + inc(OBSand.top); + inc(OBSand.bottom);{bottom sand higher} + dec(TrickleBot.v);{shorten trickle} + CopyBits(OffScreen,myWindow^.portBits,OTSand, + TopSand,srcCopy,TopSandRgn);{draw topsand} + If OTSand.top = OffTopSand.top then begin + GameOver := True;{flag end of game/hourglass empty!!} + CopyBits(OffScreen,myWindow^.portBits,OffHour, + Hour,srcCopy,nil);{erase all the trickles} + end; + + CopyBits(OffScreen,myWindow^.portBits,OBSand, + BotSand,srcCopy,BotSandRgn);{draw bottom sand} + + Counter := 1;{reset the counter} + end; {if else} +End; + +procedure DoCommonLevelStuff; +Begin + inc(Level);{next level} + DrawStatus(1,Level); + {increase speeds etc.} + Case Level of + 3: inc(Speed[3]); + 4: begin + inc(Speed[2]);{increase scroll window speed} + inc(Speed[3]); + SpotSpeed[5] := 4;{increase spot speed} + SpotSpeed[6] := -4; + end; + end;{of case level} + + SpotSpeed[5] := abs(SpotSpeed[5]);{a must for each level,positive speed} + SpotSpeed[6] := -abs(SpotSpeed[6]);{negative speed} + + If LastValid < 6 then inc(LastValid);{increase Valid targets} + err := PBKillIO(SoundParmBlk,false);{kill sound} + InitialAnimation; + FlushEvents(mDownMask,0); +End; + +procedure EndThisGame; +var i:integer; +Begin + {draw hourglass empty} + OffSetRect(OBSand,0,OffBotSand.bottom-OBSand.bottom); + OffSetRect(OTSand,0,OffTopSand.top-OTSand.top); + + If PauseStatus = 2 then InvalRect(myWindow^.portRect) {clue, redraw window} + else begin + InvalRect(RedrawRect);{force redraw of shade area+gun} + InvalRect(Hour);{update will draw empty hourglass} + end; + PauseStatus := 0;{no pause} + + WhichSound := Silence;{not sound} + SetCursorMenus(false);{show cursor and menus} + EnableItem(myMenus[2],1);{enable begin} + If Score > theHighs^.score[4] then begin {new hiscore} + UpDateMyWindow;{update/draw window before dialog} + FlushEvents(upDatemask,0);{we just updated} + DisplayDialog(Name);{new hiScore dialog,name and sort} + end;{if score} +End; + +procedure AnimateOneLoop; +Begin + GetMouse(myMouse); + {limit movement to target area, note: by using a sliding MouseRect with a + fixed GunLimit we can get the GunRect to respond immediately to a change of + direction outside the GunLimit Rect. MouseRect simply moves with the mouse + when the mouse is outside the GunLimit Rect then we map the mouse to + MouseRect and GunLimit to get relative move inside GunLimit if there is any} + + If not(PtInRect(myMouse,MouseRect)) then begin + {a coordinate is outside mouseRect so slide mouseRect to myMouse} + If myMouse.h < MouseRect.left then begin {slide MouseRect to left} + MouseRect.right := MouseRect.right - + (MouseRect.left - myMouse.h); + MouseRect.left := myMouse.h; + end + else If myMouse.h > MouseRect.right then begin + MouseRect.left := MouseRect.left + myMouse.h - MouseRect.right; + MouseRect.right := myMouse.h; + end; + If myMouse.v < MouseRect.top then begin + MouseRect.bottom := MouseRect.bottom - + (MouseRect.top - myMouse.v); + MouseRect.top := myMouse.v; + end + else If myMouse.v > MouseRect.bottom then begin + MouseRect.top := MouseRect.top + myMouse.v - MouseRect.bottom; + MouseRect.bottom := myMouse.v; + end; + end;{if not(ptinRect)} + + {myMouse is to Mouse as site is to GunLimit,myMouse becomes Site coordinates} + MapPt(myMouse,MouseRect,GunLimit); + + If myMouse.h < GunRect.left-2 then begin {slide gun and site to left or right} + GunRect.left := GunRect.left - 3; + GunRect.right := GunRect.right - 3;{gun/site rect.top/bottom are constant} + SiteRect.left := SiteRect.left - 3; + SiteRect.right := SiteRect.right - 3; + end + Else If myMouse.h > GunRect.left+2 then begin + GunRect.left := GunRect.left + 3; + GunRect.right := GunRect.right + 3; + SiteRect.left := SiteRect.left + 3; + SiteRect.right := SiteRect.right + 3; + end; + {horizontal site always points to area directly above the gun} + {vertical siteRect moves with mouse,whatever user requests} + SiteRect.top := myMouse.v; + SiteRect.bottom := myMouse.v + 25;{25 is Site bottom - top} + + {we have two OffGuns in OffScreen, one is offset over gray by one pixel} + WhichGun := GunRect.right mod 2;{proper shape to draw over gray background} + + CheckSpotStatus;{move and draw the spot targets} + + {do all the scrolling window stuff} + For i := 1 to 3 do begin + ScrollRect(TarWindow[i],Vector[i],0,tRgn);{scroll the 3 windows} + If (OffPiece[i].left < 0) then StartOne(i) {scrolling right} + else {scrolling left} + If (OffPiece[i].right > Wings[i].bounds.right) then StartOne(i); + + CopyBits(Wings[i],myWindow^.portBits,OffPiece[i],Piece[i],srcCopy,nil); + OffPiece[i].left := OffPiece[i].left - Vector[i];{move OffPiece} + OffPiece[i].right := OffPiece[i].right - Vector[i];{Direction[i] * Speed[i]} + end;{for i := 1 to 3} + + {delay either 2 or 3 ticks, in our tests Delay returned 0.4 ticks sooner + than speedFactor requested} + If SpeedTrapOn then Delay(SpeedFactor,aTick); + + If BonusUnderWay then begin {draw all the BonusRect target stuff} + dec(BonusLoops); + If BonusLoops = 0 then begin {find new speed/loop count} + If BonusSpeed < 0 then begin {moving left so double to right} + BonusSpeed := abs(BonusSpeed); + BonusLoops := PrevBonusLoops * 2;{multiply is faster than add} + end + else begin {moving right.. pick a new at random..left or right} + BonusSpeed := Random div 6554;{-4 to +4} + BonusLoops := abs(Random div 1639) + 8; + PrevBonusLoops := BonusLoops;{Prev.. needed later if moving left} + end; + end;{If BonusLoops} + + {is new Rect.left to right side of window yet?} + If BonusRect.left + BonusSpeed > TarWindow[4].right then begin + WhichSound := Silence;{will kill the bonussnd} + BonusUnderWay := false; + end + else begin {draw the bonus shape} + {change shapes..ie.each of two faces every 8 loops} + Case BonusShape of + 1: If ((Counter Mod 8) = 0) then inc(BonusShape); + 2: If ((Counter Mod 8) = 0) then dec(BonusShape); + end;{case BonusShape} + + {draw the BonusRect...} + BonusRect.left := BonusRect.left + BonusSpeed;{move rect} + BonusRect.right := BonusRect.right + BonusSpeed;{offset BonusRect rect} + CopyBits(BonusMap,myWindow^.portBits, + OffBonus[BonusNdx,BonusShape], + BonusRect,srcCopy,TarWindRgn);{draw it} + end; + end;{If BonusUnderWay} + + If ShotFired = True then begin {ShotFired is set by mouseDown event} + {draw bullet trace and explode, use PatXOr mode so we can reverse it} + PenMode(PatXOr);{invert} + TargetCenter.h := GunRect.left+ 16; + TargetCenter.v := myMouse.v + 14; + MoveTo(TargetCenter.h,GunRect.top); + LineTo(TargetCenter.h,TargetCenter.v);{line from gun to explode} + + {locate Explode shape} + ExplodeRect.right := GunRect.left+(ExplodeRect.right-ExplodeRect.left); + ExplodeRect.left := GunRect.left; + ExplodeRect.bottom := myMouse.v+(ExplodeRect.bottom-ExplodeRect.top); + ExplodeRect.top := myMouse.v; + CopyBits(OffScreen,myWindow^.portBits,OffExplode, + ExplodeRect,srcXor,nil);{draw it} + + {detect a hit here....sound and such....} + DetectAHit;{we'll process the hit later, after erasing line/explode} + + {draw Site (from screen) to OffGun then OffGun to Screen, do it + here so explode is shown in site!} + CopyBits(myWindow^.portBits,OffScreen,SiteRect, + OffSite[WhichGun],srcCopy,SiteRgn[WhichGun]); + CopyBits(OffScreen,myWindow^.portBits,OffGun[WhichGun], + GunRect,srcCopy,nil); + + aTick := TickCount;{delay abit...so we can see line/explode} + inc(aTick); + Repeat Until(TickCount > aTick); + + ShotFired := False;{reset gun,ready for next mousedown} + + {erase the bullet line & explode, just invert the inversion!} + MoveTo(TargetCenter.h,GunRect.top); + LineTo(TargetCenter.h,TargetCenter.v);{inverse is still in effect} + CopyBits(OffScreen,myWindow^.portBits,OffExplode, + ExplodeRect,SrcXor,nil);{inverse mode} + PenMode(PatCopy);{set back to normal} + + {window is back to normal (no lines/explode) so do hit stuff} + {would have a mess if we erased targets before erasing explode} + If Hit = True then HandleAHit; + + end + Else begin {no shot fired... just draw the site/gun} + CopyBits(myWindow^.portBits,OffScreen,SiteRect, + OffSite[WhichGun],srcCopy,SiteRgn[WhichGun]);{site into OffGun} + CopyBits(OffScreen,myWindow^.portBits,OffGun[WhichGun], + GunRect,srcCopy,nil);{offGun into window} + end; + + PixOff[1] := PixOff[1] + Speed[1];{distance (in pixels) targets have moved} + PixOff[2] := PixOff[2] + Speed[2]; + PixOff[3] := PixOff[3] + Speed[3]; + + If ValidDone then {all score targets finished, check for end of this level} + If (MissCount = 0) and not(BonusUnderWay) then begin {do new levelstuff} + FlushEvents(mDownMask,0);{flush the mouseDown events} + DrawPicture(TarWindPic,TarWindPic^^.picFrame);{erase old targets} + ClipRect(GunRect);{erase the gun area only} + EraseRect(GunRect);{will erase in clipped area only} + DrawPicture(BackSide,BackSide^^.picframe);{will draw over gun} + ClipRect(myWindow^.portRect);{reset to whole window} + + {display the timebonus frame stuff and animate} + EraseRect(Hour);{erase current hourglass} + MoveHourGlass(TBone.hourMove.h,TBone.hourMove.v);{move hourglass to new} + EraseRect(TBone.frame); + tRect := TBone.frame; + PenSize(2,2); + FrameRect(tRect); + PenSize(1,1); + InsetRect(tRect,3,3); + FrameRect(tRect);{erase and frame area for timebonus} + DrawHourGlass;{draw hourglass in new location} + Copybits(FontMap,myWindow^.portbits,TBone.arrowSource, + TBone.arrowDest,srcCopy,nil);{draw the arrow} + FrameRect(TBone.numBox);{draw box to frame numbers} + tRect := TBone.numBox; + InsetRect(tRect,4,4);{for moveTo and erase} + + aTick := 0;{will use to accumulate/display time score} + If SoundOn then begin + If odd(Level) then begin + whichSound := SnakeCharmer; {designate which of FourTone} + i := mySongPtr^.noteCount; + end + else begin + whichSound := Walk; + i := myWSongPtr^.noteCount;{walk noteCount for loop} + end; + InitialSnakeSong(1);{Start the Song} + end;{if sound} + + {now animate hourglass and score until sand is gone (GameOver)} + Repeat + SystemTask; + {check song note to see if we need to start another note} + If (TickCount > ToneDelay) then begin {get another songnote} + {if last note then reset to beginning} + If NoteCount < i then inc(NoteCount) else NoteCount := 1; + SetSnakeSoundStuff;{get next sound,etc.} + end; {if TickCount} + If SpeedTrapOn then Delay(Speedticks,BuffSize);{slow down 1 tick} + DrawSandStuff; {will return GameOver true if done} + {use aTick to accumulate the Time Score} + aTick := aTick + (Level div 2) + 1;{timebonus score} + NumToString(aTick,tStr); + MoveTo(tRect.right-StringWidth(tStr)-1,tRect.bottom); + EraseRect(tRect); + DrawString(tStr);{draw the new score} + Until(GameOver = True);{true when all the sand is gone} + + Score := Score + aTick;{update score} + DrawStatus(2,Score); + NewHiScore; + MoveHourGlass(-TBone.hourMove.h,-TBone.hourMove.v);{back to original} + DrawHourGlass; + whichSound := Silence; + err := PBKillIO(SoundParmBlk,false);{kill sound} + EraseRect(TBone.frame);{get rid of TimeBonus frame} + + If Level <> 4 then begin {handle various levels} + Case Level of + 2:Begin + DrawAClue(1);{draw a clue} + PauseThisGame(2);{2 indicates a clue pause} + end; + 10:Begin {display the clue dialog, get user input} + ShowCursor;{for dialog input} + DisplayDialog(ClueDial);{will cause update for myWindow} + HideCursor;{continue with game} + + If PauseStatus = 3 then begin {3 if tomb,0 if not} + ShowCursor; + DisplayDialog(Tomb);{display the tomb dialog} + HideCursor; + PauseStatus := 0;{enable normal redrawing} + Score := Score + 10000;{tomb bonus = 10000} + DrawStatus(2,Score); + NewHiScore; + end; + + InvalRect(myWindow^.portRect);{whole window} + UpdateMyWindow; + FlushEvents(updateMask,0); + + DrawPicture(TarWindPic,TarWindPic^^.picFrame);{continue} + DoCommonLevelStuff; + end;{case 5} + OtherWise begin + {draw the empty TarWindows and start another level} + DrawPicture(TarWindPic,TarWindPic^^.picFrame); + DoCommonLevelStuff;{do here for no clue, controls for clues} + end;{otherwise} + end;{case level} + end{if level <> 4} + Else begin {game is locked...let user know} + ShowCursor; + DisplayDialog(Unlock);{text about unlocking the game} + HideCursor; + EndThisGame; + end; + FlushEvents(mDownMask,0); + end;{if (MissCount} + + {draw the hourglass stuff, end game if out of sand} + If PauseStatus = 0 then begin {if no pause, watch out for clue level pause} + DrawSandStuff; {SandStuff will return GameOver = true if sand empty} + If GameOver then EndThisGame; + end;{if pauseStatus} +End; + + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + {HLock(Handle(ClockCursor));locked in resource} + + For i := 1 to 3 do begin + myCursor[i] := GetCursor(129 + i); + {HLock(Handle(myCursor[i]));locked in resource} + end; + + {show the watch while we wait for inits & setups to finish} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; {startup the fonts manager} + InitWindows; {startup the window manager} + InitMenus; {startup the menu manager} + TEInit; {startup the text edit manager} + InitDialogs(Nil); {startup the dialog manager} + + Finished := False; {set program terminator to false} + FlushEvents(everyEvent,0); {clear events from previous program} + { set up screen size stuff } + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); + + ResRefNum := CurResFile;{i-116} +End; + +procedure CreateWindow;{windows,dialogs} +var + tRect: Rect; +Begin + {get window. template will use our WDEF 128 resource. set PROCId field} + myWindow := GetNewWindow(WindResId,@wRecord,Pointer(-1)); + SetRect(tRect,0,-10,600,400);{this will clip OffScreen bitMaps as well!!} + + ResumeButton := GetNewControl(pressResume,myWindow); + EndButton := GetNewControl(pressEnd,myWindow); + + {remember: Dialog itemlists should be purgable, dialogs invisible} + For DialNdx := Help to PrintDial do begin {read all the dialogs into array} + myDialog[DialNdx] := + GetNewDialog(ord(DialNdx)+129,@myDialogRec[DialNdx],myWindow); + SetPort(myDialog[DialNdx]); + ClipRect(tRect);{set clip to smaller size..} + end; + + Environs(i,j);{peek to see what hardware, i is rom, j is machine + 1 = 128k thru Plus,2 = Mac II,3 = SE} + If j = 2 then SetControlValue(4) {must be a Mac II speedwise} + else SetControlValue(2);{set NORMAL button in Speed dialog} + + ShowWindow(myWindow);{done so back to myWindow...} + SetPort(myWindow); + ClipRect(tRect); {i-166, set cliprgn to small rgn} + TextFont(3);{Geneva font, installed in System resource file} + TextSize(12); + + ScoreHandle := GetResource('HSCR',128);{we're keeping Scores in Resource} + {HLock(ScoreHandle);lock it permanently in resource} + theHighs := ScorePtr(ScoreHandle^); +end; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; + i,h: integer; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetPort(tPort); + If theItem = 1 then DisplayDialog(About) + Else begin + GetItem(myMenus[1],theItem,name);{must be a desk acc.} + refNum := OpenDeskAcc(name); + end; + SetPort(tPort); + End; + fileMenu: + Case theItem of + 1:Begin + HiliteMenu(0); + BeginTheGame;{rem draws a new menu bar} + end; + 2:Finished := True; {quit this program} + end;{of case} + optionMenu: + Case theItem of + 1:DisplayDialog(Guard);{protection dialog} + 2:DisplayDialog(Help); + 3:DisplayDialog(Scores);{hiscore stuff} + 4:Begin {toggle sound on or off} + If SoundOn then SoundOn := false else SoundOn := true; + CheckItem(myMenus[3],theItem,SoundOn);{check if true,none if false} + end; + 5:DisplayDialog(SetSpeed);{for hardware} + end; { case theItem} + worksMenu: + Case theItem of + 1:DisplayDialog(BMap);{two of our Offscreen bitmaps} + 2:DisplayDialog(Source);{our source code offer} + end;{case theItem} + End;{case theMenu of} + HiliteMenu(0); +End; + +procedure TakeCareKeyDown(Event:EventRecord); +Var + CharCode: char; +Begin + CharCode := chr(LoWord(BitAnd(Event.message,CharCodeMask))); + + If BitAnd(Event.modifiers,CmdKey) = CmdKey then + DoMenuCommand(MenuKey(CharCode)) + Else If GameUnderWay then PauseThisGame(1);{pause with any other key press} +End; + +procedure TakeCareControls(whichControl:ControlHandle;localMouse:point); +var + ControlHit,i: integer; +Begin + ControlHit := TrackControl(whichControl,localMouse,nil); { Find out which} + If ControlHit > 0 then {i-417} + Begin + If whichControl = ResumeButton then {RESUME the game..} + Begin + HideControl(ResumeButton);{watch out for update event} + HideControl(EndButton); + If (PauseStatus = 2) then InvalRect(myWindow^.portRect);{erase clue} + + BeginUpdate(myWindow);{clear Update stuff...buttons} + EraseRect(myWindow^.portRect); + Case PauseStatus of + 2:Begin {clue is being draw over} + PauseStatus := 0;{so Draw will redraw properly} + GameUnderWay := True; + DrawWindowContents(myWindow); + PauseStatus := 2;{so we can DoCommon... below} + end; + OtherWise DrawWindowContents(myWindow) {just the hidden control area} + end;{case PauseStatus} + EndUpDate(myWindow); + + FlushEvents(updateMask,0);{clear Update caused by HideControl} + If PauseStatus = 2 then DoCommonLevelStuff;{start next level} + SetCursorMenus(True); + PauseStatus := 0; + End; + If whichControl = EndButton then {END this current game...} + Begin + HideControl(ResumeButton);{hide the resume and end} + HideControl(EndButton); + EndThisGame; + End; + End; {of If ControlHit} +End; { of procedure} + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + WhichControl: ControlHandle; + MouseLoc: Point; + WindowLoc: integer; + ControlHit: integer; + GlobalMouse:Point; +Begin + If GameUnderWay = True then begin {handle a game click} + ShotFired := True;{flag a shot, will be checked in AnimateOneLoop} + If SoundOn then begin {write/start a Gunshot sound} + {leave whichsound = to whatever it is} + err := PBKillIO(SoundParmBlk,false);{kill current sound} + WriteASoundBlock(ptr(Sound[GunShot]),Buff[GunShot],TickCount); + end;{if soundOn} + end + Else begin {Mouse is normal...handle normal functions} + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {I-441} + inDrag: + DragWindow(WhichWindow,MouseLoc,DragArea); + inContent: + If WhichWindow <> FrontWindow then SelectWindow(WhichWindow) + else Begin + GlobaltoLocal(MouseLoc); + {check to see if our Continue/End buttons were clicked} + ControlHit := FindControl(MouseLoc,whichWindow,whichControl); + If ControlHit > 0 then TakeCareControls(whichControl,Mouseloc) + {check to see if click on hourglass, begin the game} + Else If (PtInRect(MouseLoc,Hour)) and (PauseStatus = 0) + then BeginTheGame;{start the game} + end;{else} + end; {case of} + end;{Else} +end; { TakeCareMouseDown } + + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); +End; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect); + DrawWindowContents(UpDateWindow);{we only have one window!} + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure TakeCareSoundStuff;{called from MainEvent} +{the current sound is complete if ioResult < 0. Some of our sounds only play +thru once so we leave WhichSound = Silence so that the sound is not restarted. +BonusSnd is played over and over while a Bonus (bottom target) is displayed so +we set WhichSound = BonusSnd.. and each time it is finished this procedure +starts another BonusSnd. In the case of our two 'songs' we are changing the +notes before the sound is complete (before ioResult < 1 or duration = 0). +We check the current TickCount against a variable ToneDelay which equals the +TickCount when the note started plus the ticks the note should play... so +if TickCount > ToneDelay we need to start another note.. we just change the +pitches and reset the Duration field and keep going! Note: the sound i/o +decrements the duration field so it must be reset each time!} +Begin + If SoundParmBlk^.ioResult < 1 then {sound is finished find another/play it} + Case WhichSound of + BonusSnd:{start another BonusSnd} + WriteASoundBlock(ptr(Sound[BonusSnd]),Buff[BonusSnd],TickCount); + end; {case whichsound} + + {we don't use Songs from MainEvent loop but if we did we'd need this Else. + All our 'song's are played from closed loops in the AnimateOneLoop} + (* Else {sound is still underway if Snake or Walk check for next note} + Case WhichSound of + SnakeCharmer:begin {snakeCharmer song is underway check for new note} + If (TickCount > ToneDelay) then begin {then get next note} + inc(NoteCount); + SetSnakeSoundStuff; + {check to see if this is the last note} + If mySongPtr^.noteCount = NoteCount then WhichSound := Silence; + end;{if} + end;{snake} + Walk:begin {Walk song is underway check for new note} + If (TickCount > ToneDelay) then begin {need new note} + inc(NoteCount); + SetSnakeSoundStuff; + If myWSongPtr^.noteCount = NoteCount then WhichSound := Silence; + end;{if} + end;{walk} + end;{case & else} *) +End;{of procedure} + +procedure MainEventLoop; +var + myEvent: EventRecord; + i: integer; +Begin + InitCursor; + SetCursor(myCursor[1]^^);{one of 3 custom cursors} + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then {handle the event} + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: TakeCareKeyDown(myEvent); + ActivateEvt: TakeCareActivates(myEvent); + UpdateEvt: TakeCareUpdates(myEvent); + End {of Case} + Else {no event pending so lets do some game stuff} + If GameUnderWay then begin + AnimateOneLoop; + If SoundOn then TakeCareSoundStuff; + end;{ if GameUnderWay} + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + myMenus[3] := GetMenu(optionMenu); + myMenus[4] := GetMenu(worksMenu); + myMenus[5] := GetMenu(MessageMenu); {this is the backspace message} + CheckItem(myMenus[3],4,True); {check the Sound item} + SoundOn := True; {sound will start on first begin} + For i := 1 to 4 do InsertMenu(myMenus[i],0); + DrawMenuBar; +End; + +procedure CloseStuff; +Begin + ShowCursor; + err := PBKillIO(SoundParmBlk,false);{always kill sound before quitting} + WriteResource(ScoreHandle);{write out if changes were flagged..HiScore} +End; + +{Main Program begins here} +BEGIN + InitThings; + SetUpMenus; {load the menus from resources} + CreateWindow; {load game window and dialogs from resources} + CreateSound; {load songs and create sound stuff} + CreateCairoStuff; {load Cairo font bitimage and locate table} + CreatePictures; {load Pictures from resources} + CreateOffScreenBitMap; {load Bitmap from resources} + CreateStrings; {load strings from resources} + OneTimeGameStuff; {locate all our rects, etc. in game window} + MainEventLoop; {let's play a game! get user input} + CloseStuff; {done so close} +END. diff --git a/Duane Blehm's Code/Cairo Æ’/aCairo.R b/Duane Blehm's Code/Cairo Æ’/aCairo.R new file mode 100755 index 0000000..f0de4c2 --- /dev/null +++ b/Duane Blehm's Code/Cairo Æ’/aCairo.R @@ -0,0 +1,38 @@ +* aCairo.R + +aCairo.rsrc + +TYPE NILE = STR + ,0 +Cairo ShootOut ver1.2src, by Duane Blehm, 9/15/87 + +TYPE MENU + ,1 +\14 + About ShootOut... + (- + + ,256 +Game + Begin + Quit /Q + + ,257 +Options + Unlock.. + Help.. + HiScore.. + ! Sound + Set Speed.. + + ,258 +Works + Peek OffScreen + Source Code... + + ,259 +(Any Key to Exit) + +* will include Resource file built with ResEdit and ScrapBook +INCLUDE aCairo/rsrc + diff --git a/Duane Blehm's Code/Cairo Æ’/aCairo:rsrc b/Duane Blehm's Code/Cairo Æ’/aCairo:rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Copy Mask Æ’/About CopyMask b/Duane Blehm's Code/Copy Mask Æ’/About CopyMask new file mode 100755 index 0000000..894ca3d Binary files /dev/null and b/Duane Blehm's Code/Copy Mask Æ’/About CopyMask differ diff --git a/Duane Blehm's Code/Copy Mask Æ’/CMask.R b/Duane Blehm's Code/Copy Mask Æ’/CMask.R new file mode 100755 index 0000000..2f51bc9 --- /dev/null +++ b/Duane Blehm's Code/Copy Mask Æ’/CMask.R @@ -0,0 +1,16 @@ +* CMask.R + +CMask.rsrc + +TYPE MENU + ,1 +\14 + + ,256 +File + OffScreen.. + Quit + +* will include Pict resources file built with ResEdit and ScrapBook +INCLUDE CMask/rsrc + diff --git a/Duane Blehm's Code/Copy Mask Æ’/CMask:rsrc b/Duane Blehm's Code/Copy Mask Æ’/CMask:rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Copy Mask Æ’/CopyMask b/Duane Blehm's Code/Copy Mask Æ’/CopyMask new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Copy Mask Æ’/CopyMask.pas b/Duane Blehm's Code/Copy Mask Æ’/CopyMask.pas new file mode 100755 index 0000000..f9da404 --- /dev/null +++ b/Duane Blehm's Code/Copy Mask Æ’/CopyMask.pas @@ -0,0 +1,533 @@ +Program CopyMaskDemo;{copyright ©,1987...Duane Blehm,HomeTown Software} +{This program illustrates using the CopyMask() ROM procedure for animation. +Note: CopyMask and CalcMask are NOT available in the 64k ROMs. +We just set up everything so we could achieve the same 'effect' as we did +with CopyBits and regions in the Animation Demo. We're sure there are +some better examples of how CopyMask might be used... thought out with +its features in mind. See Inside Mac vol. 4 or Technical Note #57 for +specs. on these and other 'new' 128k ROM Quickdraw routines.} + +USES MacIntf; + +{$L CMask.rsrc}{link resources...just our menu stuff} + +CONST + lastMenu = 2; {Number of Menus} + appleMenu = 1; + fileMenu = 256; + +VAR {global program stuff} + FlowerMap, {will use FlowerMap to do our CalcMask of Flower pict} + MaskMap: BitMap;{final mask, used with CopyMask call} + imagePtr,tPtr: Ptr;{imagePtr points to temp. BitImage of FlowerMap} + tRect, + MaskRect: Rect;{MaskRect same size as FlyRect, CopyMask call} + + HomeTown: PicHandle;{handle for our Logo pic} + HomeRect: Rect;{destination rect for our Logo} + + {here's all the fly/regions stuff} + Fly: array[1..2] of PicHandle;{for loading 2 fly pictures} + FlyRect: Rect;{destination rect for drawing fly} + OffFly: array[1..2] of Rect;{source rects in offscreen} + FlyNdx: integer;{which offFly to draw} + Flower: PicHandle;{load picture from resource} + FlowerRect: Rect;{for locating the flower} + FlyBorder: Rect;{for fly border} + FlightRect: Rect;{For limiting fly flight} + CursorIsOn: Boolean; + MouseRect: Rect;{slides with mouse to smooth movement} + + OffScreen, {will hold our 2 fly images} + OldBits: BitMap; + SizeOfOff: Size; {for Size-ing the offscreen bitmap} + OffRowBytes: Integer; + + myDialog: DialogPtr; + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu, + theItem: integer; + Finished: Boolean;{used to terminate the program} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; + Screen,DragArea, + GrowArea: Rect; + i,x,y: integer; + +{----------------------------------------------} +procedure CreatePictures; +var + i:integer; +Begin + HomeTown := GetPicture(131);{HomeTown logo} + HomeRect := HomeTown^^.picFrame;{size dest.Rest for drawing pic} + + {we'll draw logo into upper right corner of window so relocate} + OffSetRect(HomeRect,myWindow^.portRect.right - 1 - + HomeRect.right,1 - HomeRect.top); + + {load flystuff to demonstrate regions} + Fly[1] := GetPicture(132); + Fly[2] := GetPicture(133); + {size the fly rectangles} + For i := 1 to 2 do OffFly[i] := Fly[i]^^.picFrame;{they're both same size} + FlyRect := OffFly[1]; + + Flower := GetPicture(134); + FlowerRect := Flower^^.picFrame;{size the FlowerRect} +end; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +var + bRect: Rect; +Begin + {find size/rows/bounds of bitimage} + SetRect(bRect,0,0,50,95); { big enough for our flys! } + with bRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(bRect,-left,-top); {local coordinates...0,0 = topleft } + end; { of with } + + with OffScreen do begin; { create new BitMap record } + {create new bitimage and make baseAddr point to it} + baseAddr := QDPtr(NewPtr(SizeOfOff)); + rowbytes := OffRowBytes;{number of bytes/row,can extend beyond bounds} + bounds := bRect;{limits of bitimage drawing?} + end; { of with OffScreen } +End; + +procedure DrawPicsIntoOffScreen; +var + i: integer; +Begin + OldBits := myWindow^.portBits; {preserve old myWindow BitMap} + SetPortBits(OffScreen); {our new myWindow BitMap } + + FillRect(OffScreen.bounds,white); {erase our new BitMap to white} + + {locate the flys in the offscreen bitmap} + OffSetRect(OffFly[1],- OffFly[1].left,-OffFly[1].top); + OffSetRect(OffFly[2],- OffFly[2].left,OffFly[1].bottom-OffFly[2].top); + + {draw the flys into offscreen} + For i := 1 to 2 do begin + DrawPicture(Fly[i],OffFly[i]); + ReleaseResource(Handle(Fly[i]));{done with pics so dump em} + end; + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure CreateMaskBitMap; +{first in the FlowerMap we draw the flower then we do a CalcMask of it +into an identical bitimage (imagePtr). Once we have the mask in imagePtr +we make FlowerMap.baseAddr point to it and invert it so the fly is drawn +'outside' the flower edges (the 'flower' becomes white instead black). The +MaskMap needs to be size of the area the fly will be flying over, and we +have a RoundRect border which includes the flower area. So we fill the +RoundRect area with black and copyBits the inverted Flower mask from +FlowerMap. (see Paint document for illustration of this) Now there is +black everywhere in the MaskMap we want the Fly to be drawn on the screen +except the 'coordinates' are not the same. All we need to do is start +the MaskRect over the MaskMap in sync with the flyRect in our window so +they begin in the same 'relative' position to the flower/border} +Begin + {find size/rows/bounds of bitimage} + tRect := Flower^^.picFrame;{FlowerMap size of flower picture} + with tRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(tRect,-left,-top); {local coordinates...0,0 = topleft } + end; { of with } + + with FlowerMap do begin; { create new BitMap record } + {create new bitimage and make baseAddr point to it} + baseAddr := QDPtr(NewPtr(SizeOfOff)); + rowbytes := OffRowBytes;{number of bytes/row,can extend beyond bounds} + bounds := tRect;{limits of bitimage drawing?} + end; {with} + + imagePtr := QDPtr(NewPtr(SizeOfOff));{Bitimage same as FlowerMap} + + OldBits := myWindow^.portBits; {preserve old myWindow BitMap} + SetPortBits(FlowerMap); {our new myWindow BitMap } + + DrawPicture(Flower,tRect);{draw flower into the BitMap} + + {create mask for flower picture, 128k ROMs only} + CalcMask(FlowerMap.baseAddr,imagePtr,FlowerMap.rowbytes, + FlowerMap.rowbytes,FlowerMap.bounds.bottom-FlowerMap.bounds.top, + FlowerMap.rowbytes div 2); + + FlowerMap.baseAddr := imagePtr;{make FlowerMap point to mask} + SetPortBits(FlowerMap);{make our window point to new flowerMap} + InvertRect(FlowerMap.Bounds);{we want to draw outside the flower } + + {create the offscreen MaskMap.....find size/rows/bounds of bitimage} + tRect := FlightRect; + InsetRect(tRect,-64,-64);{enlarge so fly can exceed border} + with tRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(tRect,-left,-top); {local coordinates...0,0 = topleft } + end; { of with } + + with MaskMap do begin; { create new BitMap record } + {create new bitimage and make baseAddr point to it} + baseAddr := QDPtr(NewPtr(SizeOfOff)); + rowbytes := OffRowBytes;{number of bytes/row,can extend beyond bounds} + bounds := tRect;{limits of bitimage drawing} + end; {with} + + SetPortBits(MaskMap);{so we can draw into the MaskMap} + FillRect(MaskMap.bounds,white);{erase it to white} + InsetRect(tRect,64,64);{shrink it back to flight size to draw border} + FillRoundRect(tRect,48,32,black);{black in the flight area} + {copy the Flower mask into MaskMap} + tRect := FlowerRect; + {locate it in center of border} + OffSetRect(tRect,64+FlowerRect.left-FlightRect.left-tRect.left, + 64+FlowerRect.top-FlightRect.top-tRect.top); + CopyBits(FlowerMap,MaskMap,FlowerMap.bounds,tRect,srcCopy,nil); + + {note: we are done with the FlowerMap at this point.} + + SetPortBits(OldBits); {restore old bitmap} + +End; + +procedure DrawWindowContents(WhichWindow:WindowPtr);{response to Update event} +var + trect:Rect; + i:integer; +Begin + DrawPicture(HomeTown,HomeRect);{draw our logo} + + {copy offScreen flys into Window..upperleft corner,as in bitmap} + CopyBits(OffScreen,myWindow^.portBits,OffScreen.bounds, + OffScreen.bounds,srcCopy,nil); + + {all the fly stuff} + DrawPicture(Flower,FlowerRect); + FrameRoundRect(FlyBorder,48,32);{border around the fly area} + CopyMask(OffScreen,MaskMap,myWindow^.portBits,OffFly[FlyNdx], + MaskRect,FlyRect);{draw the fly} + +End; + +Procedure InitialAnimation;{locate everything to begin animation} +Begin + {locate the flower} + OffSetRect(FlowerRect,160-FlowerRect.left,90-FlowerRect.top); + + {size the FlyBorder} + FlyBorder := FlowerRect; + InsetRect(FlyBorder,-18,0);{expand left/right for border} + FlyBorder.top := FlyBorder.top - 18;{also top.. leave bottom for stem} + + FlightRect := FlyBorder;{FlightRect will compensate for using fly.topleft} + + CreateMaskBitMap;{create the 'mask' for use with the CopyMask call} + + InsetRect(FlightRect,-16,-16);{so fly can go beyond the flyborder} + OffSetRect(FlightRect,-16,-16);{because we're using FlyRect.topleft} + MouseRect := FlightRect;{MouseRect moves with cursor,& maps into FlightRect} + + {expand limits by 1 so we can have a frame border that's not erased} + InSetRect(FlyBorder,-1,-1); + + MaskRect := FlyRect; + {locate fly in upperleft of FlightRect} + OffSetRect(FlyRect,FlightRect.left-FlyRect.left, + FlightRect.top-FlyRect.top); + {locate MaskRect in MaskMap relative to FlyRect and FlightRect} + OffSetRect(MaskRect,32-MaskRect.left,32-MaskRect.top);{in synch with fly} + FlyNdx := 1;{set to first Fly shape} +end; + +procedure AnimateStuff; +var tPoint:Point; + tRect:Rect; + aTick:Longint; +Begin + Delay(2,aTick);{CopyMask is seems to be faster than CopyBits with Rgn..?} + {now animate the fly} + GetMouse(tPoint);{get current mouse coordinates} + + {hide cursor if its over the fly area} + If PtInRect(tPoint,FlyBorder) then begin + If CursorIsOn then begin {hide cursor if its on} + HideCursor; + CursorIsOn := False; + end; + end + else If not(CursorIsOn) then begin {show cursor if its off} + ShowCursor; + CursorIsOn := True; + end; + + {limit fly image (FlyRect) to FlightRect extremes..} + {to keep fly from wondering off visible area} + {mouseRect moves with the cursor and tPoint is mapped into FlightRect} + + If not(PtInRect(tPoint,MouseRect)) then begin + {a coordinate is outside mouseRect so slide mouseRect to tPoint} + If tPoint.h < MouseRect.left then begin {slide MouseRect to left} + MouseRect.right := MouseRect.right - + (MouseRect.left - tPoint.h); + MouseRect.left := tPoint.h; + end + else If tPoint.h > MouseRect.right then begin + MouseRect.left := MouseRect.left + tPoint.h - MouseRect.right; + MouseRect.right := tPoint.h; + end; + If tPoint.v < MouseRect.top then begin + MouseRect.bottom := MouseRect.bottom - + (MouseRect.top - tPoint.v); + MouseRect.top := tPoint.v; + end + else If tPoint.v > MouseRect.bottom then begin + MouseRect.top := MouseRect.top + tPoint.v - MouseRect.bottom; + MouseRect.bottom := tPoint.v; + end; + end;{if not(ptinRect)} + + {tPoint is to MouseRect as FlyRect.topleft is to FlightRect} + MapPt(tPoint,MouseRect,FlightRect); + + {determine horiz/vert. offset if needed, MaskRect moves in sync with FlyRect} + If tPoint.h > FlyRect.left + 2 then begin + FlyRect.left := FlyRect.left + 3;{offsetRect to right} + FlyRect.right := FlyRect.right + 3; + MaskRect.left := MaskRect.left + 3;{offsetRect to right} + MaskRect.right := MaskRect.right + 3; + end + else if tPoint.h < FlyRect.left - 2 then begin + FlyRect.left := FlyRect.left - 3;{offsetRect to left} + FlyRect.right := FlyRect.right - 3; + MaskRect.left := MaskRect.left - 3;{offsetRect to left} + MaskRect.right := MaskRect.right - 3; + end; + {vertical offset?} + If tPoint.v > FlyRect.top + 2 then begin + FlyRect.top := FlyRect.top + 3; + FlyRect.bottom := FlyRect.bottom + 3; + MaskRect.top := MaskRect.top + 3; + MaskRect.bottom := MaskRect.bottom + 3; + end + else if tPoint.v < FlyRect.top - 2 then begin + FlyRect.top := FlyRect.top - 3; + FlyRect.bottom := FlyRect.bottom - 3; + MaskRect.top := MaskRect.top - 3; + MaskRect.bottom := MaskRect.bottom - 3; + end; + + {draw the fly from OffScreen to myWindow using MaskMap} + CopyMask(OffScreen,MaskMap,myWindow^.portBits,OffFly[FlyNdx], + MaskRect,FlyRect); + + If FlyNdx = 1 then inc(FlyNdx) {next shape, there are 2} + else FlyNdx := 1; {back to first shape} +end; + + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups to finish} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; {startup the fonts manager} + InitWindows; {startup the window manager} + InitMenus; {startup the menu manager} + TEInit; {startup the text edit manager} + InitDialogs(Nil); {startup the dialog manager} + + Finished := False; {set program terminator to false} + FlushEvents(everyEvent,0); {clear events from previous program} + { set up screen size stuff } + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do { Screen.Left, etc. } + Begin + SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); + SetRect(GrowArea,Left,Top+24,Right,Bottom); + End; +End; + +procedure CreateWindow; +var + Wrect: Rect; + TypeWind: Integer; + Visible: Boolean; + GoAway: Boolean; + RefVal: Longint; +Begin + SetRect(Wrect,8,40,504,336); + TypeWind := 0; + Visible := True; + GoAway := True; + + myWindow := NewWindow(Nil,Wrect,'CopyMask Demo',Visible, + TypeWind,Nil,GoAway,RefVal); + SetPort(myWindow); + ClipRect(myWindow^.portRect);{set clipping area as per Inside Mac} +End; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetItem(myMenus[1],theItem,name); + refNum := OpenDeskAcc(name); + End; + fileMenu: + Case theItem of + 1:{display offScreen bitmap in dialog} + Begin + myDialog := GetNewDialog(128,nil,myWindow);{from resource} + GetPort(tPort); + ShowWindow(myDialog);{invisible in resource} + SelectWindow(myDialog); + SetPort(myDialog); {so we can draw into our window} + + CopyBits(OffScreen,myDialog^.portBits,OffScreen.bounds, + OffScreen.bounds,srcOr,Nil);{the whole thing} + FrameRect(OffScreen.bounds);{frame it } + MoveTo(OffScreen.bounds.left + 10,OffScreen.bounds.bottom + 20); + DrawString('^ copy of OffScreen Bitmap'); + ModalDialog(Nil,theItem);{we'll close no matter what hit} + + HideWindow(myDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort); + end;{1:} + 2:Finished := True; + end;{case theItem} + End; + HiliteMenu(0); +End; + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + MouseLoc: Point; + WindowLoc: integer; +Begin + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {I-441} + inContent: + If WhichWindow <> FrontWindow then + SelectWindow (WhichWindow); + inGoAway: + If TrackGoAway(WhichWindow,MouseLoc) then + Finished := True;{end the game} + end; {case of} +end; { TakeCareMouseDown } + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); +End; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect);{ or UpdateWindow^.VisRgn^^.rgnBBox } + DrawWindowContents(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure MainEventLoop; +var + myEvent: EventRecord; +Begin + InitCursor; + CursorIsOn := True;{keep track of hide/show cursor} + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: Finished:= True; + ActivateEvt:TakeCareActivates(myEvent); + UpDateEvt: TakeCareUpdates(myEvent); + End + + Else AnimateStuff; + + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + For i := 1 to lastMenu do + begin + InsertMenu(myMenus[i],0); + end; + DrawMenuBar; +End; + +procedure CloseStuff; +Begin + {be sure to kill any sound I/O before quitting!} +End; + +{Main Program begins here} +BEGIN + InitThings; + {check for the 64k ROMs, abort if they're present} + Environs(x,y); + If x >= 117 then begin {64k roms aren't present,so go ahead} + SetUpMenus; + CreateWindow; + CreatePictures;{load picts from resource file} + CreateOffScreenBitMap;{for use with copyBits procedure} + DrawPicsIntoOffScreen;{OffScreen holds all our fly shapes} + InitialAnimation;{set up stuff for start of animation} + MainEventLoop;{will animate fly if nothing else is going on} + end + else begin + InitCursor;{show arrow cursor} + i := NoteAlert(128,nil); {display alert and exit} + end;{else} + CloseStuff; +END. diff --git a/Duane Blehm's Code/Drag Piece Æ’/About DragPiece.txt b/Duane Blehm's Code/Drag Piece Æ’/About DragPiece.txt new file mode 100755 index 0000000..422730b --- /dev/null +++ b/Duane Blehm's Code/Drag Piece Æ’/About DragPiece.txt @@ -0,0 +1,14 @@ +About the DragPieceDemo files.... + +'DragPieceDemo.pas' is an 'Edit' text file containing sourcecode for our +'almost generic' DragPiece() procedure used in a simple example program. + +'aDragPiece.rsrc' is a resource file which is linked to the Demo code. + +'DragPieceDemo' is the application file. + +'DragPiece animation' is a paint-type file that illustrates what's happening. + +We're including this Demo with 'ZeroGravity' because it illustrates the same +principle of animation over backgrounds with some improvements, refinements, +etc. We make extensive use of this procedure in our new 'PUZZ'L' application. \ No newline at end of file diff --git a/Duane Blehm's Code/Drag Piece Æ’/DragPiece animation b/Duane Blehm's Code/Drag Piece Æ’/DragPiece animation new file mode 100755 index 0000000..881bd8a Binary files /dev/null and b/Duane Blehm's Code/Drag Piece Æ’/DragPiece animation differ diff --git a/Duane Blehm's Code/Drag Piece Æ’/DragPieceDemo b/Duane Blehm's Code/Drag Piece Æ’/DragPieceDemo new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Drag Piece Æ’/DragPieceDemo.Pas b/Duane Blehm's Code/Drag Piece Æ’/DragPieceDemo.Pas new file mode 100755 index 0000000..2b65670 --- /dev/null +++ b/Duane Blehm's Code/Drag Piece Æ’/DragPieceDemo.Pas @@ -0,0 +1,392 @@ +Program DragPieceDemo;{Thu Dec 17, 1987, © HomeTown Software,1987 +all rights reserved, this code not for distribution} +{this program demonstrates use of our 'generic' DragPiece() procedure for +animation of a 'piece' over a background. We're still trying to make the +procedure truly independent. Currently it has some 'options' embedded +within it for initializing the PicMap and PatchMap and also for how +the piece is effected on completion... see DragPiece(). The procedure +is called from TakeCareMouseDown() in window contents. } + +{also you might want to check out the MakeBitMap() procedure, this is a big +improvement over our old CreateOffScreenBitmap() stuff!} + +USES MacIntf;{TML Directives} + +{$L aDragPiece.rsrc}{link the resource file} + +CONST + lastMenu = 2; {Number of Menus} + appleMenu = 1; + fileMenu = 256; + +VAR {global program stuff} + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu,theItem: integer; + Finished: Boolean;{used to terminate the program} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; + Screen,DragArea,GrowArea: Rect; + + tRect:Rect; + tPt:Point; + DragRgn:RgnHandle; + LogoLoc:Point; +{----------------------------------------------} + +procedure DrawWindowContents(WhichWindow:WindowPtr); +var + trect:Rect; + tStr:Str255; + myPic:PicHandle; +Begin + FillRect(whichWindow^.portRect,ltGray); + tStr := 'Click to create a spot and then DRAG it!'; + SetRect(tRect,0,80,StringWidth(tStr) + 20,105); + EraseRect(tRect); + moveTo(10,100); + DrawString(tStr); + myPic := GetPicture(128);{logo} + tRect := myPic^^.picFrame; + {watchout! the window^.portRect is offset by a SetOrigin() call!} + OffSetRect(tRect,LogoLoc.h-tRect.right,LogoLoc.v-tRect.bottom); + DrawPicture(myPic,tRect); +End; + +procedure DragPiece(aWindow:WindowPtr;var theMouse:Point; + DragRect:Rect;PieceRgn:RgnHandle); +{aWindow is window the piece is to be dragged in, theMouse is location of +mouseDown, DragRect limits the area the piece can be dragged within the +window, PieceRgn is destination Region in aWindow for the piece. PieceRgn +defines the size of the piece by its .rgnBBox and shape by the shape of +the region... the Piece drawn into the PicMap is masked into the ComboMap +by a copy of the PieceRgn. If no Mask is desired just pass a new Rgn set +to the Rect in the window with a RectRgn() call. This procedure does NO +error checking for memory available, etc., beware!} +var + tRgn:RgnHandle;{temp copy of PieceRgn for animation} + PatchMap,ComboMap,PicMap,OldBits:Bitmap; + sRect,sUnionRect,LastsRect, + cRect,cUnionRect,LastcRect:Rect; + MouseLoc,LastMouseLoc,tPt:Point; + ExitToAnimate:boolean; + MaxMove:Point; + +procedure MakeBitMap(var theMap:Bitmap);{create a bitmap record} +{assumes that 'theMap.bounds' is predefined rect for size and +does NO error checking for available memory or MemErr..} +Begin + {following same as OffsetRect(theMap.bounds,-left,-top)} + theMap.bounds.right := theMap.bounds.right-theMap.bounds.left; + theMap.bounds.left := 0; + theMap.bounds.bottom := theMap.bounds.bottom-theMap.bounds.top; + theMap.bounds.top := 0; + + {now with 0,0 origin.. '.right' is width, and '.bottom' is height} + theMap.rowbytes := (((theMap.bounds.right - 1) DIV 16) + 1) * 2; + {size of bitimage is bottom times rowBytes, potential MemError here} + theMap.baseAddr := NewPtr(theMap.bounds.bottom * theMap.rowbytes); +End; + +Begin {procedure dragPiece} + SetPt(MaxMove,20,20);{piece will move max. of 20 pixels per loop} + {create temp. bitmaps, size Combo for MaxMove} + PicMap.bounds := PieceRgn^^.rgnBBox;{size to contain the region} + MakeBitMap(PicMap);{create the bitmap record.. must define .bounds first} + PatchMap.bounds := PicMap.bounds;{same size} + MakeBitMap(PatchMap); + + ComboMap.bounds := PatchMap.bounds; + {increase size to accomodate extra for drawing background/union } + ComboMap.bounds.right := ComboMap.bounds.right + MaxMove.h; + ComboMap.bounds.bottom := ComboMap.bounds.bottom + MaxMove.v; + MakeBitMap(ComboMap); + + tRgn := NewRgn; + CopyRgn(PieceRgn,tRgn);{make a temp. copy for use in drawing} + + {_________ PicMap must contain image to be 'dragged' ______} + {we're just going to 'fill' the PieceRgn for this example, this bitmap + could be passed as a parameter.} + OldBits := aWindow^.portBits; + SetPortBits(PicMap);{so we can quickdraw 'piece' into the PicMap} + tRect := aWindow^.portRect; + InsetRect(tRect,-50,-50);{in case mouse is near edge of window,clipping} + ClipRect(tRect); + SetOrigin(tRgn^^.rgnBBox.left,tRgn^^.rgnBBox.top); + EraseRect(tRgn^^.rgnBBox);{erase to white first} + FillRgn(tRgn,gray); + FrameRgn(tRgn); + SetOrigin(0,0); + SetPortBits(OldBits); + {__________________________} + + {____ initialize PatchMap, will be background under piece _____} + {restore, erase or draw initial BackGround under piece to PatchMap} + SetPortBits(PatchMap);{so we can quickdraw into the PatchMap} + {make the PatchMap topleft = to PieceRgn topleft for drawing, so + that the correct stuff is drawn into PatchMap for Piece location + in aWindow!} + SetOrigin(PieceRgn^^.rgnBBox.left,PieceRgn^^.rgnBBox.top); + EraseRect(PieceRgn^^.rgnBBox);{erase whole thing to white first} + {note: SetOrigin effects the aWindow^.portRect!!} + DrawWindowContents(aWindow);{draw window just like update event...} + SetOrigin(0,0);{restore normal origin} + SetPortBits(OldBits); + {___________________________} + + LastsRect := tRgn^^.rgnBBox;{last location in aWindow!} + sRect := LastsRect;{initialize sRect for size} + cRect := sRect; + LastMouseLoc := theMouse;{this is where user clicked} + MouseLoc := LastMouseLoc; + MouseLoc.h := MouseLoc.h + 1;{this will force redraw thru first loop} + Repeat { the entire process Until a MouseUp} + {determine If the mouse has moved and how much} + ExitToAnimate := false;{will flag mouse movement, need to draw stuff} + Repeat {wait for mouse move or mouse up} + {keep the piece on screen} + If not (PtInRect(MouseLoc,DragRect)) then Begin {move it back in} + If MouseLoc.h > DragRect.right then + MouseLoc.h := DragRect.right + Else If MouseLoc.h < DragRect.left then + MouseLoc.h := DragRect.left; + If MouseLoc.v > DragRect.bottom then + MouseLoc.v := DragRect.bottom + Else If MouseLoc.v < DragRect.top then + MouseLoc.v := DragRect.top; + End; + tPt.h := MouseLoc.h - LastMouseLoc.h;{tPt is offset requested} + tPt.v := MouseLoc.v - LastMouseLoc.v; + If (tPt.h <> 0) OR (tPt.v <> 0) then Begin {must have moved so animate} + {tPt move must be less than MaxMove} + If tPt.h > MaxMove.h then tPt.h := MaxMove.h + Else If tPt.h < -MaxMove.h then tPt.h := -MaxMove.h; + + If tPt.v > MaxMove.v then tPt.v := MaxMove.v + Else If tPt.v < -MaxMove.v then tPt.v := -MaxMove.v; + + {slide sRect to new location by tPt offset} + sRect.left := sRect.left + tPt.h; + sRect.right := sRect.right + tPt.h; + sRect.top := sRect.top + tPt.v; + sRect.bottom := sRect.bottom + tPt.v; + + {LastMouse is moved to the 'adjusted' location} + LastMouseLoc.h := tPt.h + LastMouseLoc.h; + LastMouseLoc.v := tPt.v + LastMouseLoc.v; + ExitToAnimate := True;{TML users can do a 'Leave' here!} + End;{If (abs..} + GetMouse(MouseLoc); + Until (not(StillDown) or ExitToAnimate); + + {combine/union the Last and new sRects, in the aWindow/screen} + UnionRect(LastsRect,sRect,sUnionRect); + LastcRect := LastsRect;{copy the sRects to cRects in ComboMap} + cRect := sRect; + cUnionRect := sUnionRect; + + {offset/slide all the cRects (combo) rects so cUnion is topleft} + LastcRect.right := LastcRect.right-cUnionRect.left; + LastcRect.left := LastcRect.left-cUnionRect.left; + LastcRect.bottom := LastcRect.bottom-cUnionRect.top; + LastcRect.top := LastcRect.top-cUnionRect.top; + cRect.right := cRect.right-cUnionRect.left; + cRect.left := cRect.left-cUnionRect.left; + cRect.bottom := cRect.bottom-cUnionRect.top; + cRect.top := cRect.top-cUnionRect.top; + cUnionRect.right := cUnionRect.right-cUnionRect.left; + cUnionRect.left := cUnionRect.left-cUnionRect.left; + cUnionRect.bottom := cUnionRect.bottom-cUnionRect.top; + cUnionRect.top := cUnionRect.top-cUnionRect.top; + + {copy current screen Union to ComboMap} + CopyBits(aWindow^.portBits,ComboMap,sUnionRect, + cUnionRect,srcCopy,NIL); + + {copy patch over last in combo.. will restore previous background} + CopyBits(PatchMap,ComboMap,PatchMap.bounds,LastcRect,srcCopy,NIL); + + CopyBits(ComboMap,PatchMap,cRect,PatchMap.bounds,srcCopy,NIL); + + {copy the piece into new location in Combo} + {move the tRgn to cRect to mask drawing into the Combo map} + OffSetRgn(tRgn,cRect.left - tRgn^^.rgnBBox.left, + cRect.top - tRgn^^.rgnBBox.top); + Copybits(PicMap,ComboMap,PicMap.bounds,cRect,srcCopy,tRgn); + + {copy Combo union to screen} + CopyBits(ComboMap,aWindow^.portBits,cUnionRect, + sUnionRect,srcCopy,NIL); + + LastsRect := sRect;{remember where the last piece is drawn} + Until (not(StillDown));{Until the mouse button is released,i-259} + + {________ optional to erase the piece or leave it, etc. _____} + {we'll restore the patch of background over the piece... erasing it} + CopyBits(PatchMap,aWindow^.portBits,PatchMap.bounds,sRect,srcCopy,nil); + {____________________} + + theMouse := LastMouseLoc;{return last mouse location to caller} + DisposPtr(PatchMap.baseAddr);{dispose of temp stuff in heap} + DisposPtr(ComboMap.baseAddr); + DisposPtr(PicMap.baseAddr); + DisposeRgn(tRgn); +End;{DragPiece procedure} + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups to finish} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; {startup the fonts manager} + InitWindows; {startup the window manager} + InitMenus; {startup the menu manager} + TEInit; {startup the text edit manager} + InitDialogs(Nil); {startup the dialog manager} + + Finished := False; {set program terminator to false} + FlushEvents(everyEvent,0); {clear events from previous program} + { set up screen size stuff } + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do Begin + SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); + SetRect(GrowArea,Left,Top+24,Right,Bottom); + End; +End; + +procedure CreateWindow; +Begin + SetRect(tRect,2,40,508,40 + 290); + myWindow := NewWindow(Nil,tRect,'DragPiece Demo',True,4,Nil,True,0); + SetPort(myWindow); + ClipRect(myWindow^.portRect); +End; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetItem(myMenus[1],theItem,name); + refNum := OpenDeskAcc(name); + End; + fileMenu: Finished := True; + End; + HiliteMenu(0); +End; + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + MouseLoc: Point; + WindowLoc: integer; +Begin + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {I-441} + inContent: + If WhichWindow <> FrontWindow then + SelectWindow (WhichWindow) + else Begin + GlobalToLocal(MouseLoc); + SetRect(tRect,MouseLoc.h-20,MouseLoc.v-26, + MouseLoc.h+20,MouseLoc.v+26); + DragRgn := NewRgn; + OpenRgn; + FrameOval(tRect); + CloseRgn(DragRgn); + + tRect := WhichWindow^.portRect; + DragPiece(WhichWindow,MouseLoc,tRect,DragRgn); + end; + inGoAway: + Finished := True; + end; {case of} +end; { TakeCareMouseDown } + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); +End; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect);{ or UpdateWindow^.VisRgn^^.rgnBBox } + DrawWindowContents(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure MainEventLoop; +var + myEvent: EventRecord; + EventAvail: Boolean; +Begin + InitCursor; + Repeat + SystemTask; + EventAvail := GetNextEvent(EveryEvent,myEvent); + If EventAvail then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: Finished:= True; + ActivateEvt:TakeCareActivates(myEvent); + UpDateEvt: TakeCareUpdates(myEvent); + End; + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + myMenus[2] := GetMenu(fileMenu); + For i := 1 to lastMenu do InsertMenu(myMenus[i],0); + DrawMenuBar; +End; + +{Main Program begins here} +BEGIN + InitThings; + MaxApplZone; + SetUpMenus; + CreateWindow; + + {set destination for bottom right of our logo picture} + SetPt(LogoLoc,myWindow^.portRect.right-20,myWindow^.portRect.bottom-20); + + MainEventLoop;{until finished = true} +END. diff --git a/Duane Blehm's Code/Drag Piece Æ’/aDragPiece.rsrc b/Duane Blehm's Code/Drag Piece Æ’/aDragPiece.rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/More Info*/Programming the Mac.4.6 b/Duane Blehm's Code/More Info*/Programming the Mac.4.6 new file mode 100755 index 0000000..3fb3fef Binary files /dev/null and b/Duane Blehm's Code/More Info*/Programming the Mac.4.6 differ diff --git a/Duane Blehm's Code/More Info*/TML Std.Procedures b/Duane Blehm's Code/More Info*/TML Std.Procedures new file mode 100755 index 0000000..e8b0c88 --- /dev/null +++ b/Duane Blehm's Code/More Info*/TML Std.Procedures @@ -0,0 +1 @@ +The following are TML 2.5 Standard Procedures and Functions that you may find in the sourcecode: inc(x).. increments the value of x or x := x +1. dec(x).. decrements.... etc. trunc(x).. converts a real-type value to longint value rounded down. round(x).. converts real to longint rounded to nearest whole number. odd(x).. tests whether integer is odd... true if odd. abs(x).. returns absolute value of number. sin(x).. sine of number (radians) cos(x).. cosine of number. concat(str1,str2).. merges strings. length(str).. gives number of characters in string. leave... exit the current loop immediately (For loops, Repeat... etc.) \ No newline at end of file diff --git a/Duane Blehm's Code/Regions Æ’/About Regions b/Duane Blehm's Code/Regions Æ’/About Regions new file mode 100755 index 0000000..55db975 Binary files /dev/null and b/Duane Blehm's Code/Regions Æ’/About Regions differ diff --git a/Duane Blehm's Code/Regions Æ’/Regions b/Duane Blehm's Code/Regions Æ’/Regions new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Regions Æ’/Regions.pas b/Duane Blehm's Code/Regions Æ’/Regions.pas new file mode 100755 index 0000000..5c66c82 --- /dev/null +++ b/Duane Blehm's Code/Regions Æ’/Regions.pas @@ -0,0 +1,776 @@ +Program aRegions;{Sun Aug 23, 1987 09:19:36} +{version 0.87,copyright ©,1987...Duane Blehm,HomeTown Software} + +USES MacIntf; + +{$T APPL RDBN set the TYPE and CREATOR} +{$B+ set the application's bundle bit } +{$L aRegions.rsrc}{link resource file stuff} + +CONST + lastMenu = 4; {Number of Menus} + appleMenu = 1; + fileMenu = 256; + editMenu = 257; + optionsMenu = 258; + +TYPE + {type the dialogs so we can access by name and add to list} + DialogList = (Save,Help,SizeTheRgn,About,HowTo,Source); + +VAR + tRgn: RgnHandle; {temporary region,used to Invert DemoRgn} + tStr: Str255; + myPattern: Pattern; {will hold the background pattern} + DemoRgn: RgnHandle; {region to be used in demo animation} + DemoUnderWay: Boolean; {flag if demo is underway} + i: integer; + Demo,InvertDemo: ControlHandle; {Rgn Demo and Invert It! buttons} + OffScreen: BitMap; {contains our offscreen MacPic image} + MacPic: PicHandle; {for loading PICT resource} + WorkRefNum: integer; {work resource file RefNum} + alertResult: integer; {result of putting up Alert dialogs} + DisplayStatus: integer; {what's on display,0 = nothing,1 = pic,2 = rgn} + DialNdx: DialogList; {will step through our dialogs array} + myDialog: Array[Save..Source] of DialogPtr;{our 'dialogList'} + myDialogRec: Array[Save..Source] of DialogRecord; + + WorkRect: Rect; {'work'ing/drawing area of myWindow,left side} + CreateRgn,SaveRgn: ControlHandle; {Create Rgn,Save Rgn buttons} + myPic: PicHandle; {Handle to Pic pasted in from Clipboard} + PicRect: Rect; {Rect for offsetting/Drawing myPic} + DemoBox: Rect; {Bounds for movement of demo animation} + myRgn: RgnHandle; {Region created by alogrithm} + RgnFrame, {Rect limits for FindRegion call...} + tRect: Rect; + length,offSet, + theErr: Longint; + + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu, + theItem: integer;{menustuff} + Finished: Boolean;{used to terminate the program} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; + wRecord: WindowRecord; + Screen,DragArea, + GrowArea: Rect; + +{----------------------------------------------} +procedure AnimateDemo; +{animate the mac image clipped to DemoRgn} +var + tPoint:Point; + dh,dv:integer; +Begin + GetMouse(tPoint);{get current mouse coordinates} + + {limit mac image (PicRect) to DemoBox extremes..} + {to keep PicRect from wondering off our visual area} + + If not(PtInRect(tPoint,DemoBox)) then begin {force tPoint into DemoBox} + If tPoint.h > DemoBox.right then tPoint.h := DemoBox.right + else if tPoint.h < DemoBox.left then tPoint.h := DemoBox.left; + If tPoint.v > DemoBox.bottom then tPoint.v := DemoBox.bottom + else if tPoint.v < DemoBox.top then tPoint.v := DemoBox.top; + end; + + {determine horizontal offset if needed} + If tPoint.h > PicRect.left + 2 then begin + PicRect.left := PicRect.left + 3;{offsetRect to right} + PicRect.right := PicRect.right + 3; + end + else if tPoint.h < PicRect.left - 2 then begin + PicRect.left := PicRect.left - 3;{offsetRect to left} + PicRect.right := PicRect.right - 3; + end; + {vertical offset?} + If tPoint.v > PicRect.top + 1 then begin + PicRect.top := PicRect.top + 2; {only move 2 because of pattern} + PicRect.bottom := PicRect.bottom + 2; + end + else if tPoint.v < PicRect.top - 1 then begin + PicRect.top := PicRect.top - 2; {only move 2 because of pattern} + PicRect.bottom := PicRect.bottom - 2; + end; + + {ok... now draw it using the DemoRgn as a mask} + CopyBits(OffScreen,myWindow^.portBits,OffScreen.bounds, + PicRect,srcCopy,DemoRgn); +end; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +var + tRect: Rect; + OffRowBytes,SizeOfOff:integer; + OldBits:Bitmap; +Begin + MacPic := GetPicture(128);{load the Mac picture from resource file} + tRect := MacPic^^.picframe;{size tRect for creating offscreen} + + {calculate size/dimensions of OffScreen stuff} + with tRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(tRect,-left,-top);{move rect to 0,0 topleft} + end; { of with } + + OffScreen.baseAddr := QDPtr(NewPtr(SizeOfOff));{Create BitImage with Ptr} + OffScreen.rowbytes := OffRowBytes;{bytes / row in BitImage} + OffScreen.bounds := tRect; + + {draw the Mac Picture into OffScreen} + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(OffScreen); { our new BitMap } + FillRect(OffScreen.bounds,white); {erase our new BitMap to white} + DrawPicture(MacPic,OffScreen.bounds); {draw all the pictures} + ReleaseResource(handle(MacPic)); {done so dump picture from memory} + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure InitialDemo;{set up stuff for Demo Regions animation} +var + dh,dv,myErr:integer; +begin + myErr := noErr;{myErr will flag a resource loading error} + If DisplayStatus = 2 then begin {use the current rgn in the demo} + DemoRgn := NewRgn;{Create a fresh DemoRgn, disposed of the last} + CopyRgn(myRgn,DemoRgn);{Copy of current myRgn into DemoRgn} + end + else begin {no current myRgn so we'll load our own in from resources} + DemoRgn := RgnHandle(GetResource('RGN ',20843)); + myErr := ResError;{check for loading error} + DetachResource(Handle(DemoRgn));{take control from resource Manager} + end; + If myErr = noErr then begin {continue if no errors where encountered} + DemoUnderWay := True;{flag Demo animation in MainEventLoop} + {disable menus, other controls} + HiliteControl(CreateRgn,255);{disable} + HiliteControl(SaveRgn,255);{disable} + ShowControl(InvertDemo); + DisableItem(myMenus[4],2);{size the Rgn} + For i := 1 to LastMenu do DisableItem(myMenus[i],0);{disable menus} + DrawMenuBar; + SetCTitle(Demo,'End Demo');{so user can end the demo} + EraseRect(WorkRect);{erase the work area of window} + {find offsets to relocate DemoRgn in center of work area} + with DemoRgn^^.rgnBBox do begin + dh := ((WorkRect.right-(right-left)) div 2) - left; + dv := ((WorkRect.bottom-(bottom-top)) div 2) - top; + end; + OffSetRgn(DemoRgn,dh,dv);{center the rgnBBox} + FillRgn(DemoRgn,myPattern);{pattern of horizontal lines} + FrameRgn(DemoRgn);{outline the Rgn} + InsetRgn(DemoRgn,1,1);{so the animation won't erase Frame lines} + DemoBox := DemoRgn^^.rgnBBox;{DemoBox will limit movement of PicRect} + InsetRect(DemoBox,-120,-80);{expand beyond the Rgn a little} + PicRect := OffScreen.bounds;{Size PicRect for CopyBits} + + {we'll use Mouse location and PicRect.topleft to offset PicRect + so subtract width and height of PicRect from DemoBox limits} + DemoBox.right := DemoBox.right - (PicRect.right - PicRect.left); + DemoBox.bottom := DemoBox.bottom - (PicRect.bottom - PicRect.top); + + If not(odd(DemoBox.top)) then inc(DemoBox.top);{force odd for pattern} + + {start the PicRect in upper left of DemoBox} + OffSetRect(PicRect,DemoBox.left-PicRect.left,DemoBox.top-PicRect.top); + end + else begin + alertResult := NoteAlert(134,nil);{sorry, bad resource} + ResetAlrtStage; + end; +end; + +procedure DisplayDialog(WhichDialog:DialogList); +var + tRect,fRect: Rect; + itemHit,i,j,tIDNum,RepeatID,count: integer; + tPort: GrafPtr; + tHandle,nameHand,tbuf:Handle; + tStr,nameStr:Str255; + aLong:Longint; + tFlag:boolean; + theType:ResType; +Begin + GetPort(tPort);{save the current port} + ShowWindow(myDialog[WhichDialog]); + SelectWindow(myDialog[WhichDialog]); + SetPort(myDialog[WhichDialog]); {we may draw into our dialog window} + + Case WhichDialog of + + HowTo:begin {text for how to use a 'RGN ' resource} + TextFont(geneva); + Textsize(10); + tbuf := GetResource('TBUF',128);{text created with our TransText utility} + HLock(tbuf);{lock it we'll be using ptr's} + TextBox(tbuf^,GetHandleSize(tBuf),myDialog[HowTo]^.portRect,teJustLeft); + HUnLock(tBuf);{done} + ModalDialog(Nil,itemHit); + end; + + Source:begin {text for Source code offer} + TextFont(geneva); + Textsize(10); + tbuf := GetResource('TBUF',129); + HLock(tbuf); + TextBox(tbuf^,GetHandleSize(tBuf),myDialog[Source]^.portRect,teJustLeft); + HUnLock(tBuf);{done} + ModalDialog(Nil,itemHit); + end; + + About:ModalDialog(nil,itemHit); {put up our about Regions dialog} + + SizeTheRgn:{user input data for doing an InsetRgn call} + Repeat + SelIText(myDialog[SizetheRgn],5,0,32767);{select horiz.size text} + ModalDialog(nil,itemHit); + If itemHit = 1 then Begin {size the rgn} + GetDItem(myDialog[SizetheRgn],5,i,tHandle,tRect);{Handle textitem} + GetIText(tHandle,tStr);{get the horiz. size text} + StringToNum(tStr,aLong);{convert to Number,error check?} + i := aLong;{horiz.size} + GetDItem(myDialog[SizetheRgn],6,j,tHandle,tRect);{vertical size} + GetIText(tHandle,tStr); + StringToNum(tStr,aLong); + j := aLong; + InsetRgn(myRgn,i,j); + DisplayStatus := 2;{so InvalRect 'update' will draw the region} + end; + Until((itemHit = 1) or (itemhit = 7));{size or cancel} + + Help:ModalDialog(nil,itemHit);{help dialog box} + + Save: Begin {save myRgn into our work resource file} + nameStr := 'myRegion';{set default Res. name} + Repeat + {get a 'unique' resID number > 128} + Repeat + tIDNum := UniqueID('RGN '); + Until(tIDNum > 127); + {install resID in item 4, name in item 3} + NumToString(tIDNum,tStr); + GetDItem(myDialog[Save],4,i,tHandle,tRect); + SetIText(tHandle,tStr);{set res Id to unique ID} + SelIText(myDialog[Save],4,0,32767);{select the res Id text} + GetDItem(myDialog[Save],3,i,nameHand,tRect); + SetIText(nameHand,NameStr); + ModalDialog(Nil,itemHit); {close it no matter what was hit} + Case itemHit of + 1:{add it} begin + {get,check name and id, watch out for duplicates} + GetIText(nameHand,nameStr);{nameString} + GetIText(tHandle,tStr); + StringtoNum(tStr,aLong); + tIdNum := aLong; + + {check for resource using tIDNum as ID#} + count := CountResources('RGN ');{how many rgns} + tFlag := True;{initial flag for duplicate Id numbers} + If Count > 0 then {if there any RGN'S} + For i := 1 to count do begin {step thru 'RGN ' resources} + tHandle := GetIndResource('RGN ',i); + GetResInfo(tHandle,j,theType,tStr); + If j = tIdNum then tFlag := false;{id already exists!} + end; + + If tFlag then begin {unique id, so save it} + UseResFile(WorkRefNum); + AddResource(Handle(myRgn),'RGN ',tIdNum,nameStr); + UpdateResFile(WorkRefNum); + end + Else begin {id alreay used, alert user, Replace it?} + alertResult := CautionAlert(128,nil); + ResetAlrtStage; + If alertResult = 1 then begin {replace old with new} + {tIDNum is the repeated ID no.!} + tHandle := GetResource('RGN ',tIdNum);{handle to old} + RmveResource(tHandle);{remove the old} + UseResFile(WorkRefNum);{our Work file} + AddResource(Handle(myRgn),'RGN ',tIdNum,nameStr); + UpDateResFile(WorkRefNum);{force write of new} + end + else itemHit := 0;{Cancel,reset itemhit so won't exit Save:} + end;{else} + end;{1:} + + end;{case itemhit} + Until((itemHit = 1) or (itemHit = 2)); + end;{Save:} + end;{Case WhichDialog} + + HideWindow(myDialog[WhichDialog]); {put away dialog window} + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} + InvalRect(WorkRect);{force redraw of 'whole' work area.. new Rgn?} +end; + +procedure FindRegion(tRect:Rect;var theRgn:RgnHandle); +{be sure that tRect is in visible window on screen before executing this +procedure to avoid system crash} +var + x,y:integer; + ExitNoBlack: Boolean; + Vector,LineVector,ExitStatus:integer; + Original:Point; + SizeCount:integer; +Begin + SetEmptyRgn(theRgn);{in case we have to abort, will return empty} + {scanning by 'pixels' with GetPixel() ... 'pixel' is right and left + of the 'Coordinate' so move inside 1 coordinate} + dec(tRect.right); + dec(tRect.bottom); + x := tRect.left;{we'll begin in topleft, looking for topleftmost black pixel} + y := tRect.top; + {find upper left black pixel} + ExitNoBlack := false; + While ((not GetPixel(x,y)) and (not ExitNoBlack)) do + Begin + If x < tRect.right then inc(x) {move right on line} + else begin {move down to next line} + x := tRect.left;{reset x to left side of line} + If y < tRect.bottom then inc(y) + else ExitNoBlack := true; {exit!,didn't find any black pixels} + end;{else} + end;{while} + + If not(ExitNoBlack) then begin {have a Black pixel..x,y so start region} + OpenRgn; + SetPt(Original,x,y); {keep track of starting point} + MoveTo(x,y); + + LineVector := 1;{track direction of line, won't LineTo() until it changes} + Vector := 1; {first vector is down (1)} + inc(y);{move coordinates to next} + ExitStatus := 0;{1 = 'Original' found, rgn complete, 2 = rgn too large} + SizeCount := 0;{count LineTo's for size of region,avoid overflow} + + {from Original begin 'counterclockwise' circuit around Black pixel border} + Repeat + Case Vector of {case 'last vector move' of.. get next vector} + 1: Begin {last vector move was down} + {if pixel left and below is black then move left} + If GetPixel(x-1,y) then Vector := 4 + + {if not, then check pixel right and below... move down} + else If GetPixel(x,y) then Vector := 1 + {if not, then must be pixel right and above... move right} + else Vector := 2; + end; + 2: Begin {last was right} + If GetPixel(x,y) then Vector := 1 + else If GetPixel(x,y-1) then Vector := 2 + else Vector := 3; + end; + 3: Begin {last was move up} + If GetPixel(x,y-1) then Vector := 2 + else If GetPixel(x-1,y-1) then Vector := 3 + else Vector := 4; + end; + 4: Begin {last was move left} + If GetPixel(x-1,y-1) then Vector := 3 + else If GetPixel(x-1,y) then Vector := 4 + else Vector := 1; + end;{of case 4:} + End; {of case vector} + + If Vector <> LineVector then begin{new direction,end of current 'line'} + SystemTask;{keep system happy?} + LineTo(x,y);{include line into region} + + {sizeCount limits number of LineTo()'s, to avoid Stack crashes} + If SizeCount < 4000 then inc(SizeCount) {we'll get another line} + else begin {too much!, getting too big!.. abort the region} + ExitStatus := 2;{force exit of loop} + LineTo(Original.h,Original.v);{we'll show the Region} + end; + + LineVector := Vector;{start a new line} + end; + + Case Vector of {we checked for new 'line',etc. so move coordinates} + 1:inc(y);{vector moves down} + 2:inc(x);{vector moves right} + 3:dec(y);{moves up} + 4:dec(x);{moves left} + end;{case vector} + + If x = Original.h then {is the new Coordinate our 'Original'} + If y = Original.v then begin + ExitStatus := 1;{finished if it is!,will force exit} + LineTo(x,y);{last line} + end; + + Until (ExitStatus <> 0);{until we get back to start or too large} + + CloseRgn(theRgn);{we're done so close the rgn} + InitCursor;{in case of alerts} + If ExitStatus = 2 then begin {display the abort rgn alert} + alertResult := NoteAlert(136,nil);{rgn aborted too big} + ResetAlrtStage; + end; + end {if not(Done)} + else begin {display no black pix alert} + InitCursor;{show arrow cursor for alert} + alertResult := NoteAlert(135,nil);{no black pixels} + ResetAlrtStage; + end; +End; + +procedure DrawWindowContents(WhichWindow:WindowPtr); +{Remember to SetPort first, response to update event} +var + trect:Rect; + i:integer; + tRgn:RgnHandle; + tStr:Str255; +Begin + ClipRect(WorkRect);{limit drawing to WorkRect} + Case DisplayStatus of + 1:Begin {picture on display} + DrawPicture(myPic,PicRect);{draw clipboard picture} + end; + 2:Begin {region on display} + OffSetRgn(myRgn,5-myRgn^^.rgnBBox.left,5-myRgn^^.rgnBBox.top); + FillRgn(myRgn,ltGray); + FrameRgn(myRgn);{will appear same coords as pict} + end; + end;{case displayStatus} + ClipRect(myWindow^.portRect);{set clip to window borders.. controls} + MoveTo(WorkRect.right,WorkRect.top);{draw right work border line} + LineTo(WorkRect.right,WorkRect.bottom); + DrawControls(WhichWindow); +End; + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups to finish} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; {startup the fonts manager} + InitWindows; {startup the window manager} + InitMenus; {startup the menu manager} + TEInit; {startup the text edit manager} + InitDialogs(Nil); {startup the dialog manager} + + Finished := False; {set program terminator to false} + FlushEvents(everyEvent,0); {clear events from previous program} + { set up screen size stuff } + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do { Screen.Left, etc. } + Begin + SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); + SetRect(GrowArea,Left,Top+24,Right,Bottom); + End; +End; + +procedure CreateWindow; +var + tRect: Rect; +Begin + SetRect(tRect,2,24,508,338);{size the window} + + myWindow := NewWindow(@wRecord,tRect,'',True,2,Nil,True,0); + + SetRect(tRect,0,0,520,340);{size of clip rgn} + For DialNdx := Save to Source do begin {read all the dialogs into array} + myDialog[DialNdx] := + GetNewDialog(ord(DialNdx)+128,@myDialogRec[DialNdx],myWindow); + SetPort(myDialog[DialNdx]); + ClipRect(tRect);{set clip to smaller size..} + end; + + SetPort(myWindow); + ClipRect(tRect); + + SetRect(tRect,416,35,502,59);{size/location of 1st control button} + CreateRgn := NewControl(myWindow,tRect,'Create Rgn',True,0,0,0,0,0); + OffSetRect(tRect,0,36); + SaveRgn := NewControl(myWindow,tRect,'Save Rgn',True,0,0,0,0,0); + OffSetRect(tRect,0,36); + Demo := NewControl(myWindow,tRect,'Rgn Demo',True,0,0,0,0,0); + OffSetRect(tRect,0,36); + InvertDemo := NewControl(myWindow,tRect,'Invert It!',False,0,0,0,0,0); +End; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: Begin + If theItem = 1 then DisplayDialog(About) + Else begin + GetPort(tPort); + GetItem(myMenus[1],theItem,name);{must be a desk acc.} + refNum := OpenDeskAcc(name); + SetPort(tPort); + end; + End; + fileMenu: Finished := True; + editMenu: + {if systemEdit returns false then process click at our end} + If not(SystemEdit(theItem - 1)) then + Case theItem of + 5:begin {paste} + {call GetScrap and draw picture into window} + length := GetScrap(Handle(myPic),'PICT',offset); + If length < 0 then begin {no PICT type scrap available!} + alertResult := NoteAlert(129,nil);{sorry no picture} + ResetAlrtStage; + end + else begin {we've got a picture waiting, 'paste' it} + PicRect := myPic^^.picframe; + OffSetRect(PicRect,5 - PicRect.left,5 - PicRect.top); + {check to see it picture is too large for work area} + If (PicRect.right > (WorkRect.right-6)) or + (PicRect.bottom > (WorkRect.bottom-6)) then Begin + {alert user... pic too large!} + alertResult := NoteAlert(130,nil); + ResetAlrtStage; + DisplayStatus := 0;{display nothing} + InvalRect(WorkRect);{force update redraw of work} + end + else begin {draw the scrap picture!} + EraseRect(WorkRect); + ClipRect(WorkRect); + DrawPicture(myPic,PicRect); + ClipRect(myWindow^.portRect); + RgnFrame := PicRect; + {enlarge by one pixel,to ensure white pixel border!} + InsetRect(RgnFrame,-1,-1); + DisplayStatus := 1;{flag picture on display} + HiliteControl(CreateRgn,0);{enable create a rgn} + HiliteControl(SaveRgn,255);{disable} + DisableItem(myMenus[4],2);{size} + end;{else} + end;{else} + end;{5:} + 6:begin {clear to original start up status} + DisplayStatus := 0; + InvalRect(myWindow^.portRect); + HiliteControl(CreateRgn,255); + HiliteControl(SaveRgn,255);{disable} + DisableItem(myMenus[4],2);{size} + end;{6:} + end;{case theitem} + + optionsMenu: + Case theItem of + 1:DisplayDialog(Help); + 2:DisplayDialog(SizeTheRgn); + 3:DisplayDialog(HowTo); + 4:DisplayDialog(Source); + end;{case theItem} + End; + HiliteMenu(0);{take hilite off menubar} +End; + +procedure TakeCareControls(whichControl:ControlHandle;localMouse:point); +var + ControlHit,i: integer; + refnum:integer; +Begin + ControlHit := TrackControl(whichControl,localMouse,nil); { Find out which} + If ControlHit > 0 then {i-417} + Begin + If whichControl = CreateRgn then Begin + {handle a hit in the Create region button} + SetCursor(ClockCursor^^);{while we work on the region} + myRgn := NewRgn; + FindRegion(RgnFrame,myRgn); + DisplayStatus := 2; + InvalRect(workRect); + HiliteControl(CreateRgn,255);{disable} + HiliteControl(SaveRgn,0); + EnableItem(myMenus[4],2);{size rgn} + End; + If whichControl = SaveRgn then Begin + DisplayDialog(Save);{will handle all the save stuff} + end; + If whichControl = Demo then begin {could be begin or end of demo!} + If DemoUnderWay then begin {then end it} + DemoUnderWay := False;{stop the animation} + {enable menus, other controls} + SetCTitle(Demo,'Rgn Demo'); + HideControl(InvertDemo); + InvalRect(WorkRect); + For i := 1 to LastMenu do EnableItem(myMenus[i],0); + DrawMenuBar; + DisposHandle(Handle(DemoRgn));{dump DemoRgn from memory} + If DisplayStatus = 2 then begin {still have a valid myRgn} + HiliteControl(SaveRgn,0); + EnableItem(myMenus[4],2);{size rgn} + end + else DisplayStatus := 0;{won't preserve picture?} + end {if demoUnderway begin} + else InitialDemo; {start the demo} + end;{if whichcontrol} + If whichControl = InvertDemo then Begin {invert the region} + FillRgn(DemoRgn,white);{fill with white, will erase pattern,etc.} + FrameRgn(DemoRgn);{frame is just 'inside' the rgn} + RectRgn(tRgn,WorkRect);{tRgn of WorkRect} + DiffRgn(tRgn,DemoRgn,DemoRgn);{DemoRgn out of tRgn,new in DemoRgn} + FillRgn(DemoRgn,myPattern);{Fill new DemoRgn with horz.lines} + HideControl(InvertDemo);{we can't invert it again.. so hide button} + end; + End; {of If ControlHit} +End; { of procedure} + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + MouseLoc: Point; + WindowLoc: integer; + ControlHit: integer; + WhichControl:ControlHandle; +Begin + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {I-441,scrapbook,etc.} + inContent: + If WhichWindow <> FrontWindow then + SelectWindow(WhichWindow) {bring window to front} + else Begin {check for hit in control buttons} + GlobaltoLocal(MouseLoc); + ControlHit := FindControl(MouseLoc,whichWindow,whichControl); + If ControlHit > 0 then TakeCareControls(whichControl,Mouseloc); + end; + end; {case of} +end; { TakeCareMouseDown } + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + If odd(myEvent.modifiers) then begin {becoming active} + SetPort(WhichWindow); + {disable undo,cut,copy} + DisableItem(myMenus[3],1); + DisableItem(myMenus[3],3); + DisableItem(myMenus[3],4); + end + else begin {deactivated must be desk accessory} + {enable all the Edit stuff} + For i := 1 to 6 do EnableItem(myMenus[3],i);{for DA's} + end; +End; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect); + DrawWindowContents(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure TakeCareKeyDown(Event:EventRecord); +Var + KeyCode,i: integer; + CharCode: char; +Begin + { KeyCode := LoWord(BitAnd(Event.message,keyCodeMask)) div 256; not used } + CharCode := chr(LoWord(BitAnd(Event.message,CharCodeMask))); + + If BitAnd(Event.modifiers,CmdKey) = CmdKey then begin + {key board command - probably a menu command} + DoMenuCommand(MenuKey(CharCode)); + end; +End; + +procedure MainEventLoop; +var + myEvent: EventRecord; + EventAvail: Boolean; +Begin + InitCursor; + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: TakeCareKeyDown(myEvent); + ActivateEvt: TakeCareActivates(myEvent); + UpDateEvt: TakeCareUpdates(myEvent); + End + else If DemoUnderWay then AnimateDemo;{animate one step of demo} + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + myMenus[3] := GetMenu(editMenu); + myMenus[4] := GetMenu(optionsMenu); + DisableItem(myMenus[4],2);{size region} + For i := 1 to lastMenu do + begin + InsertMenu(myMenus[i],0); + end; + DrawMenuBar; +End; + +procedure CloseStuff; +Begin + {always kill sound i/o before quit} + CloseResFile(WorkRefNum);{close work resource file} +End; + +{Main Program begins here} +BEGIN + InitThings; + MaxApplZone;{grow application zone to max... Scrapbook uses appl.heap} + theErr := ZeroScrap;{initialize the Scrap, erases any existing scrap, i-458} + SetUpMenus; + CreateWindow; + CreateOffScreenBitMap; + myPic := picHandle(NewHandle(0));{create valid handle for GetScrap call} + myRgn := NewRgn;{create the rgns} + tRgn := NewRgn; + WorkRect := myWindow^.portRect;{size the work area of window} + WorkRect.right := 410; + DisplayStatus := 0;{nothing is on display} + HiliteControl(CreateRgn,255);{disable} + HiliteControl(SaveRgn,255);{disable} + CreateResFile('RgnWork.rsrc');{no action if it already exists} + {will be created in same folder as Regions application?} + WorkRefNum := OpenResFile('RgnWork.rsrc');{open for action.. save} + DemoUnderWay := false;{no demo to start} + GetIndPattern(myPattern,sysPatListID,25);{horiz.line pattern in systemfile} + MainEventLoop; + CloseStuff; +END. diff --git a/Duane Blehm's Code/Regions Æ’/RgnWork.rsrc b/Duane Blehm's Code/Regions Æ’/RgnWork.rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Regions Æ’/aRegions.R b/Duane Blehm's Code/Regions Æ’/aRegions.R new file mode 100755 index 0000000..0620cd9 --- /dev/null +++ b/Duane Blehm's Code/Regions Æ’/aRegions.R @@ -0,0 +1,35 @@ +* aRegions.R + +aRegions.rsrc + +TYPE RDBN = STR + ,0 +Regions ver0.87, Duane Blehm, 8/87 + +TYPE MENU + ,1 +\14 + About Regions.. + (- + + ,256 +File + Quit /Q + + ,257 +Edit + Undo /Z + (- + Cut /X + Copy /C + Paste /V + Clear + + ,258 +Options + Help + Size Region.. + Using Rgns.. + Source offer.. + +INCLUDE aRegions/Rsrc diff --git a/Duane Blehm's Code/Regions Æ’/aRegions:Rsrc b/Duane Blehm's Code/Regions Æ’/aRegions:Rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/StuntCopter Æ’/About StuntCopter files.. b/Duane Blehm's Code/StuntCopter Æ’/About StuntCopter files.. new file mode 100755 index 0000000..7f2c97c --- /dev/null +++ b/Duane Blehm's Code/StuntCopter Æ’/About StuntCopter files.. @@ -0,0 +1 @@ +About the StuntCopter files: 'StuntCopter.shapes' is a 'FullPaint' document containing the shapes that are used for animation, score, etc... can be opened from MacPaint. 'StuntCopter.Pas' is an 'Edit' text file containing pascal source code to be compiled by the Pascal compiler. 'StuntCopter.R' is an 'Edit' text file containing RMaker source code to be compiled by RMaker. 'StuntCopter.Rsrc' is a resource document containing resources built with ResEdit, the resource editor. These resources are 'included' when StuntCopter.R is compiled by RMaker. 'StuntCopter' is our application... the result of Link. \ No newline at end of file diff --git a/Duane Blehm's Code/StuntCopter Æ’/Anim.R b/Duane Blehm's Code/StuntCopter Æ’/Anim.R new file mode 100755 index 0000000..abf2d0c --- /dev/null +++ b/Duane Blehm's Code/StuntCopter Æ’/Anim.R @@ -0,0 +1,16 @@ +* Anim.R + +Anim.rsrc + +TYPE MENU + ,1 +\14 + + ,256 +File + OffScreen.. + Quit + +* will include Pict resource file built with ResEdit and ScrapBook +INCLUDE Anim/rsrc + diff --git a/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.R b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.R new file mode 100755 index 0000000..ddb3bba --- /dev/null +++ b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.R @@ -0,0 +1,33 @@ +* StuntCopter.R + +aSCopt.rsrc + +TYPE COPT = STR + ,0 +StuntCopter 1.5.. by Duane Blehm, 7/87 + +TYPE MENU + ,1 +\14 + About Stunt... + (- + + ,256 +File + Quit /Q + + ,257 +Options + ! Sound + Reset HiScore + Help.. + Set Speed.. + Source Code.. + OffScreen.. + + ,258 +(Backspace to Exit) + Hi! + +* the rest of the resources where built with ResEdit in StuntCopter.Rsrc +INCLUDE StuntCopter/Rsrc diff --git a/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.pas b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.pas new file mode 100755 index 0000000..6bab39d --- /dev/null +++ b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.pas @@ -0,0 +1,1823 @@ +Program StuntCopter;{Copyright © 1986,1987 by Duane Blehm, All Rights Reserved} + +{ Version 1.5 ,drag-able window, speed selection dialog for faster Macs} + +{note: we increased the speed of StuntCopter 20% by NOT expanding window +beyond edge of screen... must have something to do with Clipping} +{Our window is using our custom WDEF..Window definition resource, it only +has ability to drag,draw and calcRegions..no close,etc. just poke +(16 * Resource ID) into window template ProcID} + +{Format note: 'tabs' for this text should be set to '3'} +{StuntCopter animation is based on the CopyBits procedure. The various shapes +are loaded from three PICT resources and drawn into an OffScreen bitmap. +See Apple Tech Note #41 - 'Drawing into an OffScreen Bitmap'. Access +to these shapes is thru various Rectangles that describe their +position in the OffScreen bitmap. The destination OnScreen for the shape is an +identically sized rectangle that has been positioned (OffSetRect procedure) +to receive the drawing. + +Note: to move a given rectangle 'myRect' from its present location +(Current) to another location (Destination) the following is used throughout +this program... + + OffsetRect(myRect,Destination.h - Current.h,Destination.v - Current.v); +or + OffsetRect(myRect,DestRect.left - myRect.left,DestRect.top - myRect.top); + +Copter control is based around the MapPt procedure... by 'Mapping' the mouse +coordinates into a Rectangle (DeltaRect) sized according to the extreme +Copter moves in any direction. Shapes must have white borders equal to +these extreme coordinates because the next shape erases the previous by drawing +over it.: + + -3 x x x x x x x x x + -2 x x x x x x x x x + -1 x x x x x x x x x + 0 x x x x x x x x x < 1 then FlipSound[i]^.sound2Rate := 78264 + else FlipSound[i]^.sound2Rate := 0; + If i > 2 then FlipSound[i]^.sound3Rate := 98607 + else FlipSound[i]^.sound3Rate := 0; + If i > 3 then FlipSound[i]^.sound4Rate := 117264 + else FlipSound[i]^.sound4Rate := 0; + end; { for i} +End; + +procedure CreateStrings; +var + i:integer; +begin {i-468, get all the strings from resource file} + For i := 1 to lastString do GetIndString(myStrings[i],StringId,i); +end; + +procedure DrawAString(theString:Str255;h,v:integer); +begin + moveto(h,v); + DrawString(theString); +end; + +procedure DrawAllmyStrings; +var + tRect:Rect; +begin + TextFace([bold,underline]); + DrawAString(myStrings[1],(504-StringWidth(myStrings[1]))div 2,60);{centered} + TextFace([]); + DrawAString(myStrings[2],(504-StringWidth(myStrings[2]))div 2,80); + {lets draw a cloud} + tRect := Cloud[3]; {this is a cloud rect, lets draw one of our clouds} + OffSetRect(tRect,256-tRect.left,30-tRect.top);{locate it on the screen} + {now draw it with 'srcOr' mode so we won't disturb our text} + CopyBits(OffScreen,myWindow^.portBits,OffCloud[3],tRect,srcOr,Nil); +end; + +procedure CreateSound; +{we're writing direct to the sound driver with PBWrite... this procedure sets +up all the various buffers, Parm blocks, and such for the three different +types of sounds used in this game, the copter engine is a freeform sound, +the fanfare played for a good jump is 4 fourtone sounds, and the splat is +a freeform sound. You can determine if a sound is finished by checking +the ioresult field of the Parmblock, if it is less than 1 then the sound +is done. In the mainloop we check it and start another sound if the last +one is done.... sometimes the Driver has changed ioresult but is not done +with the ParmBlock so writing another sound can be messed up.. buzzing, +this can be avoided by waiting for Tickcount to increment once,or by doing +a PBKillIO. We use the PBKill when we want to switch sounds before they +are complete... this avoids the problem of our program trying to write to +the Driver buffers at the same time the driver is trying to access them. +To avoid system errors always be sure to Kill sound I/O before exiting the +program!!.. and remember freeform sound slows the program by about 20%} + +Var + i,j: integer; +Begin + CoptBuff := 7406; { Create the Copter sound stuff,6 bytes for mode & count} + myHandle := NewHandle(CoptBuff); + HLock(myHandle); + myPtr := myHandle^; + CoptSound := FFSynthPtr(myPtr); + CoptSound^.mode := ffMode; {freeform mode} + CoptSound^.count := FixRatio(1,6); {fixed point notation} + CoptBuff := CoptBuff - 7; {this is size of WaveForm array'0-7399'} + For j := 0 to CoptBuff do CoptSound^.WaveBytes[j] := 127; {set all to 127} + j := 0; + While j<= CoptBuff do Begin + i := abs(Random) div 512; {random number 0 to 64} + CoptSound^.WaveBytes[j] := i; {fill up the buffer with copter sound} + if (j mod 370 = 100) then + begin + j:= j+200; + CoptSound^.WaveBytes[j] := 255; + j:= j+ 70; + end + else inc(j); + end; { of while} + + SplatBuff := 1486; { Create the Splat sound stuff } + myHandle := NewHandle(SplatBuff); + HLock(myHandle); + myPtr := myHandle^; + SplatSound := FFSynthPtr(myPtr); + SplatSound^.mode := ffMode; + SplatSound^.count := FixRatio(1,2); {fixed point notation} + SplatBuff := SplatBuff - 7; {this is size of WaveForm array '0-1479'} + j := 0; + i := 0; + While j<= SplatBuff do Begin + SplatSound^.WaveBytes[j] := i; {fill up the buffer} + If i < 255 then inc(i) else i := 0; {Sawtooth wave form} + inc(j); + end; { of while} + + new(Squarewave); {my wavePtr...describe a squarewave form for flip sound} + for i := 0 to 127 do + begin + Squarewave^[i] := 255; + Squarewave^[i+128] := 0; + end; + + new(FlipSynth); {my FTSynthPtr, FourTone Synthesizer} + FlipSynth^.mode := ftMode; + + FlipTime[1] := 10; FlipTime[2] := 5; {durations for flipsounds} + FlipTime[3] := 5; FlipTime[4] := 20; + + {note: the duration field must be reset after each flipsound as the driver + decrements its value} + + For i := 1 to 4 do begin {Build the four FourToneSndRecords} + new(FlipSound[i]); + FlipSound[i]^.duration := FlipTime[i]; {initial for each sound} + FlipSound[i]^.sound1Phase := 64; + FlipSound[i]^.sound2Phase := 192; + FlipSound[i]^.sound3Phase := 128; {out of phase just for fun} + FlipSound[i]^.sound4Phase := 0; + FlipSound[i]^.sound1Wave := Squarewave; + FlipSound[i]^.sound2Wave := Squarewave; + FlipSound[i]^.sound3Wave := Squarewave; + FlipSound[i]^.sound4Wave := Squarewave; + end; { of for i } + {remember must InitialSoundRates each game,at BeginButton press} + WhichSound := 0; + + new(SoundParmBlk); {TML standard procedure,10-1} + with SoundParmBlk^ do begin {see tech note 19, PBWrite vs. StartSound} + iocompletion := nil; + iorefnum := -4; + iobuffer := ptr(CoptSound);{coerce the Sound pointer to plain ptr} + ioreqcount := CoptBuff; + ioresult := 0; {will Start coptersound when game begins,MainEventLoop} + end; {of with} +end; + +procedure CreateWindow;{windows,dialogs, and controls} +var + h,v,width: integer; + tRect:Rect; +Begin + myWindow := GetNewWindow(WindResId,@wRecord,Pointer(-1)); + SetPort(myWindow); + ClipRect(myWindow^.PortRect); {i-166, set cliprgn to small rgn} + TextFont(0);{System font, should be Chicago unless its been altered} + HelpDialog := GetNewDialog(HelpId,@dRecord,myWindow); + AboutDialog := GetNewDialog(AboutId,@AboutdRecord,myWindow); + {our new dialogs} + SourceDialog := GetNewDialog(137,@SrcRec,myWindow); + SpeedDialog := GetNewDialog(138,@SpdRec,myWindow); + GetDItem(SpeedDialog,2,h,myHandle,tRect); + SetCtlValue(ControlHandle(myHandle),1);{click the Normal Box} + SpeedTrapOn := False; + BitMapDialog := GetNewDialog(139,@BitRec,myWindow); + + BeginButton := GetNewControl(pressBegin,myWindow); + ResumeButton := GetNewControl(pressResume,myWindow); + EndButton := GetNewControl(pressEnd,myWindow); + LevelButton := GetNewControl(pressLevel,myWindow); + + LevelOnDisplay := false; { flag when level is displayed} +{locate the control buttons in the center of myWindow.. Begin and Resume +are in same location as are End and Level} + width := myWindow^.portRect.right-myWindow^.portRect.left; + h := myWindow^.portRect.left + ((width-80) div 2); {center control} + v := 165; + SizeControl(BeginButton,80,26);MoveControl(BeginButton,h,v); + SizeControl(ResumeButton,80,26);MoveControl(ResumeButton,h,v); + SetRectRgn(BeginRgn,h,v,h+80,v+26); { BeginButton rect. region } + v := 200; + SizeControl(EndButton,80,26);MoveControl(EndButton,h,v); + SizeControl(LevelButton,80,26);MoveControl(LevelButton,h,v); + SetRectRgn(EndRgn,h,v,h+80,v+26); + CopyRgn(EndRgn,LevelUnion); + OffsetRgn(LevelUnion,-1,0); {used to mask level on scrolling CopterRgn} +End; + +procedure CreatePictures; {get 3 PICT's from resource file} +var + i: integer; +Begin + Copter := GetPicture(CopterId); {contains 3 Copters,3 Wagons,14 Flips} + CoptRect := Copter^^.picFrame; { i-159 } + Man := GetPicture(ManId); {contains 12 Men,2 thumbs,10 numbers,Cross,etc.} + ManRect := Man^^.picFrame; + ScoreBox := GetPicture(ScoreBoxId); {Score,status,yoke control,etc.} + ScoreBoxRect := ScoreBox^^.picFrame; +{cloudstuff} + For i := 1 to 3 do begin + CloudPic[i] := GetPicture(i+355); {the three cloud pictures} + Cloud[i] := CloudPic[i]^^.picFrame;{set the cloud Rects size} + OffCloud[i] := Cloud[i]; + CloudRgn[i] := RgnHandle(GetResource('RGN ',i+355));{regions for clouds} + {enlarge region so we can mask just inside it as we move to the left} + InsetRgn(CloudRgn[i],-1,0); + end; {for i} +end; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +const + OffLeft = 0; + OffTop = 0; + OffRight = 426; + OffBottom = 261; {size bitmap to contain all six PICTs} +var + bRect: Rect; +Begin + SetRect(bRect,Offleft,OffTop,OffRight,OffBottom); { drawing area } + with bRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(bRect,-left,-top); { local coordinates } + end; { of with } + + with OffScreen do begin; { create new BitMap record } + baseAddr := QDPtr(NewPtr(SizeOfOff));{big enough for all 6 picts} + rowbytes := OffRowBytes; + bounds := bRect; + end; { of with OffScreen } +End; + +procedure DrawPicsIntoOffScreen; +Begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(OffScreen); { our new BitMap } + {if offscreen bitmap is bigger than myWindow bitmap watchout for + clipping caused by ClipRgn and VisRgn fields of grafport record, you + can set cliprgn with ClipRect procedure and use CopyRgn procedure + to store old visrgn in temporary rgn... etc.} + + FillRect(myWindow^.PortRect,white); {erase our new BitMap to white} + + OffSetRect(ScoreBoxRect,-ScoreBoxRect.left,-ScoreBoxRect.top); + DrawPicture(ScoreBox,ScoreBoxRect); { ScoreBox stuff } + OffSetRect(CoptRect,-CoptRect.left, + ScoreBoxRect.bottom-CoptRect.top);{below ScoreBox} + DrawPicture(Copter,CoptRect); + OffSetRect(ManRect,CoptRect.right-ManRect.left, + ScoreBoxRect.bottom-ManRect.top); + DrawPicture(Man,ManRect); { right of Copter,below ScoreBox } + + ReleaseResource(handle(ScoreBox)); {done with Pictures so dump them} + ReleaseResource(handle(Man)); + ReleaseResource(handle(Copter)); + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure DrawCloudsIntoOffScreen; {draw the 3 clouds into offscreen} +var i:integer; +Begin + OldBits := myWindow^.portBits; {preserve old BitMap} + SetPortBits(OffScreen); { our new BitMap } + + OffSetRect(OffCloud[3],-OffCloud[3].left, + OffFlip[14].bottom-OffCloud[3].top);{left side,below flips} + DrawPicture(CloudPic[3],OffCloud[3]); + + OffSetRect(OffCloud[1],OffCross.right-OffCloud[1].left, + OffHorse.bottom-OffCloud[1].top);{right of cross,below deadhorse} + DrawPicture(CloudPic[1],OffCloud[1]); + + OffSetRect(OffCloud[2],OffCloud[3].right-OffCloud[2].left, + OffCloud[1].bottom-OffCloud[2].top);{right of cloud3,below cloud1} + DrawPicture(CloudPic[2],OffCloud[2]); + + ReleaseResource(handle(CloudPic[1])); + ReleaseResource(handle(CloudPic[2])); + ReleaseResource(handle(CloudPic[3])); + + {let's shrink our cloud borders...leave one pixel border on right side, + this will limit cloud movement to left only!} + {so now CloudRgn[]^^.RgnBBox.topleft will be same as Cloud[].topleft} + for i := 1 to 3 do begin + InsetRect(OffCloud[i],1,1);inc(OffCloud[i].right); + InsetRect(Cloud[i],1,1);inc(Cloud[i].right); + end;{for i} + + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure CreateOffScreenRects; +{ where are all those shapes? locate all the shapes in the OffScreen bitmap +by defining the rectangles that contain them. } +var + i: integer; + tRect: Rect; +Begin + OffScoreBox := ScoreBoxRect; {Scorebox is easy... already upper left} + + {find the 3 copters} + tRect := CoptRect; {here CoptRect is the whole Copter PICT.frame} + tRect.right := trect.left + 74; { width of one copter } + trect.bottom := tRect.top + 26; { height of copter } + for i := 1 to 3 do begin + OffCopter[i] := tRect; + OffSetRect(tRect,74,0); { 3 copters in a row } + end; + CoptRect := OffCopter[1]; {now CoptRect is set to size of first copter} + + {find the 3 wagons} + tRect.left := CoptRect.left; {left edge of OffScreen} + tRect.top := CoptRect.bottom; {3 wagons are just below copters} + tRect.right := trect.left + 73; { width of one wagon } + trect.bottom := tRect.top + 22; { height of wagon } + for i := 1 to 3 do begin + OffWagon[i] := tRect; + OffSetRect(tRect,73,0); { 3 wagons in a row } + end; + WagonRect := OffWagon[1]; {Size onscreen rect} + + {find the 14 flip shapes} + tRect.left := WagonRect.left; { topleft corner for reference } + tRect.top := WagonRect.bottom; {2 rows of 7 Flips just below wagons} + tRect.right := trect.left + 32; { width of one manflip } + trect.bottom := tRect.top + 41; { height of manflip } + for i := 1 to 7 do begin + OffFlip[i] := tRect; { 7 in top row } + OffSetRect(tRect,0,41); + OffFlip[i+7] := tRect; { 7 in bottom row } + OffSetRect(tRect,32,-41); + end; + OffFlip[15] := OffFlip[1]; { complete animation back to 'stand up' } + For i := 1 to 2 do FlipRect[i] := tRect; {Size onscreen Rects} + + {find men hanging,dropping,splat and thumb up/down shapes} + tRect := ManRect; {upper left corner of Man Picture} + tRect.right := trect.left + 14; { width of one man } + trect.bottom := tRect.top + 16; { height of man } + for i := 1 to 7 do begin + OffMan[i] := tRect; {7 in toprow,1=manhanging,2-6=dropping,7=thumbup} + OffSetRect(tRect,0,16); + OffMan[i+7] := tRect; {7 in bottom row,1-6=splat,7th=thumbdown} + OffSetRect(tRect,14,-16); + end; + ManRect := OffMan[1]; + ManHt := ManRect.bottom - ManRect.top; + ManWdth := ManRect.right - ManRect.left; + + {find the 10 numeral shapes used for score} + tRect.left := ManRect.left; + tRect.top := OffMan[8].bottom; {2 rows of 5 numerals below men} + tRect.right := trect.left + 20; {width of one number} + trect.bottom := tRect.top + 15; { height of number } + for i := 0 to 4 do begin + OffNum[i] := tRect; { 5 numerals '0-4'in top row } + OffSetRect(tRect,0,15); + OffNum[i+5] := tRect; { 5 numerals '5-9' in bottom row } + OffSetRect(tRect,20,-15); + end; + NumRect := tRect; + + tRect.left := ManRect.left;{cross/yoke in scorebox shows mouse movements} + tRect.top := OffNum[5].bottom; {cross/yoke is below numerals} + tRect.right := trect.left + 81; + trect.bottom := tRect.top + 81; { height of cross/yoke } + OffCross := tRect; + CrossRect := tRect; + + tRect.top := OffCross.top; {ManInWagon is drawn for safe landing} + tRect.left := OffCross.right; + tRect.Bottom := tRect.top + 10; + tRect.right := tRect.left + 28; + OffManInWagon := tRect; + ManInWagon := tRect; + + tRect.top := OffManInWagon.bottom; {Driver is drawn if driver is hit} + tRect.bottom := tRect.top + 22; + tRect.left := OffCross.right; + tRect.right := tRect.left + 40; + OffDriver := tRect; + DriverRect := OffDriver; + + tRect.top := OffDriver.bottom; {Horse is drawn if horse is hit} + tRect.bottom := tRect.top + 22; + tRect.left := OffCross.right; + tRect.right := tRect.left + 29; + OffHorse := tRect; + HorseRect := OffHorse; +End; + +procedure DisplayHelpDialog; +var + itemHit: integer; +Begin {Display help dialog window} + ShowWindow(HelpDialog); + SelectWindow(HelpDialog); + ModalDialog(Nil,itemHit); {We'll close it not matter what was hit} + HideWindow(HelpDialog); + SelectWindow(myWindow); +end; + +procedure DisplayAboutDialog;{ display the About Stunt... dialog window} +var + tRect,fRect: Rect; + itemHit,i: integer; + tPort: GrafPtr; +Begin {Display about dialog window} + GetPort(tPort); + ShowWindow(AboutDialog); + SelectWindow(AboutDialog); + SetPort(AboutDialog); {so we can draw into our window} + + tRect := FlipRect[1]; + with tRect do begin + right := 2*(right-left)+left; {enlarge 4 times} + bottom := 2*(bottom-top)+top; + end; + OffSetRect(tRect,AboutDialog^.portRect.right-40-tRect.right, + AboutDialog^.portRect.top+54-tRect.top); + fRect := tRect; + InsetRect(fRect,-2,-2); + FrameRect(fRect); + InsetRect(fRect,-1,-1); {draw a frame for the enlarged flip} + FrameRect(fRect); + InsetRect(fRect,-2,-2); + FrameRoundRect(fRect,8,8); + FillRect(tRect,gray); + + fRect := Cloud[3]; {this is a cloud rect, lets draw one of our clouds} + OffSetRect(fRect,120-fRect.left,-12-fRect.top); + CopyBits(OffScreen,AboutDialog^.portBits,OffCloud[3], + fRect,srcOr,Nil); + + Repeat + ModalDialog(Nil,itemHit); {find which button hit,OK or BACKFLIP} + + If itemHit = 4 then + begin { do a backflip } + For i := 1 to 15 do begin + CopyBits(OffScreen,AboutDialog^.portBits,OffFlip[i], + tRect,srcCopy,Nil); + aTick := TickCount + 10; + repeat until (TickCount > aTick); {pause...} + end; + FillRect(tRect,gray); {erase the last flipshape} + end; { of if itemHit} + Until ((itemHit = 3) or (itemHit = 1)); {the done button or 'enter' key} + HideWindow(AboutDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort); +end; + +procedure DisplaySourceDialog; +var + itemHit: integer; + tPort: GrafPtr; +Begin + GetPort(tPort); + ShowWindow(SourceDialog); + SelectWindow(SourceDialog); + SetPort(SourceDialog); + ModalDialog(Nil,itemHit); {close it no matter what was hit} + HideWindow(SourceDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} +end; + +procedure DisplayBitMapDialog; +var + itemHit: integer; + tPort: GrafPtr; +Begin + GetPort(tPort); + ShowWindow(BitMapDialog); + SelectWindow(BitMapDialog); + SetPort(BitMapDialog); + CopyBits(OffScreen,BitMapDialog^.portBits,OffScreen.bounds, + OffScreen.bounds,srcCopy,nil); + ModalDialog(Nil,itemHit); {close it no matter what was hit} + HideWindow(BitMapDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} +end; + +procedure SetControlValue(which:integer); +var + i,h:integer; + tRect:Rect; +Begin + For i := 2 to 4 do begin + GetDItem(SpeedDialog,i,h,myHandle,tRect); + If i = which then SetCtlValue(ControlHandle(myHandle),1) + else SetCtlValue(ControlHandle(myHandle),0); + end; +End; +procedure DisplaySpeedDialog; +var + itemHit,i: integer; + tPort: GrafPtr; +Begin + GetPort(tPort); + ShowWindow(SpeedDialog); + SelectWindow(SpeedDialog); + SetPort(SpeedDialog); {so we can draw into our dialog window} + + Repeat + ModalDialog(Nil,itemHit); {close it no matter what was hit} + Case itemHit of + 2:Begin + SetControlValue(2); + SpeedTrapOn := False; + end;{2:} + 3:Begin + SetControlValue(3); + SpeedFactor := 1; + SpeedTrapOn := True; + end; + 4:Begin + SetControlValue(4); + SpeedFactor := 2; + SpeedTrapOn := True; + end; + end;{case itemhit} + Until(itemHit = 1); + + HideWindow(SpeedDialog); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} +end; + +{note: we've since figured out a way to simplify putting up and adding +all the dialogs... we declare a ordinal TYPE called DialogList with +the names of our dialogs in sequence the way they're stored in resource +and declare arrays of DialogPtr's and Records! so that all dialogs can +be called from one procedure that uses a 'Case WhichDialog of' to display +the desired dialog...reference the 'name' from the DialogList} + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; + h: integer; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetPort(tPort); + If theItem = 1 then DisplayAboutDialog + Else begin + GetItem(myMenus[1],theItem,name);{must be a desk acc.} + refNum := OpenDeskAcc(name); + end; + SetPort(tPort); + End; + fileMenu: Finished := True; {quit this program} + optionMenu: + Case theItem of + 1:Begin {toggle sound on or off} + If SoundOn then SoundOn := false else SoundOn := true; + CheckItem(myMenus[3],theItem,SoundOn); + end; + 2: Begin {reset hiscore} + HiScore := 0; + DrawScoreIntoBox(HiScore,HiScoreNum); + end; + 3: DisplayHelpDialog; + 4: DisplaySpeedDialog; + 5: DisplaySourceDialog; + 6: DisplayBitmapDialog;{show our pics and shapes} + end; { case theItem} + End; + HiliteMenu(0); +End; + +procedure StartNewCloud; +{get one of 3 clouds, locate at right of screen,do all the rgn stuff} +var + tRect: Rect; + cloudheight:integer; +Begin + If CloudNdx < 3 then inc(CloudNdx) else CloudNdx := 1;{get the next cloud} + CloudHeight := abs(Random) div 256;{random between 0 and 128} + OffSetRect(Cloud[CloudNdx],512-Cloud[CloudNdx].left, + CloudHeight-Cloud[CloudNdx].top); + OffSetRgn(CloudRgn[CloudNdx],512-CloudRgn[CloudNdx]^^.rgnBBox.left, + CloudHeight-CloudRgn[CloudNdx]^^.rgnBBox.top); + {define region copter can be drawn in...will move with cloud} + tRect := FlightRect; + tRect.right := Cloud[CloudNdx].right + 514; {a screen width beyond cloud} + RectRgn(tempRgn,tRect); + DiffRgn(tempRgn,CloudRgn[CloudNdx],CopterRgn);{cloud out of the Copter area} +end; + +procedure InitialCopterStuff; +var + Dest: point; + i:integer; +Begin + OffsetRect(CoptRect,212-CoptRect.left,110-CoptRect.top); {dest. - source} + CoptNdx := 1; + OffsetRect(WagonRect,-WagonRect.left, + ScoreBoxRect.top-4-WagonRect.bottom); + + WagonNdx := 1; {set index to first wagon shape} + WagonMoving := True; + ManNdx := 1; {this is the man hanging} + Dh := 0;Dv := 0; { no initial copter movement } + + Score := 0; + MenLeft := 5; { # of men/level } + ManStatus := 4; { a man is hanging from the copter } + GoodJumps := 0; { # of successfull jumps } + WagonSpeed := 1; { Slowest, wagon will move 1 pixel per loop } + Gravity := 4; { fastest, man drops 4 pixels per loop } + CurrentLevel := 1; { keeps count of levels...} + + For i := 1 to 5 do begin { erase the thumbs...} + EraseRect(ThumbUp[i]); + EraseRect(ThumbDown[i]); + ThumbState[i] := 0; {none are drawn, keep track for 'update' drawing} + end; + + {cloudstuff} + CloudNdx := 3; {Which cloud is being drawn?} + StartNewCloud;{set up a cloud on right side and get all the regions ready} +End; + +procedure TakeCareControls(whichControl:ControlHandle;localMouse:point); +var + ControlHit,i: integer; +Begin + ControlHit := TrackControl(whichControl,localMouse,nil); { Find out which} + If ControlHit > 0 then {i-417} + Begin + If whichControl = BeginButton then {BEGIN a game} + Begin + InsertMenu(myMenus[4],0); + For i := 1 to 3 do DisableItem(myMenus[i],0); + DrawMenuBar; {display exit message} + HideControl(BeginButton); + InitialCopterStuff; {Reset game varibles to beginning} + DrawScoreIntoBox(Score,ScoreNum);{overwrite previous score,zero} + DrawWagonStatus; { into Scorebox } + InitialSoundRates; {reset pitch of flipsounds} + InvertRect(ScoreMan[1]); {hilite first man in scorebox} + GameUnderWay := True; { animation loop branch is active} + CloudOnDisplay := True; {flag for Update, will draw in cloud} + EraseRect(FlightRect); { Clear the Screen....} + HideCursor; {game mode,no normal mouse functions} + FlushEvents(mDownMask,0); {clear mousedowns} + Mask := 4; { mask shapes to flightRect } + WagonMoving := True; + end; {of begin} + If whichControl = ResumeButton then {RESUME} + Begin + InsertMenu(myMenus[4],0); {display exit message} + For i := 1 to 3 do DisableItem(myMenus[i],0); + DrawMenuBar; + + HideControl(ResumeButton); + HideControl(EndButton); + + {now hilite the proper man in the scorebox, was unhilited + when the user paused the game.} + If MenLeft > 0 then InvertRect(ScoreMan[6-MenLeft]); + + GameUnderWay := True; {we're back into game mode} + HideCursor; + Mask := 4;{entire flight area} + CopyRgn(tempRgn,CopterRgn); {restore prior region saved in PauseThisGame} + FlushEvents(mDownMask,0); {clear all mouseDowns} + End; + If whichControl = EndButton then {END current game...} + Begin + If LevelOnDisplay then begin {Hide levelbutton if it's drawn} + LevelOnDisplay := False; + HideControl(LevelButton); + UnionRgn(CopterRgn,LevelUnion,CopterRgn);{restore button area to CopterRgn} + end; + HideControl(ResumeButton);{hide the resume and end} + HideControl(EndButton); + InvalRect(FlightRect); {make 'Update' redraw the begin screen} + For i := 1 to 2 do FillRect(FlipFrame[i],dkGray);{flip showing?} + WhichSound := 0; + ShowControl(BeginButton); + Mask := 1;{mask out begin button} + CloudOnDisplay := False; + CopyRgn(FlightRgn[Mask],CopterRgn);{must use CopyRgn instead of ':='} + End; + End; {of If ControlHit} +End; { of procedure} + +procedure PauseThisGame; {called if a backspace or doubleclick during game} +var + i: integer; +Begin + GameUnderWay := False; { halt animation } + FillRect(YokeErase,Gray); { Cover the yoke } + If MenLeft > 0 then InvertRect(ScoreMan[6-MenLeft]);{unhilite man in scorebox} + ShowCursor; + ShowControl(ResumeButton); + ShowControl(EndButton); + DeleteMenu(MessageMenu); {remove exit message,i-354} + For i := 1 to 3 do EnableItem(myMenus[i],0);{show other menu options} + DrawMenuBar; + Mask := 3; { flags a pause is underway....mask out buttons} + CopyRgn(CopterRgn,tempRgn); {keep old region in case we resume this game} + DiffRgn(FlightRgn[Mask],CloudRgn[CloudNdx],CopterRgn);{Mask Cloud from region} + err := PBKillIO(SoundParmBlk,false); {kill any current sound} +End; + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + WhichControl: ControlHandle; + MouseLoc: Point; + WindowLoc: integer; + ControlHit,i: integer; + tLong: LongInt; +Begin + If GameUnderWay then begin {game is underway..Mousedown can only drop man} + + If ManStatus = 4 then begin{man is hanging so begin the drop} + ManNdx := 2; { draw first man dropping at current manRect} + CopyBits(OffScreen,myWindow^.portBits,OffMan[ManNdx], + ManRect,srcCopy,CopterRgn); + ManStatus := 0; {this flags that a man is now dropping} + HeightOfDrop := WagonRect.bottom - CoptRect.bottom; {for score} + end; { of if ManStatus} + end + + Else begin { then Mouse is normal...handle normal functions } + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {i-441} + inDrag: + DragWindow(WhichWindow,MouseLoc,DragArea); + inContent:{by not selecting the window, DA's can be open during game} + Begin + GlobaltoLocal(MouseLoc); + ControlHit := FindControl(MouseLoc,whichWindow,whichControl); + If ControlHit > 0 then TakeCareControls(whichControl,Mouseloc); + end; + end; {case of} + end; { of Else} +end; { TakeCareMouseDown } + +PROCEDURE TakeCareKeyDown(Event:EventRecord); +Var + KeyCode,i: integer; + CharCode: char; +Begin + { KeyCode := LoWord(BitAnd(Event.message,keyCodeMask)) div 256; not used } + CharCode := chr(LoWord(BitAnd(Event.message,CharCodeMask))); + + If BitAnd(Event.modifiers,CmdKey) = CmdKey then begin + {key board command - probably a menu command} + DoMenuCommand(MenuKey(CharCode)); + end + Else If (CharCode = chr(BackSpace)) and GameUnderWay then PauseThisGame; +End; + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); + {other windows can't be selected or worked while in game mode} +End; + +procedure OneTimeGameStuff; {set up the gamestuff only needed on startup} +var + Dest: Point; + i,width,dh,dv:integer; + tRect:Rect; +Begin + CloudOnDisplay := False;{no clouds are to be drawn by update} + { center ScoreBoxRect in Window bottom } + with ScoreBoxRect do begin + OffSetRect(ScoreBoxRect,-left,myWindow^.portRect.bottom - bottom); + i := (myWindow^.portRect.right - right) div 2; + end; {with} + OffSetRect(ScoreBoxRect,i,-2); + OffsetRect(WagonRect,-WagonRect.left, + ScoreBoxRect.top-4-WagonRect.bottom);{wagon to baseline} + OffSetRect(CoptRect,0,WagonRect.top-10-CoptRect.bottom);{lowest copter} + CopterBottomLimit := CoptRect.bottom; {lower limit for copterflight} + SetRect(BorderRect,-76,-4,509,CoptRect.top-1); + + {we'll let Update draw the scorebox over a gray background} + + {now define the flight area and various masking regions} + FlightRect := myWindow^.portRect; + FlightRect.bottom := ScoreBoxRect.top-4; {define flight area} + RectRgn(FlightRgn[4],FlightRect); + DiffRgn(FlightRgn[4],BeginRgn,FlightRgn[1]); { Flight less begin button } + DiffRgn(FlightRgn[4],EndRgn,FlightRgn[2]); { Flight less End button} + DiffRgn(FlightRgn[1],EndRgn,FlightRgn[3]); {Flight less both buttons} + DisposeRgn(BeginRgn); + + {now locate two fliprect's on either side of scoreBox } + with myWindow^.portRect do begin + width := ScoreBoxRect.left - left;{width of area} + dh := (width - (FlipRect[1].right - FlipRect[1].left)) div 2; + width := bottom - ScoreBoxRect.top + 4;{height of area} + dv := (width - (FlipRect[1].bottom - FlipRect[1].top)) div 2; + {Left flip location, destination-source} + OffSetRect(FlipRect[1],left + dh - FlipRect[1].left,bottom - + dv - FlipRect[1].bottom); + {Right flip location, destination-source} + OffSetRect(FlipRect[2],ScoreBoxRect.right + dh - FlipRect[2].left, + bottom - dv - FlipRect[2].bottom); + end; { of with} + + For i := 1 to 2 do begin { Frames for flips....} + tRect := FlipRect[i]; + InsetRect(tRect,-4,-4); {give the guy some room to flip} + tRect.bottom := tRect.bottom - 3;{but keep his feet on the ground} + FlipFrame[i] := tRect; + end; { of for i} + + {establish Crosshair control limits} + YokeLimits := ScoreBoxRect; + YokeLimits.right := YokeLimits.left + 51; + YokeErase := YokeLimits; + InsetRect(YokeLimits,7,7);{limits of movement of center of cross} + InsetRect(YokeErase,4,4); {used to create MaskRgn,mask copyBits for cross} + + {using MaskRgn forces CopyBits to draw only the visible part of the cross + into the ScoreBox, making it appear to slide inside the box} + MaskRgn := NewRgn; + OpenRgn; + FrameRect(YokeErase); + CloseRgn(MaskRgn); {create maskrgn for copybits crosshair} + + {now locate Cross centerline relative to Yoke window.topleft, each loop the + destination rectangle for the drawing the Cross is offset from this + position... the offset is determined by mapping the mouse position into + the MouseRect in the AnimateOneLoop procedure} + with CrossRect do begin + Dest.h := YokeLimits.left - ((right - left) div 2); + Dest.v := YokeLimits.top - ((bottom - top) div 2); + OffSetRect(CrossRect,Dest.h - left,Dest.v - top); + end; {with CrossRect} + + SetRect(MouseRect,210,134,302,206); { this is for mapping control } + SetRect(DeltaRect,-4,-3,4,4); { this is for finding copter offset } + + {find all the Constants used to locate Cross in ScoreBox, we'll be + replacing MapPt and OffSetRect calls} + YokeHt := YokeLimits.bottom-YokeLimits.top; + YokeWdth := YokeLimits.right-YokeLimits.left; + MouseHt := MouseRect.bottom-MouseRect.top; + MouseWdth := MouseRect.right-MouseRect.left; + OffsetHt := YokeLimits.top-CrossRect.top; + OffsetWdth := YokeLimits.left-CrossRect.left; + CrossHt := CrossRect.bottom - CrossRect.top; + CrossWdth := CrossRect.right - CrossRect.left; + DeltaHt := DeltaRect.bottom - DeltaRect.top; + DeltaWdth := DeltaRect.right - DeltaRect.left; + + { onscreen rectangles based in ScoreBox } + tRect:= OffMan[1]; {size of man..locate ScoreMan in ScoreBox} + OffSetRect(tRect,ScoreBoxRect.left + 54 - tRect.left, + ScoreBoxRect.top - tRect.top); + for i := 1 to 5 do begin + ScoreMan[i] := tRect; { boxes to track which man is in action} + OffSetRect(tRect,0,17); { move one row down } + ThumbUp[i] := tRect; { boxes for thumbs up } + OffSetRect(tRect,0,17); + ThumbDown[i] := tRect; { Boxes for thumbs down } + OffSetRect(tRect,15,-34); { back to top and over one} + ThumbState[i] := 0; {No Thumbs are drawn yet} + end; { of for } + + tRect:= OffNum[1]; {size of Numbers..locate in ScoreBox} + OffSetRect(tRect,ScoreBoxRect.left + 135 - tRect.left, + ScoreBoxRect.top + 10 - tRect.top); + for i := 1 to 6 do begin + ScoreNum[i] := tRect; { boxes to record score digits} + OffSetRect(tRect,0,25); { move one row down } + HiScoreNum[i] := tRect; { boxes for hiscore digits } + OffSetRect(tRect,21,-25); + end; { of for } + + {find point for writing current height into the scorebox} + {HtStatRect is destination rect in scorebox, Offheight is source rect + in OffScreen, HeightPt is moveto location in offScreen} + HeightStr := '444'; {max width for 3 numerals?,for centering in box} + HtStatRect.top := ScoreBoxRect.top + 2; + HtStatRect.left := ScoreBoxRect.right - 48; + HtStatRect.bottom := ScoreBoxRect.top + 14; + HtStatRect.right := HtStatRect.left + StringWidth(HeightStr); + OffHeight := HtStatRect; {offScreen rect to contain drawstring} + OffSetRect(OffHeight,OffHorse.right-OffHeight.left, + OffHorse.bottom-OffHeight.bottom); {right of dead horse} + HeightPt.h := OffHeight.left; + HeightPt.v := OffHeight.bottom; {'moveto' location for height in offscreen} + + OldBits := myWindow^.portBits;{always preserve the old map!!} + SetPortBits(OffScreen); + FillRect(OffHeight,white); {erase to white} + SetPortBits(OldBits); {restore old bitmap} + + tRect := HtStatRect; + InSetRect(tRect,-14,-1); + OffSetRect(tRect,0,17); {size and locate Rects for Wagon/gravity} + WagStatRect := tRect; + OffSetRect(tRect,0,17); + GravStatRect := tRect; + + WagonStatus[1] := 'WALK'; + WagonStatus[2] := 'TROT'; + WagonStatus[3] := 'GALLOP'; + GravityStatus[4] := 'HEAVY'; {strings for Wagon speed and gravity} + GravityStatus[3] := 'NORMAL'; + GravityStatus[2] := 'OH BOY'; + GravityStatus[1] := 'FLYING'; +End; { of OneTimeGameStuff } + +procedure AnimateWagonCopter(ClipTo:RgnHandle;DrawWagon:Boolean); + {animate copter/wagon while game is not underway} +Begin + If CoptNdx < 3 then inc(CoptNdx) else CoptNdx := 1; + OffsetRect(CoptRect,212-CoptRect.left,110-CoptRect.top); + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,ClipTo); + If DrawWagon then begin + If WagonNdx < 3 then inc(WagonNdx) else WagonNdx := 1; + If (WagonRect.left > 510 ) then OffSetRect(WagonRect,-WagonRect.right,0) + else OffsetRect(WagonRect,WagonSpeed,0); + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,nil); + end; {if DrawWagon} + {draw current height into scorebox} + Height := WagonRect.bottom - CoptRect.bottom; + NumToString(Height,HeightStr); + OldBits := myWindow^.portBits; + SetPortBits(OffScreen); {we want to draw into offScreen} + EraseRect(OffHeight); {erase to white} + MoveTo(HeightPt.h,HeightPt.v);{move the pen to bottom left of OffHeight} + DrawString(HeightStr); { draw current height into offscreen} + SetPortBits(OldBits); {restore old bitmap} + CopyBits(OffScreen,myWindow^.portBits,OffHeight, + HtStatRect,srcCopy,nil); {now stamp it onto the screen} +End; + +procedure ResetManHanging; {test for end of game, reset if more men available} +var + i: integer; +Begin { the following executed success or fail } + ManStatus := 4;{man is hanging} + InvertRect(ScoreMan[6-MenLeft]); { make last scorebox man normal} + dec(MenLeft); + If MenLeft > 0 then InvertRect(ScoreMan[6-MenLeft]); {next man} + OffSetRect(ManRect,CoptRect.left+36-ManRect.left, + CoptRect.top+23-ManRect.top); {move ManRect back up to Copter} + If MenLeft = 0 then { *** end of this level...} + begin + If GoodJumps < 5 then begin { **** end of this game } + GameUnderWay := False; + If Score > HiScore then begin {New HiScore?} + HiScore := Score; + DrawScoreIntoBox(HiScore,HiScoreNum); + end; + ShowCursor; + err := PBKillIO(SoundParmBlk,false); {kill sound} + whichSound := 0; + EraseRect(CoptRect); {erase last copter} + EraseRect(Cloud[CloudNdx]); {erase last cloud} + CloudOnDisplay := False; {no cloud for Update} + FillRect(YokeErase,Gray); { fill in yoke } + DeleteMenu(MessageMenu); { remove exit message,i-354} + For i := 1 to 3 do EnableItem(myMenus[i],0); + DrawMenuBar; + ShowControl(BeginButton); + Mask := 1; + DrawAllmyStrings; + end { of If GoodJumps } + Else begin { move on to next level stuff} + Mask := 2; { mask out level button } + inc(CurrentLevel); { next level } + LevelToButtonTitle(CurrentLevel);{level to buttontitle} + ShowControl(LevelButton); + LevelOnDisplay := True; + DiffRgn(CopterRgn,EndRgn,CopterRgn);{mask levelbutton from CopterRgn} + LevelTimer := TickCount + 120; {time levelbutton onscreen 2 secs.} + If (WagonSpeed = 3) and (Gravity > 1) then + Gravity := Gravity - 1; {MaxGravity = 4} + If WagonSpeed < 3 then WagonSpeed := WagonSpeed + 1; + DrawWagonStatus; {update scorebox for wagon/gravity} + MenLeft := 5; { # of men/level } + InvertRect(ScoreMan[1]); + GoodJumps := 0; { # of successfull jumps } + For i := 1 to 5 do begin {erase the last set of thumbs...} + EraseRect(ThumbUp[i]); + EraseRect(ThumbDown[i]); + ThumbState[i] := 0; + end; { of for i} + If CurrentLevel < 6 then begin { higher pitch flipsound } + For i := 1 to 4 do begin + with FlipSound[i]^ do begin + sound1Rate := 2 * sound1Rate;{raise pitch one octave} + sound2Rate := 2 * sound2Rate; + sound3Rate := 2 * sound3Rate; + sound4Rate := 2 * sound4Rate; + end; { of with} + end; { of for i} + end; { if CurrentLevel} + end; { of Else} + end; { of If MenLeft} + FlushEvents(mDownMask,0); {so an old mousedown will not drop new man!} +End; + +procedure AnimateOneLoop; +var + i,j:integer; + Where,which: integer; + MouseLoc,tpoint: Point; + tRect: Rect; +Begin + GetMouse(MouseLoc); { MouseLoc in Coords of currentGrafPort } + + {if out of bounds then limit MouseLoc to MouseRect extremes } + If MouseLoc.h > MouseRect.right then MouseLoc.h := MouseRect.right + else If MouseLoc.h < MouseRect.left then MouseLoc.h := MouseRect.left; + + If MouseLoc.v > MouseRect.bottom then MouseLoc.v := MouseRect.bottom + else If MouseLoc.v < MouseRect.top then MouseLoc.v := MouseRect.top; + + Case CoptNdx of + {split time between clouds,height and cross...draw each every 3rd loop} + + 1:begin {height into scorebox, use offscreen to avoid flicker} + NumToString(WagonRect.bottom - CoptRect.bottom,HeightStr); + OldBits := myWindow^.portBits; + SetPortBits(OffScreen); {we want to draw into offScreen} + EraseRect(OffHeight); {erase to white} + MoveTo(HeightPt.h,HeightPt.v);{move the pen to bottom left of OffHeight} + DrawString(HeightStr); { draw current height into offscreen} + SetPortBits(OldBits); {restore old bitmap} + CopyBits(OffScreen,myWindow^.portBits,OffHeight, + HtStatRect,srcCopy,nil); {now stamp it onto the screen} + inc(CoptNdx); + end;{of Case 1:} + + 2:begin {cloudstuff} + If (Cloud[CloudNdx].right < 0) then StartNewCloud;{is it offscreen left?} + dec(Cloud[CloudNdx].left); + dec(Cloud[CloudNdx].right); {move cloudrect left,faster than OffsetRect} + OffSetRgn(CloudRgn[CloudNdx],-1,0);{move the masking region too} + + {draw the cloud masked by the cloudrgn} + CopyBits(OffScreen,myWindow^.portBits,OffCloud[CloudNdx], + Cloud[CloudNdx],srcCopy,CloudRgn[CloudNdx]); + + {move the CopterRgn to mask the new cloud position} + OffsetRgn(CopterRgn,-1,0); + inc(CoptNdx); {next Copter shape} + + {level stuff is here because we are masking the button onto CopterRgn} + If LevelOnDisplay then begin { is level button being shown?} + If TickCount>LevelTimer then begin {put away levelbutton if time is up} + Mask := 4; { normal window} + UnionRgn(CopterRgn,LevelUnion,CopterRgn); {put back last level mask} + HideControl(LevelButton); + LevelOnDisplay := False; + end {if Tickcount} + else begin {make sure level is masked properly as copterRgn scrolls} + UnionRgn(CopterRgn,LevelUnion,CopterRgn); {put back last mask} + DiffRgn(CopterRgn,EndRgn,CopterRgn); {mask out present} + end; { of else} + End; { if LevelOnDisplay} + end; {of case 2:} + + 3:begin + {Draw CrossHair into ScoreBox,use tpoint to preserve MouseLoc} + {lets do our own MapPt and Offset calculations here instead of ROM calls} + tRect.left := CrossRect.left + YokeWdth * (MouseLoc.h - 210) div 92; + tRect.right := tRect.left + CrossWdth; + tRect.top := CrossRect.top + YokeHt * (MouseLoc.v- 134) div 72; + tRect.bottom := tRect.top + CrossHt; + CopyBits(OffScreen,myWindow^.PortBits,OffCross, + tRect,srcCopy,MaskRgn); + CoptNdx := 1; + end; {of case 3:} + end; {of case CoptNdx} + + + {find distance to move the copter for this loop} + {MapPt will convert our MouseLoc into a point in the DeltaRect which + represents what the user is requesting for the copter move in pixels per + loop, since objects cannot instantly accelerate, we will accelerate one + pixel of offset per loop. Therefore if the copter is currently flying + backwords at 4 pixels per loop it will take 8 loops to reach a forward + speed of 4 pixels per loop assuming that the user has the mouse on full + forward position. Full forward position being a Point on the rightmost + of MouseRect which will return a horizontal value of 4 when mapped into + the DeltaRect.} + MapPt(MouseLoc,MouseRect,DeltaRect); + + If MouseLoc.h>Dh then inc(Dh) {MouseLoc.h won't be over 4 or under -4} + else if MouseLoc.h < Dh then dec(Dh); + If MouseLoc.v > Dv then inc(Dv) + else if MouseLoc.v < Dv then dec(Dv);{Dh,Dv are offset to move copter} + + CoptRect.left := CoptRect.left + Dh; {faster than an OffsetRect} + CoptRect.right := CoptRect.right + Dh; + CoptRect.top := CoptRect.top + Dv; + CoptRect.bottom := CoptRect.bottom + Dv; + + {now check location,BorderRect sets limits for Copter at edges of window} + If not(PtInRect(CoptRect.topleft,BorderRect)) then begin{outside,find which} + If CoptRect.left > 510 then OffSetRect(CoptRect,-CoptRect.right,0);{wraparound} + If CoptRect.left < -76 then OffSetRect(CoptRect,510-CoptRect.left,0);{wrap} + If CoptRect.top < -4 then OffSetRect(CoptRect,0,-4-CoptRect.top); + If CoptRect.bottom > CopterBottomLimit then + OffSetRect(CoptRect,0,CopterBottomLimit-CoptRect.Bottom); + end;{of if not PtInRect} + + If WagonNdx < 3 then inc(WagonNdx) else WagonNdx := 1;{which wagon shape} + If (WagonRect.left > 512) then OffSetRect(WagonRect,-582,0) + else begin + WagonRect.left := WagonRect.left + WagonSpeed; {locate next wagon} + WagonRect.right := WagonRect.right + WagonSpeed; + end;{of else} + + Case ManStatus of + 0:begin { man is dropping......} + If ManRect.bottom > WagonRect.top then {Check for a hit!} + begin + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn); {draw copter} + EraseRect(ManRect); { erase man and redraw wagon} + CopyBits(OffScreen,myWindow^.portBits, + OffWagon[WagonNdx],WagonRect,srcCopy,nil); + + Where := ManRect.left - WagonRect.left;{where relative to wagon} + If (Where < -6) or (Where > 70) then Which := 0 {no hit} + else if Where < 34 then Which := 1 {success } + else If where < 45 then Which := 2 {hit driver} + Else Which := 3; {hit horse!} + Case Which of + 1: begin {landed in hay so initialize Success} + CopyBits(OffScreen,myWindow^.portBits,OffMan[7], + ThumbUp[6-MenLeft],srcCopy,Nil); {draw Thumbs UP!} + ThumbState[6-MenLeft] := 1; + Score := Score + CurrentLevel * HeightOfDrop; + DrawScoreIntoBox(Score,ScoreNum); + If Score > HiScore then begin {New HiScore?} + HiScore := Score; + DrawScoreIntoBox(HiScore,HiScoreNum); + end; + for i := 1 to 2 do begin + EraseRect(FlipFrame[i]);{erase area for the flips} + FrameRect(FlipFrame[i]); {frame for Flips} + end; + inc(GoodJumps); + ManStatus := 1; {flag its a success!} + FlipCount := 3; {index for the flips} + If SoundOn then begin {start the first of four flipsounds} + err := PBKillIO(SoundParmBlk,false);{kill sound} + WhichSound := 1; + {store flipsound stuff into SoundParmBlk} + SoundParmBlk^.iobuffer := ptr(FlipSynth); + SoundParmBlk^.ioreqcount := SizeOf(FlipSynth^); + FlipSynth^.sndRec := FlipSound[whichSound]; + {reset duration always..as it is altered by Driver} + For j:=1 to 4 do FlipSound[j]^.duration:= FlipTime[j]; + err := PBWrite(SoundParmBlk,true); {do the Flip sound} + end;{If soundOn} + end; {of case 1:} + 0,2,3: begin { missed the hay.... initialize Splat} + CopyBits(OffScreen,myWindow^.portBits,OffMan[14], + ThumbDown[6-MenLeft],srcCopy,Nil); {Thumbs down} + ThumbState[6-MenLeft] := 2;{remember thumbstate for update} + FlipCount := 16; {index for drawing flip/splat shapes} + If SoundOn then Begin + err := PBKillIO(SoundParmBlk,false); + WhichSound := 0; {restart copter sound after splat} + SoundParmBlk^.iobuffer := ptr(SplatSound); + SoundParmBlk^.ioreqcount := SplatBuff; + err := PBWrite(SoundParmBlk,true); {do the splatsound} + end;{if SoundOn} + OffSetRect(ManRect,0,WagonRect.bottom-13-ManRect.bottom); + case which of {determine which kind of failure} + 0: begin {missed the whole thing!} + ManStatus := 2; {its a failure} + OffSetRect(ManRect,0,WagonRect.bottom-ManRect.bottom); + end; + 2: ManStatus := 8; {hit the driver} + 3: ManStatus := 9; {hit the horse} + end; { of case which} + end; { case 0,2,3: } + end; { of Case which } + end {Of If manRect} + Else {man is dropping but not down to wagon yet..keep going} + begin + If ManNdx < 6 then inc(ManNdx) else ManNdx := 2;{next man} + {how about some effects on man dropping thru cloud!!} + If PtInRgn(ManRect.topleft,CloudRgn[CloudNdx]) then + OffSetRect(ManRect,Random div 10924,1) {drop is 1 pixel} + Else begin {man not behind cloud so normal drop} + ManRect.top:= ManRect.top + Gravity; {locate next man} + ManRect.bottom:= ManRect.bottom + Gravity; + end;{of else} + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn); + CopyBits(OffScreen,myWindow^.portBits,OffMan[ManNdx], + ManRect,srcCopy,CopterRgn); + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,nil); + end; { of last else(mandropping)} + end; { of case 0: mandropping} + + 1: {Success is underway... flips and man in wagon} + Begin + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn);{draw the copter} + If FlipCount < 46 then {for 1 to 15 shapes every third loop} + begin + If (FlipCount mod 3) = 0 then begin {Draw every 3rd time thru} + i := FlipCount div 3; + CopyBits(OffScreen,myWindow^.portBits,OffFlip[i], + FlipRect[1],srcCopy,Nil); {Draw left flip} + CopyBits(OffScreen,myWindow^.portBits, + OffFlip[i],FlipRect[2],srcCopy,Nil); + end;{ if not odd(Flipcount)} + OffsetRect(ManInWagon,WagonRect.left-ManInWagon.left, + WagonRect.top-ManInWagon.top); + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,Nil); {draw wagon} + CopyBits(OffScreen,myWindow^.portBits,OffManInWagon, + ManInWagon,srcCopy,Nil); {draw Man in Wagon} + inc(FlipCount); + end + Else {the flip is complete...get ready for the next man} + Begin {Erase last flip} + For i := 1 to 2 do FillRect(FlipFrame[i],dkGray); + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,Nil); {draw wagon} + ResetManHanging;{ set up another man/level or end} + end; + end; { of case 1: success} + + 2,8,9: {Fail is underway... drawing splat shapes} + begin + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn); + If FlipCount < 27 then {for 8 to 13 shapes} + begin + CopyBits(OffScreen,myWindow^.portBits, {draw wagon} + OffWagon[WagonNdx],WagonRect,srcCopy,Nil); + If not odd(Flipcount) then begin {Draw every other time thru} + CopyBits(OffScreen,myWindow^.portBits, + OffMan[FlipCount div 2],ManRect,srcCopy,Nil); + end; {if not odd} + inc(FlipCount); + end + Else {Splat animation is complete...finish up} + Begin + EraseRect(ManRect); + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,Nil); {draw wagon} + Case ManStatus of + 8: begin {hit the driver} + OffSetRect(DriverRect,WagonRect.right-DriverRect.right, + WagonRect.top-DriverRect.top); + CopyBits(OffScreen,myWindow^.portBits,OffDriver, + DriverRect,srcCopy,Nil);{draw dead driver} + InvertRect(ScoreMan[6-MenLeft]); + MenLeft := 1; {this will end the game} + InvertRect(ScoreMan[6-MenLeft]); + WagonMoving := False;{leave the last wagon as is} + end; {of case 8:} + 9: begin {hit the horse} + OffSetRect(horseRect,WagonRect.right-horseRect.right, + WagonRect.top-horseRect.top); + CopyBits(OffScreen,myWindow^.portBits,Offhorse, + horseRect,srcCopy,Nil);{draw dead horse} + InvertRect(ScoreMan[6-MenLeft]); + MenLeft := 1; {end the game} + InvertRect(ScoreMan[6-MenLeft]); + WagonMoving := False; + end; {of case 8:} + end; {of case Manstatus} + + ResetManHanging; + end; + end; {of case Fail} + + 4: {ManHanging on to copter} + begin { Draw Man and Wagon and continue...waiting for mousedown} + ManRect.left := CoptRect.left + 36; + ManRect.right := ManRect.left + ManWdth; + ManRect.top := CoptRect.top + 23; + ManRect.bottom := ManRect.top + ManHt;{locate man relative to copter} + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,nil); + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn); + CopyBits(OffScreen,myWindow^.portBits,OffMan[1], + ManRect,srcCopy,CopterRgn); + end; { of case 4: manhanging} + + end; { of Case ManStatus } + If SpeedTrapOn then Delay(SpeedFactor,SpeedTick);{ii-384} +End; { of procedure } + +Procedure DrawUpDateStuff; +{will draw all our images in response to Update event,an Update event is +waiting for our newly opened window so will draw our first stuff too!} +var + tRect: Rect; + tpoint: Point; + i: integer; +Begin + { draw in the scorebox over gray background } + tRect := myWindow^.portRect;tRect.top := ScoreBoxRect.top - 3; + FillRect(tRect,dkGray); + CopyBits(OffScreen,myWindow^.portBits,OffScoreBox, + ScoreBoxRect,srcCopy,Nil); + Moveto(0,ScoreBoxRect.top-4); + Lineto(512,ScoreBoxRect.top-4); {wagon Baseline or 'ground'line} + + CopyBits(OffScreen,myWindow^.portBits,OffWagon[WagonNdx], + WagonRect,srcCopy,nil);{draw the wagon} + + { Draw Thumbs into ScoreBox } + For i := 1 to 5 do begin + Case ThumbState[i] of {ThumbState determines which thumb to draw if any} + {0: don't do anything there is no thumb drawn in this position} + 1: CopyBits(OffScreen,myWindow^.portBits,OffMan[7], + ThumbUp[i],srcCopy,Nil); {Thumbs UP! in upper box} + 2: CopyBits(OffScreen,myWindow^.portBits,OffMan[14], + ThumbDown[i],srcCopy,Nil); {Thumbs down in lower box} + end; { of case} + end; {of for i} + + DrawScoreIntoBox(Score,ScoreNum); { Draw Scores into ScoreBox } + DrawScoreIntoBox(HiScore,HiScoreNum); + + DrawWagonStatus; { Draw Wagon and Gravity info} + + {Invert proper man in scorebox, don't think this is ever used} + If (MenLeft > 0) and GameUnderWay then InvertRect(ScoreMan[6-MenLeft]); + + For i := 1 to 2 do FillRect(FlipFrame[i],dkGray); + FillRect(YokeErase,Gray); { cover the yoke until resume or newgame} + + If Mask = 1 then begin {this is used on start up or waiting for begin} + DrawAllmyStrings; + ShowControl(BeginButton); + end; + {draw cloud if one is showing} + If CloudOnDisplay then CopyBits(OffScreen,myWindow^.portBits, + OffCloud[CloudNdx],Cloud[CloudNdx],srcCopy,CloudRgn[CloudNdx]); +end; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; + itemHit: integer; + Test: Boolean; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect); + If UpDateWindow = myWindow then DrawUpDateStuff; + DrawControls(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure MainEventLoop; +var + myEvent: EventRecord; + i: integer; +Begin + InitCursor; + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + mouseUp: + begin + LastMouseUp := myEvent.when; {to test for DoubleClick} + WhereMouseUp := myEvent.where; + end; + KeyDown: TakeCareKeyDown(myEvent); + ActivateEvt:TakeCareActivates(myEvent); + UpdateEvt:TakeCareUpdates(myEvent); + End {of Case} + Else {no event pending so lets do some game stuff} + Begin + If GameUnderWay then begin + AnimateOneLoop; {draw all our shapes) + + {sound stuff... ioresult will be <1 if sound is finished} + If (SoundParmBlk^.ioresult < 1) then begin {only if sound is done} + If GameUnderWay and SoundOn then begin {animate loop might end game} + aTick := TickCount;{we'll wait a tick before PBWrite} + Case WhichSound of + 0: begin {reset copterSound} + SoundParmBlk^.iobuffer := ptr(CoptSound); + SoundParmBlk^.ioreqcount := CoptBuff; + end; + 1,2,3: begin {reset next flipsound} + inc(WhichSound); + FlipSynth^.sndRec := FlipSound[whichSound]; + end; + 4: begin {reset last flipsound} + WhichSound := 0; + FlipSynth^.sndRec := FlipSound[4]; + end; { of case 4:} + end; { of Case} + repeat until (TickCount > aTick); {wait a tick,just in case} + err := PBWrite(SoundParmBlk,true); {start the sound} + end; { if GameUnderWay} + end; {if SoundParmBlk} + end { if GameUnderWay} + else {game is not underway..waiting for a begin or resume} + Case Mask of + 1:Begin { animate during Wait for Beginbutton press } + AnimateWagonCopter(FlightRgn[Mask],WagonMoving); + If SpeedTrapOn then Delay(SpeedFactor,SpeedTick); + End; + 3: { animate stationary copter during wait for Resume/end } + Begin + If SpeedTrapOn then Delay(SpeedFactor,SpeedTick); + If CoptNdx < 3 then inc(CoptNdx) else CoptNdx := 1; + CopyBits(OffScreen,myWindow^.portBits,OffCopter[CoptNdx], + CoptRect,srcCopy,CopterRgn); + If ManStatus = 4 then begin + OffSetRect(ManRect,CoptRect.left+36-ManRect.left, + CoptRect.top+23-ManRect.top); + CopyBits(OffScreen,myWindow^.portBits,OffMan[1], + ManRect,srcCopy,CopterRgn); + end; { if ManStatus} + end; { of 3:} + end; { case mask} + End; {else no event pending} + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + myMenus[3] := GetMenu(optionMenu); + myMenus[4] := GetMenu(MessageMenu); {this is the backspace message} + CheckItem(myMenus[3],1,True); {check the Sound item} + SoundOn := True; {sound will start on first begin} + For i := 1 to 3 do + begin + InsertMenu(myMenus[i],0); + end; + DrawMenuBar; +End; + +procedure CloseStuff; +var i:integer; +Begin + HideWindow(HelpDialog); + HideWindow(AboutDialog); + err := PBKillIO(SoundParmBlk,false);{always kill sound i/o before quitting!} +End; + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; + InitWindows; + InitMenus; + TEInit;{for the Dialog stuff} + InitDialogs(Nil); + + Finished := False; {set terminator to false} + FlushEvents(everyEvent,0); {clear events} + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } + with Screen do SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4); +End; + +{Main Program begins here} +BEGIN + InitThings; + SetUpMenus; + CreateRegions; + CreateWindow; {load window,dialogs,controls} + CreateSound; {set up sound buffers, Apple tech note 19} + CreateOffScreenBitMap; {see Apple tech note 41} + CreatePictures; {load pictures from resources} + DrawPicsIntoOffScreen; + CreateStrings; {load strings from resources} + CreateOffScreenRects; {set all rectangles for 'copybits' shape drawing} + DrawCloudsIntoOffScreen;{depends on OffScreenRects being defined} + OneTimeGameStuff; {Game varibles, scorebox stuff,etc} + HiScore := 0; + InitialCopterStuff; {called at start of each game,begin button} + GameUnderWay := False; + Mask := 1; { FlightRgn[1] } + {first Update event will draw everything in our game window } + MainEventLoop; + CloseStuff; +END. diff --git a/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.shapes b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.shapes new file mode 100755 index 0000000..56fd984 Binary files /dev/null and b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter.shapes differ diff --git a/Duane Blehm's Code/StuntCopter Æ’/StuntCopter1.5 b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter1.5 new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/StuntCopter Æ’/StuntCopter:Rsrc b/Duane Blehm's Code/StuntCopter Æ’/StuntCopter:Rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Zero GravityÆ’/About Zero Animation b/Duane Blehm's Code/Zero GravityÆ’/About Zero Animation new file mode 100755 index 0000000..e193976 Binary files /dev/null and b/Duane Blehm's Code/Zero GravityÆ’/About Zero Animation differ diff --git a/Duane Blehm's Code/Zero GravityÆ’/About Zero files.. b/Duane Blehm's Code/Zero GravityÆ’/About Zero files.. new file mode 100755 index 0000000..5687c16 --- /dev/null +++ b/Duane Blehm's Code/Zero GravityÆ’/About Zero files.. @@ -0,0 +1 @@ +About the ZeroGravity files: 'Game Animation' is a 'FullPaint' document illustrating the way animation is done in the ZeroGravity Chamber. 'About Zero Animation' is a MacWrite document that describes ZeroGravity animation. 'ZeroGravity.Pas' is an 'Edit' text file containing pascal source code to be compiled by the Pascal compiler. 'ZeroGravity.R' is an 'Edit' text file containing RMaker source code to be compiled by RMaker. 'ZeroGravity/Rsrc' is a resource document containing resources built with ResEdit, the resource editor. These resources are 'included' when ZeroGravity.R is compiled by RMaker. 'ZeroGravity' is our application... the result of Link. \ No newline at end of file diff --git a/Duane Blehm's Code/Zero GravityÆ’/Game Animation b/Duane Blehm's Code/Zero GravityÆ’/Game Animation new file mode 100755 index 0000000..700f448 Binary files /dev/null and b/Duane Blehm's Code/Zero GravityÆ’/Game Animation differ diff --git a/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity new file mode 100755 index 0000000..e69de29 diff --git a/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.R b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.R new file mode 100755 index 0000000..7db72d4 --- /dev/null +++ b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.R @@ -0,0 +1,35 @@ +* ZeroGravity.R + +ZeroGravity.rsrc + +TYPE ZGRV = STR + ,0 +ZeroGravity ver1.0, by Duane Blehm, 2/12/87 + +TYPE MENU + ,1 +\14 + About Zero... + (- + + ,256 +File + Quit /Q + + ,257 +Options + Help... + LoScore... + ! Sound + + ,258 +Works + Peek OffScreen + Source Code... + + ,259 +(Any Key to Exit) + Hi! + +* the rest of the resources where built with ResEdit +INCLUDE ZeroGravity/Rsrc diff --git a/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.pas b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.pas new file mode 100755 index 0000000..7bf664d --- /dev/null +++ b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity.pas @@ -0,0 +1,1906 @@ +Program ZeroGravity;{Copyright © 1986 by Duane Blehm, All Rights Reserved} + +{ Version 1.0 code enhancement,shrink pict resources...cover,mask,cntlbox} +{Format note: 'tabs' for this text should be set to '3'} + +USES MacIntf; {used for TML version 2.0,instead of the 'includes' directive} + +{$T APPL ZGRV set the TYPE and CREATOR} +{$B+ set the application's bundle bit } +{$L ZeroGravity.rsrc} { Link resource file } + +CONST + WindResId = 128; {Window Resource} + + lastMenu = 5; {Number of Menus} + appleMenu = 1; + fileMenu = 256; {Menu Resource Id's} + optionMenu = 257; + worksMenu = 258; + messageMenu = 259; + + {Control Resources} + pressResume = 130; + pressEnd = 131; + + Disable = 255; {disable button controls,i-322} + Enable = 0; + maxint = 32767; + Pi = 3.141593; + Decimal = '.'; + +TYPE + PilotActs = (InChamber,OpenDoors,FreeFlight,EnterLock,Resting); + SoundList = (AirDoor,BigDoor,Game,GravTone,Buzz,Blue,Silence); + + PictureList = (Pilot,BigPilot,Pod,Door,Star,Flip,BigFlip,Numeral, + BigChamber,TimerBox,TargetBox,AirLock,ScoreBox,Mask,Title, + CntlBox,Cover); + {2/14/87 note: Cntlbox... will use QuickDraw Commands + also will shrink Mask in resource and expand it later..all to save + on size of resources and application size!} + + DialogList = (Help,About,Name,LScore,SCode,BMap); + + {We used MacroMind's 'Music to Video' utility to convert Blue Danube notes + into a resource... then did some compacting,etc. to come up with our 'SONG' + resource which contains notes for use with the FourTone synthesizer} + + SongRec = Record {we'll import a song record from our 'SONG' resource file} + noteCount:integer;{how many notes in this song} + pitch:array[1..160,1..4] of Longint;{160 notes,4 voices} + duration:array[1..160] of integer;{how many ticks will pitch play} + end; + + SongPtr = ^SongRec; + SongHandle = ^SongPtr; + + Force = Record {will contain facts about force acting on Pilot in Chamber} + magH,magV: integer; {horizontal,vertical magnitude of force} + duration: integer; {how many loops will force be in effect} + liteNdx:integer; {index to array of rects used to draw lites} + end;{of record} + + ScoreNText = Record {we're keeping our scores in a resource} + nameStr:Str255; + score: Real; + end; + + ScoreBlk = Array[1..3,1..3] of ScoreNText;{3 difficultys,3 names per diff.} + + ScorePtr = ^ScoreBlk; + + +VAR + tDialPeek: DialogPeek; {so we can access TextH field of dialogrec} + FontFacts: FontInfo; {so we can change TEHandle size stuff} + MouseLoc,tpoint: Point; + FigureLoScore: boolean; {if true-track loscores; if false-don't bother} + ScoreControl: array[2..3] of Handle;{radio buttons,LoScoreDialog} + ScoreHandle: Handle; {handle to our LoScore resource data} + NameHandle: Handle; {Handle to textitem in NameDialog box} + NameString: Str255; {will hold LoScore name from one time to next} + WindFrame: array[1..16] of Rect;{array frame rects around window} + PauseUnderWay: Boolean; {flag if pause} + LastPilot: Rect; {last location of Pilot} + OffPatch, {Pilot sized rect. over OffChamber shape} + OffPilotMask: Rect; {Pilot sized rect. over OffMask shape} + DoorOpening: Boolean; {true while airlock door is opening} + FlipInProgress: Boolean; {true if a flip is being animated} + OffHold: Rect; {Pilot sized rect. in OffScreen bitmap} + CntlCover: Rect; {used to draw gray over diff. Controls} + LoopCount: integer; {count each loop of game for avgscore} + WholePart, + FractPart: Longint; {used to draw real numbers} + RotateRight: Boolean; {will pilot flip rotate right or left?} + + tinyBonus,tinyPenalty, + tinyScore, + tinyLoScore: Rect;{four boxes for drawing text into box} + BonusRect,PenaltyRect, + ScoreRect, + LoScoreRect: Rect; {four boxes inside of scoreBox} + TimeLine: integer; {how many loops to increment per game} + TimeSlide: Rect; {rect inside TimerBox for drawing indictor} + Difficulty: integer; {1 issoeasy,2 issoso,3 issotuff} + Radio: Array[1..3] of ControlHandle;{difficulty buttons} + PicNdx: PictureList;{use as index to picture arrays} + R: Array[Pilot..Cover] of Rect;{all our pic/shape rectangles!} + Home: Point;{base for drawing shapes, R[BigChamber].topleft} + TargetNum: Rect;{destination for Numerals in TargetBox} + + ChamberRgn: RgnHandle;{mask drawing of chamber interior} + + TopCover,BotCover:Rect; {two halfs of the chamber cover that opens} + PilotStatus: PilotActs;{will determine which animation is going} + CoverLoops: integer; + + {all our offscreen rectangles} + OffPod: Array[1..2] of Rect; + OffDoor,OffFrame, + OffStar,InStar,OffStarWindow, + OffBigChamber, + OffTargetBox,OffMask, + OffTimerBox,OffScoreBox, + OffAirLock,OffTitle: Rect; + OffNumeral: Array[0..3] of Rect; + OffFlip: Array[1..7] of Rect; + OffBigFlip: Array[1..7] Of Rect; + OffDoorSlide: Rect; + OffPilot: array[1..3,1..3] of Rect; + OffBigPilot: array[1..3,1..3] of Rect; + + StarRgn,BigChamberRgn,{most regions are used to 'mask' during CopyBits} + LimitRgn, + DoorRgn,DoorSlideRgn, + CoverRgn,tRgn: RgnHandle; + StarBox: Rect; + + TargetRgn: Array[0..3] of RgnHandle;{Rgns for finding subscore} + Bonus, + Penalty: Longint;{running total of all subscores for game} + SubScore: Longint;{score for distance of pilot from scorecenter} + LoScore: ScorePtr;{access ScoreBlk in Resource file} + AvgScore: Real;{Score for game divided by no. of loops} + ScoreCenter: Point; + ScoreStr,tStr: Str255; + ScoreH,ScoreV: integer;{location of pen prior to drawing Score string} + + MouseRect, {limit mouse movement to this rect} + DeltaRect: Rect; {map rect for copter control;dh,dv} + BorderRect: Rect; + + PilotNdx: Point; {index for OffPilot array} + Dh,Dv: Integer; {Offset for Pilot rectangle/shape} + Grav: array[1..30] of Force; {will wind varibles for game} + GravNdx: integer;{index for Grav array} + Gh,Gv: Integer;{offset for gravity forces} + Sustain: integer;{how many loops will force work?} + Nh,Nv: integer;{net effect of Dh,Dv and Gh,Gv} + Lights: array[0..16] of Rect;{list of lights around chamber} + LightNdx: integer; + + myMenus: Array[1..lastMenu] of MenuHandle; + refNum,theMenu, + theItem: integer; + SoundOn: Boolean; {flag for sound off or on, a menu option} + Finished: Boolean; {terminate the program,quit} + GameUnderWay: Boolean; {are we playing or pausing?} + ClockCursor: CursHandle; {handle to watch cursor} + myWindow: WindowPtr; {our game window} + wRecord: WindowRecord;{allocate record on stack} + DialNdx: DialogList;{will step through our dialogs array} + myDialog: Array[Help..BMap] of DialogPtr; + myDialogRec: Array[Help..BMap] of DialogRecord; + Screen,DragArea: Rect; + mySize: Size; {longint for CompactMem call} + + OffScreen,OldBits, + InScreen: BitMap; {for drawing into offscreen} + SizeOfOff: Size; {Size offscreen bitmap} + OffRowBytes: Integer; + + pPict: Array[Pilot..CntlBox] of PicHandle;{array of pictures} + + ResumeButton, + EndButton: ControlHandle; { handles to controlbuttons } + + aTick: Longint; {TickCount varible, sound delay} + + {Sound varibles} + Buff: Array[AirDoor..Buzz] of Longint;{for Sound buffers} + myPtr: Ptr; + myHandle: Handle; + Sound: Array[AirDoor..Blue] of FFSynthPtr;{FreeForm synthesizer sound} + BlueSynth: FTSynthPtr; {Blue is a FourTone sound} + BlueSound: FTSndRecPtr; {four FourTone Sounds} + SoundParmBlk: ParmBlkPtr; {used for PBWrite instead of StartSound} + WhichSound: SoundList; {which sound is being played?} + err: OSerr; + SawToothWave: wavePtr; + SquareWave: wavePtr; + ToneTicks,ToneDelay:Longint; + mySong: SongHandle; + mySongPtr: SongPtr; + NoteCount: Longint; + +{**********************************************} +procedure MoveRect(VAR aRect:Rect;h,v:integer;WhichCorner:integer); +{move aRect so that 'whichCorner' is alligned with the point h,v} +Begin + Case WhichCorner of + 1:Begin{aRect.topleft to h,v} + aRect.right := aRect.right - aRect.left + h; + aRect.bottom := aRect.bottom - aRect.top + v; + aRect.left := h; + aRect.top := v; + end; + 2:Begin{aRect.topright to h,v} + aRect.left := h - aRect.right + aRect.left;{h - width} + aRect.bottom := aRect.bottom - aRect.top + v; + aRect.right := h; + aRect.top := v; + end; + 3:Begin{aRect.bottomright to h,v} + aRect.left := h - aRect.right + aRect.left;{h - width} + aRect.top := v - aRect.bottom + aRect.top;{h - width} + aRect.right := h; + aRect.bottom := v; + end; + 4:Begin{aRect.bottomleft to h,v} + aRect.right := aRect.right - aRect.left + h; + aRect.top := v - aRect.bottom + aRect.top;{h - width} + aRect.left := h; + aRect.bottom := v; + end; + end;{of case whichmatch} +End; + +procedure DrawLoScores;{into loScoredialog window} +var + i,j:integer; +begin + {draw LoScores into Dialog window} + For i := 1 to 3 do begin + For j := 1 to 3 do begin + MoveTo((i-1)*160 + 10,j*16 + 60); + DrawString(LoScore^[i,j].nameStr); + + WholePart := trunc(LoScore^[i,j].score);{whole part of real number} + NumToString(WholePart,ScoreStr); + FractPart := Round((LoScore^[i,j].score-WholePart)*1000); + NumToString(FractPart,tStr); + While length(tStr) < 3 do tStr := concat('0',tStr);{ if fractpart< 0.1} + ScoreStr := concat(ScoreStr,Decimal,tStr); + MoveTo(i*160-10-StringWidth(ScoreStr),j*16+60); + DrawString(ScoreStr); + end; {for j} + end;{for i} +end;{procedure} + +procedure DrawIntegerIntoBox(theInteger:integer;theRect:Rect); +Begin + NumToString(theInteger,ScoreStr);{rom call.. convert integer into string} + MoveTo(theRect.right-StringWidth(ScoreStr),theRect.bottom); + EraseRect(theRect); + DrawString(ScoreStr); +end; + +procedure DrawScoreIntoBox(theScore:Real;theRect:Rect); +Begin + WholePart := trunc(theScore); {Whole&Fract Parts are Global Longint} + NumToString(WholePart,ScoreStr); + FractPart := Round((theScore-WholePart)*1000); + NumToString(FractPart,tStr); + While length(tStr) < 3 do tStr := concat('0',tStr);{ if fractpart< 0.1} + ScoreStr := concat(ScoreStr,Decimal,tStr); + MoveTo(theRect.right-StringWidth(ScoreStr),theRect.bottom); + EraseRect(theRect); + DrawString(ScoreStr); +End;{of procedure} + +procedure ProcessNewLoScore;{display new LoScore stuff and get name} +var + itemHit,i,index: integer; +Begin + {put up the dialog box... get nameStr} + Case Difficulty of + 1:ParamText('IsSoEasy','','','');{we're using the ^1 character, in Resource} + 2:ParamText('IsSoSo','','',''); + 3:ParamText('IsSoTuff','','','');{display difficulty string in DialogBox} + end;{of Case} + + ShowWindow(myDialog[Name]);{show the hidden dialog window} + SelectWindow(myDialog[Name]); + SetIText(NameHandle,NameString);{put default in text box} + SelIText(myDialog[Name],4,0,maxint);{select the text} + + Repeat + ModalDialog(Nil,itemHit);{Modal does an Update event for the dialog box} + + Until(itemHit = 1);{ok button} + + GetIText(NameHandle,NameString);{must be happy with text..so use it} + {Test the length of the text... shorten if its too long} + i := length(NameString); + While (StringWidth(NameString)> 86) do begin {limit width of string} + Dec(i); + NameString := copy(NameString,1,i);{drop one char off the string} + end; + + HideWindow(myDialog[Name]);{close the dialogwindow} + SelectWindow(myWindow); + + {sort the New LoScore} + index := 3;{we know that score is less than 3 already} + For i := 2 Downto 1 do + If AvgScore < LoScore^[Difficulty,i].score then index := i; + + i := 3; + While (i > index) do begin {switch old for new scores} + LoScore^[Difficulty,i] := LoScore^[Difficulty,i - 1]; + dec(i); + end;{While} + LoScore^[Difficulty,index].score := AvgScore; + LoScore^[Difficulty,index].nameStr := NameString; + + DrawScoreIntoBox(LoScore^[Difficulty,1].score,tinyLoScore);{lowest to scorebox} + + ChangedResource(ScoreHandle);{flag so we will write out changes on Quit} +End;{of procedure} + +procedure CreateLoScoreStuff; +{load in our LoScores from resource file} +Begin + ScoreHandle := GetResource('LSCR',128);{we're keeping LoScores in Resource} + HLock(ScoreHandle);{lock it permanently.. for speed} + LoScore := ScorePtr(ScoreHandle^); +end; + +procedure WriteASoundBlock(aSoundPtr:Ptr;BuffSize:Longint;LastTick:Longint); +{make changes to our SoundParmBlk and then start the sound,the lasttick pause +is included due to occassional buzz/click/or worse if sound is written +immediately.. might be because Sound Driver set ioResult to zero before +the other soundstuff was complete.. and we happened to peek at it at just +the wrong moment} +Begin + SoundParmBlk^.iobuffer := aSoundPtr;{set pointer to our sound} + SoundParmBlk^.ioreqcount := BuffSize;{size of our sound buffer} + Repeat Until(TickCount > LastTick);{we'll wait a tick before writing sound} + err := PBWrite(SoundParmBlk,true);{start the sound, asynch} +end; + +procedure InitialFourToneSound; +{Set NoteCount to first pitch and duration of our Song array (loaded from +resource file), load that data into BlueSound record and start the sound. +The varible ToneDelay is checked on each event loop, will flag if another note +needs to be loaded into BlueSound. We're loading in TakeCareSound...,if +BlueSound duration runs out then the Song is restarted from the top.} +var + i:integer; +Begin + NoteCount := 1; + + BlueSound^.sound1Rate := mySongPtr^.pitch[NoteCount,1]; + BlueSound^.sound2Rate := mySongPtr^.pitch[NoteCount,2]; + BlueSound^.sound3Rate := mySongPtr^.pitch[NoteCount,3]; + BlueSound^.sound4Rate := mySongPtr^.pitch[NoteCount,4]; + BlueSound^.duration := 80;{none of our notes is over 60 ticks long} + + ToneDelay := mySongPtr^.duration[NoteCount] + TickCount; + + If SoundOn then + WriteASoundBlock(ptr(Sound[Blue]),SizeOf(Sound[Blue]^),TickCount); +End;{of Procedure} + + +procedure CreateSound; +Var + i,j,k: integer; +Begin + {load Blue Danube from resource file} + mySong := SongHandle(GetResource('SONG',524));{this is BlueDanube notes} + HLock(Handle(mySong));{we're locking it permanently} + mySongPtr := SongPtr(mySong^); + + new(SawToothWave); {wavePtr...a SawToothWave form for Blue sound} + new(SquareWave); {wavePtr...a squarewave form for Blue sound} + for i := 0 to 127 do {build our four tone waves} + begin + SawToothWave^[i] := 2*i;{sort of a raspy sound....} + SawToothWave^[i+128] := 0; + SquareWave^[i] := 0; + SquareWave^[i+128] := 192;{height of Square determines Loudness} + end; + + {we'll coerce Blue to FreeForm pointer throughout the program just so we + can use WhichSound and our SoundList type later on....} + new(BlueSynth); {my FTSynthPtr, FourTone Synthesizer} + BlueSynth^.mode := ftMode; + + {note: the duration field must be reset after each Bluesound as the driver + decrements its value} + + new(BlueSound);{our fourtone sound} + BlueSound^.sound1Phase := 0; + BlueSound^.sound2Phase := 128; + BlueSound^.sound3Phase := 128; {out of phase just for fun} + BlueSound^.sound4Phase := 0; + BlueSound^.sound1Wave := SawToothWave; + BlueSound^.sound2Wave := SawToothWave; + BlueSound^.sound3Wave := SquareWave; + BlueSound^.sound4Wave := SquareWave; + + {must make sndRec point to our Sound} + BlueSynth^.sndRec := BlueSound; + Sound[Blue] := FFSynthPtr(BlueSynth);{its just a pointer.. so coerce it} + + Buff[AirDoor] := 1486;{size all the freeform buffers} + Buff[BigDoor] := 3706;{size should be multiple of 370 plus 6 bytes} + Buff[Game] := 3706; {6 bytes for mode and count fields} + Buff[GravTone] := 1486; + Buff[Buzz] := 1486; + + {now create all the FreeForm sounds....} + For WhichSound := AirDoor to Buzz do begin {they all have these in common} + myHandle := NewHandle(Buff[WhichSound]);{new block for our sound} + HLock(myHandle); {we're locking the blocks permanently!!} + myPtr := myHandle^; {dereference the handle} + Sound[WhichSound] := FFSynthPtr(myPtr); + Sound[WhichSound]^.mode := ffMode; + {knock off 6 bytes for mode & count.. plus 1 for switch to Zero base} + Buff[WhichSound] := Buff[WhichSound] - 7; + end; + + {AirDoor Stuff} + Sound[AirDoor]^.count := FixRatio(1,6);{see the Sound Driver stuff} + j := 0; + While j<= Buff[AirDoor] do Begin + Sound[AirDoor]^.WaveBytes[j] := abs(Random) div 1024; {fill up the buffer} + inc(j); + end; { of while} + + {BigDoor sound} + Sound[BigDoor]^.count := FixRatio(1,2); + j := 0; + While j<= Buff[BigDoor] do Begin + If j< 2000 then i := abs(Random) div 1024; {random number 0 to 64} + If j = 2000 then i := 127; + if (j mod 127 = 0) and (i=127) then i := 0 + else if (j mod 127 = 0) and (i=0) then i := 127; + Sound[BigDoor]^.WaveBytes[j] := i; {fill up the buffer} + inc(j); + end; { of while} + + {Game background sound} + Sound[Game]^.count := FixRatio(1,8); {fixed point notation} + j := 0; + k := 1; + While j<= Buff[Game] do Begin + If k > 0 then i := (abs(Random) div 1024) + 127; + If k > 10 then i := 96; + If k > 20 then i := 159; + If k > 30 then k := 0; + Sound[Game]^.WaveBytes[j] := i; {fill up the buffer} + inc(k); + inc(j); + end; { of while} + + {GravTone when Force Changes sound...} + {We'll set the Count Field each time we play a tone..in AnimateOneLoop} + j := 0; + i := 0; + k := 0; + While j<= Buff[GravTone] do Begin {Square wave with freq of 128 bytes} + Sound[GravTone]^.WaveBytes[j] := i; + If k = 64 then i := 255; + If k = 128 then begin + k := 0; + i := 0; + end;{if k} + inc(k); + inc(j); + end; { of while} + + {Buzz Sound during penaltys against the chamber wall} + Sound[Buzz]^.count := FixRatio(1,2); {fixed point notation} + j := 0; + i := 0; + While j<= Buff[Buzz] do Begin + Sound[Buzz]^.WaveBytes[j] := i; + If i < 255 then inc(i) else i := 0;{SawTooth wave} + inc(j); + end; { of while} + + {create our Sound Parameter block for use with PBWrite} + new(SoundParmBlk); + with SoundParmBlk^ do begin {see Apple tech note 19, PBWrite vs. StartSound} + iocompletion := nil; + iorefnum := -4; + {we'll poke in the pointers and size stuff in WriteASoundBlock} + ioresult := 0; {will Start sound ,MainEventLoop} + end; {of with} +end; + +procedure BuildGravArray(Diff:integer);{Diff is difficulty} +{build array of 30 forces to be used during game} +var + mag,dir: real; + maxmag,maxdir,maxtime,mintime:real; + i:integer; +Begin + maxmag := diff + 1.5; {maximum magnitude of vector} + maxdir := diff*Pi/3.0; {maximum change in angle of vector} + maxtime := 30.0/diff; {maximum number of loops force will last} + minTime := 15.0/diff; {minimum loops force will last} + dir := abs(Random/(maxint/2*Pi));{set a random angle to start} + For i := 1 to 30 do begin + mag := abs(Random/(maxint/maxmag));{magnitude of force vector} + If mag < 1.0 then mag := 1.0; + dir := abs(dir + Random/(maxint/maxdir));{angle of force in radians} + If dir > 2*Pi then dir := dir - 2*Pi; + Grav[i].magH := round(mag*cos(dir));{x axis component of force} + Grav[i].magV := round(mag*sin(dir));{y axis component of force} + Grav[i].duration := round(abs(Random/(maxint/(maxtime-mintime)))+mintime); + Grav[i].liteNdx := round(dir/(2*Pi/16.0));{which lite to light?} + end;{for i := } +End; + +procedure CreateWindow;{windows,dialogs} +var + tRect: Rect; +Begin + myWindow := GetNewWindow(WindResId,@wRecord,Pointer(-1)); + SetRect(tRect,0,0,600,400);{this will clip OffScreen bitMaps as well!!} + + {remember: Dialog itemlists should be purgable, dialogs invisible} + SetDAFont(202);{Stuttgart font for all the dialogs...} + For DialNdx := Help to BMap do begin {read all the dialogs into array} + myDialog[DialNdx] := + GetNewDialog(ord(DialNdx)+129,@myDialogRec[DialNdx],myWindow); + SetPort(myDialog[DialNdx]); + ClipRect(tRect);{set clip to smaller size..} + TextSize(9); {size of font} + end; + + {editText item TextHandle controls the font stuff in the NameDialog box} + GetFontInfo(FontFacts);{will get fontstuff from current grafport(BMap)} + SetPort(myDialog[Name]);{has a editable text item...effects Text} + tDialPeek := DialogPeek(myDialog[Name]);{coerce pointer} + tDialPeek^.textH^^.txSize := 9;{see TextHandle stuff,changing font size} + tDialPeek^.textH^^.fontAscent := FontFacts.ascent; + tDialPeek^.textH^^.lineHeight := FontFacts.ascent + FontFacts.descent; + + ShowWindow(myWindow);{done so back to myWindow...} + SetPort(myWindow); + ClipRect(tRect); {i-166, set cliprgn to small rgn} + TextFont(202);{Stuttgart font,installed in resource file} + TextSize(9); +end; + +procedure CreateControls; +var + i,width: integer; +Begin + ResumeButton := GetNewControl(pressResume,myWindow); + EndButton := GetNewControl(pressEnd,myWindow); + {load Difficulty buttons,locate them later in CntlBox rectangle} + For i := 1 to 3 do Radio[i] := GetNewControl(132 + i,myWindow); +End; + +procedure CreatePictures; {get all the PICT's from resource file} +{we've created a type called PictureList so that we can access the +PicHandle and Rect arrays by 'title' instead of a number,also +all Picture.picframes are preset in resource to proper coordinates for +drawing into OffScreen bitmap otherwise they'd have to be 'Located' first.} +var + tRect:Rect; +Begin + For PicNdx := Pilot to Title do begin {almost all of PictureList..} + pPict[PicNdx] := GetPicture(ord(PicNdx) + 128);{get picture} + R[PicNdx] := pPict[PicNdx]^^.picFrame;{get Rect for picture} + end; + + {create CntlBox picture... just a frame} + SetRect(R[CntlBox],0,0,97,59);{size of CntlBox} + tRect := R[CntlBox]; + InSetRect(tRect,2,2); + pPict[CntlBox] := OpenPicture(R[CntlBox]); + EraseRect(R[CntlBox]); + FrameRect(R[CntlBox]); + FrameRoundRect(R[CntlBox],20,20); + FrameRect(tRect); + ClosePicture; +end; + +procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41} +var + bRect: Rect; +Begin + {OffScreen BitMap stuff, will hold all our shapes for animation} + SetRect(bRect,0,0,320,296); { drawing area,size to contain all pics } + with bRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(bRect,-left,-top);{move rect to 0,0 topleft} + end; { of with } + + OffScreen.baseAddr := QDPtr(NewPtr(SizeOfOff)); + OffScreen.rowbytes := OffRowBytes; + OffScreen.bounds := bRect; + + {InScreen BitMap, Shapes are 'overlaid' in InScreen, then go to myWindow} + bRect := myWindow^.portRect;{ drawing area same as our onscreen window! } + with bRect do begin + OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!} + SizeOfOff := (bottom - top) * OffRowBytes; + OffSetRect(bRect,-left,-top); + end; { of with } + + InScreen.baseAddr := QDPtr(NewPtr(SizeOfOff)); + InScreen.rowbytes := OffRowBytes; + InScreen.bounds := bRect; +End; + +procedure DrawPicsIntoOffScreen; +{watch out for clipping of portRect,visRgn,& clipRgn of grafport record} +var + tRect:Rect; +Begin + OldBits := myWindow^.portBits; {preserve old BitMap} + tRect := myWindow^.portRect; + SetPortBits(OffScreen); { our new BitMap } + PortSize(400,330);{adjust portRect,visRgn & clipRgn for larger draw area} + CopyRgn(myWindow^.visRgn,tRgn); + CopyRgn(myWindow^.clipRgn,myWindow^.visRgn); + + FillRect(OffScreen.bounds,white); {erase our new BitMap to white} + + {All our Pictures were copied from a drawing of the bitmap in FullPaint + (the bitmap was located UpperLeft on the page) into the + ScrapBook. Then transfered from the ScrapBook to PICT resources with + ResEdit. By doing this the Picture.picFrame is already set to + Coordinates of picture location in OffScreen. OtherWise all Rects + would have to be moved into correct position before drawing.} + + For PicNdx := Pilot to Title do begin + DrawPicture(pPict[PicNdx],R[PicNdx]); {draw all the pictures} + ReleaseResource(handle(pPict[PicNdx])); {done so dump them} + end; + + {we've dumped the pictures so how about compacting the heap before loading + other stuff, like all those sound buffers we're going to lock down?} + mySize := CompactMem(1024);{force compaction of the heap...???} + + myWindow^.portRect := tRect; {restore old portRect} + CopyRgn(tRgn,myWindow^.visRgn);{restore old visRgn} + SetPortBits(OldBits); {restore old bitmap} +end; + +procedure CreateOffScreenRects; +{ where are all those shapes? locate all the shapes in the OffScreen bitmap +by defining the rectangles that contain them. We'll use the OffScreen rects +as the 'Source' in Copybits. Probably would be more efficent to have all +OffScreen Rects in a list like our pict Rects and have them predefined in +a resource file... then just load in the whole bunch all at once. } +var + i,j: integer; + tRect: Rect; +Begin + OffBigChamber := R[BigChamber]; + + tRect := R[Pilot]; {locate OffPilot shapes} + tRect.right := tRect.left + 19; + tRect.bottom := tRect.top + 19; {size of one pilot} + For i := 1 to 3 do begin + For j := 1 to 3 do begin + OffPilot[j,i] := tRect;{load 9 shapes} + tRect.left := tRect.left + 19; + tRect.right := tRect.right + 19; + end;{for j} + MoveRect(tRect,R[Pilot].left,tRect.bottom,1);{next row} + end;{for i} + R[Pilot] := tRect;{resize Pilot to a single shape} + + tRect := R[BigPilot]; {locate OffBigPilot shapes} + tRect.right := tRect.left + 22; + tRect.bottom := tRect.top + 22; {size of one BigPilot} + For i := 1 to 3 do begin + For j := 1 to 3 do begin + OffBigPilot[j,i] := tRect;{9 shapes} + tRect.left := tRect.left + 22; + tRect.right := tRect.right + 22; + end;{for j} + MoveRect(tRect,R[BigChamber].right,tRect.bottom,1); + end;{for i} + R[BigPilot] := tRect;{resize to hold one BigPilot} + + tRect := R[Pod]; + tRect.right := tRect.left + 27; + OffPod[1] := tRect;{empty pod} + OffSetRect(tRect,26,0);{2 Pods one with pilot,one empty} + OffPod[2] := tRect; + R[Pod] := tRect; + + OffDoor := R[Door]; + OffDoor.right := OffDoor.left + 65; + OffFrame := R[Door]; + OffFrame.left := OffFrame.right - 33; + R[Door] := OffFrame; + OffDoorSlide := OffFrame;{DoorSlide will slide along OffDoor} + OffSetRect(OffDoorSlide,OffDoor.right-OffDoorSlide.right,0); + + OffStar := R[Star]; + SetRect(tRect,0,0,38,38); + OffStarWindow := tRect; + MoveRect(OffStarWindow,OffStar.right,OffStar.top,2); + InStar := OffStarWindow; + + tRect := R[BigFlip]; + tRect.right := tRect.left + 22;{width of one flip} + For i := 1 to 7 do begin + OffBigFlip[i] := tRect; + tRect.left := tRect.left + 22; + tRect.right := tRect.right + 22; + end;{for i} + OffHold := tRect;{will hold last inscreen during freeflight animation} + MoveRect(OffHold,OffPod[2].right,R[TargetBox].bottom,1); + + tRect := R[Flip]; + tRect.right := tRect.left + 19;{width of one flip} + For i := 1 to 7 do begin + OffFlip[i] := tRect; + tRect.left := tRect.left + 19; + tRect.right := tRect.right + 19; + end;{for i} + OffPatch := tRect;{will be used to refresh background under pilot} + OffPilotMask := tRect;{will be used to mask target over pilot} + + OffAirLock := R[AirLock]; + OffTargetBox := R[TargetBox]; + + OffScoreBox := R[ScoreBox]; + OffTimerBox := R[TimerBox]; + OffTitle := R[Title]; + + tRect := R[Numeral]; + tRect.right := tRect.left + 20; + for i := 0 to 3 do begin + OffNumeral[i] := tRect; + tRect.left := tRect.left + 20; + tRect.right := tRect.right + 20; + end;{for i} + TargetNum := tRect; + + {expand Mask to size of BigChamber for OffPilotMask} + i := R[Mask].left - R[BigChamber].left; + InsetRect(R[Mask],-i,-i); + OffMask := R[Mask]; +End; + +procedure CreateRegions; +var i: integer; +Begin + StarRgn := NewRgn;{rgn used to mask stars drawn into InScreen} + LimitRgn := NewRgn;{rgn used to limit Pilot movement to inside chamber} + DoorRgn := NewRgn; + CoverRgn := NewRgn;{rgn used to mask Cover doors over chamber} + tRgn := NewRgn;{temporary region} + ChamberRgn := NewRgn;{rgn used to mask to chamber} + BigChamberRgn := NewRgn;{a little larger than chamber} + DoorSlideRgn := NewRgn; + for i := 0 to 3 do TargetRgn[i] := NewRgn;{used to find points/score} +End; + +procedure DisplayDialog(WhichDialog:DialogList); +var + tRect,fRect: Rect; + itemHit,i,j: integer; + tPort: GrafPtr; +Begin + GetPort(tPort); + ShowWindow(myDialog[WhichDialog]); + SelectWindow(myDialog[WhichDialog]); + SetPort(myDialog[WhichDialog]); {so we can draw into our dialog window} + + Case WhichDialog of + Help: + ModalDialog(Nil,itemHit); {close it no matter what was hit} + About: + ModalDialog(Nil,itemHit); {close it no matter what was hit} + LScore:begin + DrawLoScores;{draw the scorestuff into the dialog box} + + Repeat + ModalDialog(Nil,itemHit); {find which button hit,OK or BACKFLIP} + + Case itemHit of + 2:begin{control button, start processing loScores} + SetCtlValue(ControlHandle(ScoreControl[2]),1);{set control true} + SetCtlValue(ControlHandle(ScoreControl[3]),0);{set control true} + FigureLoScore := True;{set the flag to true} + end; + 3:begin{Stop processing LoScores} + SetCtlValue(ControlHandle(ScoreControl[2]),0);{set control true} + SetCtlValue(ControlHandle(ScoreControl[3]),1);{set control true} + FigureLoScore := False;{set the flag to true} + end; + 6:begin{Reset the LoScores to defaults} + For i := 1 to 3 do + For j := 1 to 3 do begin + LoScore^[i,j].nameStr := 'Pilot';{set all nameStrings to pilot} + LoScore^[i,j].score := i + 1.0;{new loscores} + end; + InvalRect(myDialog[LScore]^.portRect);{modalDialog will redraw items} + EraseRect(myDialog[LScore]^.portRect); + DrawLoScores;{draw our reset scores back in} + DrawScoreIntoBox(LoScore^[Difficulty,1].score,tinyLoScore); + ChangedResource(ScoreHandle);{flag so we write out changes} + end;{case 6} + end; { of case itemHit} + + Until (itemHit = 1); {the ok button or 'enter' key} + end;{LScore} + SCode:{about the sourcecode} + ModalDialog(Nil,itemHit); {close it no matter what was hit} + BMap:begin{peek offscreen bitmap} + CopyBits(OffScreen,myDialog[BMap]^.portBits,OffScreen.bounds, + OffScreen.bounds,srcCopy,nil); {copy bitmap to dialog box} + ModalDialog(Nil,itemHit); {close it no matter what was hit} + end;{BMap} + end;{Case WhichDialog} + + HideWindow(myDialog[WhichDialog]); + SelectWindow(myWindow);{restore our game window} + SetPort(tPort);{restore port} +end; + +procedure DoMenuCommand(mResult:LongInt); +var + name: Str255; + tPort: GrafPtr; + i,h: integer; +Begin + theMenu := HiWord(mResult); + theItem := LoWord(mResult); + Case theMenu of + appleMenu: + Begin + GetPort(tPort); + If theItem = 1 then DisplayDialog(About) + Else begin + GetItem(myMenus[1],theItem,name);{must be a desk acc.} + refNum := OpenDeskAcc(name); + end; + SetPort(tPort); + End; + fileMenu: Finished := True; {quit this program} + optionMenu: + Case theItem of + 1:DisplayDialog(Help); + 2:DisplayDialog(LScore); + 3:Begin {toggle sound on or off} + If SoundOn then SoundOn := false else SoundOn := true; + CheckItem(myMenus[3],theItem,SoundOn);{check if true,none if false} + end; + end; { case theItem} + worksMenu: + Case theItem of + 1:DisplayDialog(BMap); + 2:DisplayDialog(SCode); + end;{case theItem} + End; + HiliteMenu(0); +End; + +procedure InitialThisGame;{set up for opening the coverdoors/starting game} +var i:integer; +Begin + MoveRect(R[Pilot],Home.h+65,Home.v+65,1);{locate pilot} + MoveRect(OffPilotMask,OffMask.left+65,OffMask.top+65,1);{synch with pilot} + Dh := 0;Dv := 0;{no pilot movement} + BuildGravArray(Difficulty);{create the forces for this difficulty} + GravNdx := 1;{force stuff} + Sustain := 0; + LightNdx := 0; + CoverLoops := 24; {number of loops that coverdoor animation will take} + PilotStatus := OpenDoors; {this will begin coverdoor animation in maineventloop} + SetPt(PilotNdx,2,2);{this is standing still shape} + Penalty := 0; + Bonus := 0;{score stuff} + TimeLine := TimeSlide.left - 1;{TimeLine will be coordinate and counter} + LoopCount := 1;{divided into score to find avg.score} + FlipInProgress := False; + err := PBKillIO(SoundParmBlk,false);{kill AirLock sound} + WhichSound := BigDoor; {will start BigDoor Sound} +end; + +procedure CloseTheCoverDoors; +Begin + TopCover.bottom := R[Cover].top + 69; + BotCover.top := R[Cover].bottom - 70; +End;{of CloseTheCover..} + +procedure TakeCareControls(whichControl:ControlHandle;localMouse:point); +var + ControlHit,i: integer; +Begin + ControlHit := TrackControl(whichControl,localMouse,nil); { Find out which} + If ControlHit > 0 then {i-417} + Begin + If whichControl = ResumeButton then {RESUME the game..} + Begin + InsertMenu(myMenus[5],0); {display exit message} + For i := 1 to 4 do DisableItem(myMenus[i],0); + DrawMenuBar; + HideControl(ResumeButton); + HideControl(EndButton); + GameUnderWay := True; {we're back into game mode} + PauseUnderWay := False; + HideCursor; + FlushEvents(mDownMask,0); {clear all mouseDowns} + End; + If whichControl = EndButton then {END current game...} + Begin + HideControl(ResumeButton);{hide the resume and end} + HideControl(EndButton); + InvalRect(myWindow^.portRect);{force redraw of all} + CloseTheCoverDoors; + {close the airlock doors just in case} + OffSetRect(OffDoorSlide,OffDoor.right-OffDoorSlide.right,0); + For i := 1 to 3 do HiliteControl(Radio[i],0);{enable controls} + EraseRect(CntlCover); + For i := 1 to 3 do ShowControl(Radio[i]); + PilotStatus := Resting;{just so drawupdate will handle controls ok} + PauseUnderWay := False; + WhichSound := Silence;{no sounds} + End; + For i := 1 to 3 do begin + If whichControl = Radio[i] then begin + SetCtlValue(Radio[Difficulty],0); {uncheck current button} + Difficulty := i; {this is our new difficulty from 1 to 3} + {check new button,(contrlMax field must be set to 1)} + SetCtlValue(Radio[i],1); + DrawScoreIntoBox(LoScore^[Difficulty,1].score,tinyLoScore); + end;{if which} + end;{for i} + End; {of If ControlHit} +End; { of procedure} + +procedure PauseThisGame; {called if a keydown during game} +var + i: integer; +Begin + err := PBKillIO(SoundParmBlk,false);{kill sound} + {copy current screen to InScreen, so update events are redrawn correctly} + CopyBits(myWindow^.portBits,InScreen,myWindow^.portRect, + InScreen.bounds,srcCopy,nil); + GameUnderWay := False; { halt animation } + ShowCursor; + ShowControl(ResumeButton); + ShowControl(EndButton); + DeleteMenu(MessageMenu); {remove exit message,i-354} + For i := 1 to 4 do EnableItem(myMenus[i],0);{show other menu options} + DrawMenuBar; + PauseUnderWay := True; +End; + +procedure InitialAirLock;{setup to start Airlock animation} +Begin + CopyBits(OffScreen,myWindow^.portBits,OffHold, + R[BigPilot],srcCopy,nil);{erase last pilot} + MoveRect(R[BigPilot],R[Door].left+3,R[Door].top+3,1);{pilot in front of door} + SetPt(PilotNdx,2,2);{this is standing still shape} + Dh := 0;Dv := 0; + PilotStatus := EnterLock;{will begin Airlock animation} + FlipInProgress := False; + OffSetRect(OffDoorSlide,OffDoor.right-OffDoorSlide.right,0);{DoorSlide to right} + DoorOpening := True;{flag if door is opening or closing}; + err := PBKillIO(SoundParmBlk,false);{kill Blue Danube sound} + WhichSound := AirDoor;{will start AirDoor Sound} +end;{procedure} + +procedure InitialFreeFlight;{on mousedown in Pod... begin Freeflight} +var + i:integer; +Begin + GameUnderWay := True; + InsertMenu(myMenus[5],0); + For i := 1 to 4 do DisableItem(myMenus[i],0); + DrawMenuBar; {display exit message} + For i := 1 to 3 do HiliteControl(Radio[i],255);{disable Controls} + FillRect(BonusRect,Gray); + FillRect(PenaltyRect,Gray);{gray out for nice dark background} + FillRect(ScoreRect,Gray); + FillRect(LoScoreRect,Gray); + FillRect(TargetNum,Gray); + + For i := 1 to 3 do HideControl(Radio[i]);{will generate update event} + BeginUpdate(myWindow);{clear Update stuff...} + EndUpDate(myWindow); + FlushEvents(updateMask,0);{clear Update stuff caused by HideControl} + + FillRect(CntlCover,Gray);{cover where the controls were} + CopyBits(OffScreen,myWindow^.portBits,OffPod[1], + R[Pod],srcCopy,nil);{empty Pod} + {copy myWindow to InScreen so we can do animation 'overlays' in InScreen} + CopyBits(myWindow^.portBits,InScreen,myWindow^.portRect, + myWindow^.portRect,srcCopy,nil); {copy screen to inscreen} + + MoveRect(R[BigPilot],R[Pod].left,R[Pod].bottom,4);{pilot on top of empty Pod} + {OffHold will hold a copy of background under the pilot} + CopyBits(InScreen,OffScreen,R[BigPilot],OffHold,srcCopy,nil); + SetPt(PilotNdx,2,2);{this is standing still shape} + PilotStatus := FreeFlight;{type of animation} + Dh := 0;Dv := 0; + FlipInProgress := False; + LoopCount := 1;{for flashing door} + HideCursor; {game mode,no normal mouse functions} + FlushEvents(mDownMask,0); {clear mousedowns} + + WhichSound := Blue;{Blue Danube song} + InitialFourToneSound;{start the song} +end; + +procedure BeginAFlip; +Begin {start a flip} + FlipInProgress := True; + If Dh>1 then begin {man is moving right so rotate flip right} + PilotNdx.h := 1;{set to first flip} + RotateRight := True;{will cause PilotNdx to be incremented} + end {if Dh>1} + else begin {else pilot will flip to the left} + PilotNdx.h := 7; + RotateRight := False;{will cause PilotNdx to be decremented} + end;{else} +end;{procedure} + +procedure TakeCareMouseDown(myEvent:EventRecord); +var + Location: integer; + WhichWindow: WindowPtr; + WhichControl: ControlHandle; + MouseLoc: Point; + WindowLoc: integer; + ControlHit: integer; +Begin + If GameUnderWay = True then {handle a game click} + Case PilotStatus of + InChamber,OpenDoors: BeginAFlip; + + FreeFlight:If RectInRgn(R[BigPilot],DoorRgn) then InitialAirLock + else BeginAFlip; + end {Case pilotStatus} + Else begin {Mouse is normal...handle normal functions} + MouseLoc := myEvent.Where; {Global coordinates} + WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287} + case WindowLoc of + inMenuBar: + DoMenuCommand(MenuSelect(MouseLoc)); + inSysWindow: + SystemClick(myEvent,WhichWindow); {i-441} + inContent: + If WhichWindow <> FrontWindow then SelectWindow(WhichWindow) + else + Begin + GlobaltoLocal(MouseLoc); + ControlHit := FindControl(MouseLoc,whichWindow,whichControl); + If ControlHit > 0 then TakeCareControls(whichControl,Mouseloc) + Else {check for click in Pod Rect, will begin freeflight} + If (PtInRect(MouseLoc,R[Pod])) and (not(PauseUnderWay)) + then InitialFreeFlight; + end; + end; {case of} + end; { of Else} +end; { TakeCareMouseDown } + +PROCEDURE TakeCareKeyDown(Event:EventRecord); +Var + CharCode: char; +Begin + CharCode := chr(LoWord(BitAnd(Event.message,CharCodeMask))); + + If BitAnd(Event.modifiers,CmdKey) = CmdKey then + DoMenuCommand(MenuKey(CharCode)) + Else If GameUnderWay then PauseThisGame;{pause with any other key press} +End; + +procedure TakeCareActivates(myEvent:EventRecord); +var + WhichWindow: WindowPtr; +Begin + WhichWindow := WindowPtr(myEvent.message); + SetPort(WhichWindow); +End; + +procedure OneTimeGameStuff; {set up the gamestuff only needed on startup} +var + Dest: Point; + i,j,width,dh,dv,h,v:integer; + tRect:Rect; +Begin + SetRect(MouseRect,210,134,302,206); { this is for mapping control } + SetRect(DeltaRect,-4,-4,4,4); { this is for finding Pilot offset } + BorderRect := myWindow^.portRect; { limit pilot to this area } + InsetRect(BorderRect,10,10);{shrink by width of pilot} + MoveRect(BorderRect,0,0,1);{will use topleft} + + SetRect(tRect,32,32,463,274); {inside rect of borderframes} + For i := 1 to 16 do begin + WindFrame[i] := tRect;{we'll let update draw the border} + InsetRect(tRect,-2,-2); + end; + + OpenRgn; + FrameOval(InStar); + CloseRgn(StarRgn);{used as mask for drawing stars into chamber 'window'} + + OpenRgn; + SetRect(tRect,0,0,105,105); + FrameOval(tRect); + CloseRgn(LimitRgn);{used to limit pilot.topleft movement inside chamber} + + tRect := OffBigChamber; + InsetRect(tRect,13,13); + OpenRgn; + FrameOval(tRect); + CloseRgn(ChamberRgn); {used to mask Chamber} + + tRect := OffFrame; {DoorFrame} + InsetRect(tRect,3,3); + OpenRgn; + FrameRect(tRect); + CloseRgn(DoorRgn); {used to mask door during slide open} + + tRect := OffFrame; {DoorFrame} + OpenRgn; + FrameRect(tRect); + CloseRgn(DoorSlideRgn); {used to mask door during slide closed} + + {Locate BigChamber !!!!! note: was (32,30) in small window} + OffSetRect(R[BigChamber],64,66); + + {all other shapes are relative to R[BigChamber].topleft..call it 'Home'} + Home := R[BigChamber].topleft; + + OpenRgn; + FrameOval(R[BigChamber]); + CloseRgn(BigChamberRgn);{mask when drawing onto screen} + + OffSetRgn(StarRgn,Home.h+54-StarRgn^^.rgnBBox.left, + Home.v+54-StarRgn^^.rgnBBox.top); + StarBox := StarRgn^^.rgnBBox; + MoveRect(InStar,StarBox.right,StarBox.top,2); {InStar is dest.for OffStar} + + R[Cover] := R[BigChamber]; + InsetRect(R[Cover],4,4);{size of cover over the chamber} + TopCover := R[Cover];{locate... CloseTheCover.. will set proper size} + BotCover := R[Cover]; + CloseTheCoverDoors; + + OpenRgn; + FrameOval(R[Cover]); + CloseRgn(CoverRgn); {used to mask coverdoors} + + i := 13; {this is amount Chamber is inset from R[BigChamber]} + OffSetRgn(ChamberRgn,Home.h + i-ChamberRgn^^.rgnBBox.left, + Home.v + i-ChamberRgn^^.rgnBBox.top); + + OffSetRgn(LimitRgn,Home.h+12-LimitRgn^^.rgnBBox.left, + Home.v+12-LimitRgn^^.rgnBBox.top); + + {create target rings...concentric regions with limitRgn as outside} + CopyRgn(LimitRgn,TargetRgn[3]);{copy of LimitRgn for extreme target edge} + + For i := 3 downto 1 do begin + CopyRgn(TargetRgn[i],TargetRgn[i-1]); + InsetRgn(TargetRgn[i-1],10,10); + DiffRgn(TargetRgn[i],TargetRgn[i-1],TargetRgn[i]); + end;{for i} + + MoveRect(R[Mask],Home.h+24,Home.v+24,1); + + MoveRect(R[TargetBox],Home.h+147,Home.v-7,1); + MoveRect(TargetNum,R[TargetBox].left+14,R[TargetBox].top+17,1); + + MoveRect(R[AirLock],Home.h+157,Home.v+47,1); + MoveRect(R[Door],Home.h+171, + Home.v+67,1); + OffSetRgn(DoorRgn,R[Door].left+3-DoorRgn^^.rgnBBox.left, + R[Door].top+3-DoorRgn^^.rgnBBox.top); + + MoveRect(R[CntlBox],Home.h+208, + Home.v-14,1); + CntlCover := R[CntlBox]; + InsetRect(CntlCover,3,3);{will cover controls during freeflight} + + h := R[CntlBox].left + 9; + v := R[CntlBox].top + 6; + For i := 1 to 3 do begin + MoveControl(Radio[i],h,v);{locate Diff.buttons in Cntlbox} + v := v + 16; + end;{for i} + + MoveRect(R[ScoreBox],Home.h+313,Home.v-14,1); + {locate the four score areas in scorebox} + + BonusRect := R[ScoreBox]; + OffSetRect(BonusRect,3,16);{move topleft to bonus rect} + BonusRect.right := BonusRect.left + 62; + BonusRect.bottom := BonusRect.top + 18; + tinyBonus := BonusRect; + InsetRect(tinyBonus,7,5);{inset to reduce erase area,calculation for text} + PenaltyRect := BonusRect; + OffSetRect(PenaltyRect,0,41); + tinyPenalty := PenaltyRect; + InsetRect(tinyPenalty,7,5); + ScoreRect := PenaltyRect; + OffSetRect(ScoreRect,0,41); + tinyScore := ScoreRect; + InsetRect(tinyScore,7,5); + LoScoreRect := ScoreRect; + OffSetRect(LoScoreRect,0,41); + tinyLoScore := LoScoreRect; + InsetRect(tinyLoScore,7,5); + + MoveRect(R[TimerBox],Home.h+19,Home.v+166,1); + MoveRect(R[Title],R[TimerBox].left,R[TimerBox].top,1);{title on top of Timerbox} + SetRect(TimeSlide,0,0,99,7);{size of rect inside TimerBox} + MoveRect(TimeSlide,R[TimerBox].left+5,R[TimerBox].top+11,1); + MoveRect(R[Pod],Home.h+252,Home.v+144,1); + MoveRect(MouseRect,R[Pod].left-50,R[Pod].top-35,1);{center mouse on R[Pod]} + + Difficulty := 1;{Radio buttons come with 1 preset,IsSoEasy value = 1} + + CreateLoScoreStuff; + NameString := 'Pilot';{will start with pilot then use user input} + GetDItem(myDialog[Name],4,i,NameHandle,tRect);{get handle to text item} + {get handle to controls,Calculate LoScore buttons} + For i := 2 to 3 do GetDItem(myDialog[LScore],i,j, + ScoreControl[i],tRect); + SetCtlValue(ControlHandle(ScoreControl[2]),1);{set control true} + FigureLoScore := True;{flag to true,we will calculate LoScores as default} + Penalty := 0; + Bonus := 0; + AvgScore := 0.0; + PauseUnderWay := False; + PilotStatus := Resting;{for proper update drawing} + + CopyBits(OffScreen,InScreen,OffBigChamber,R[BigChamber],srcCopy,nil); + +End; { of OneTimeGameStuff } + +procedure MapMouseToDelta; +{change current mouse location into offset for pilot..Dh,Dv} +Begin + If FlipInProgress and (PilotStatus <> FreeFlight) then SetPt(MouseLoc,0,0) + Else begin {get user mouse input} + GetMouse(MouseLoc); { MouseLoc in Coords of currentGrafPort } + + {if out of bounds then limit MouseLoc to MouseRect extremes } + If MouseLoc.h > MouseRect.right then MouseLoc.h := MouseRect.right + else If MouseLoc.h < MouseRect.left then MouseLoc.h := MouseRect.left; + + If MouseLoc.v > MouseRect.bottom then MouseLoc.v := MouseRect.bottom + else If MouseLoc.v < MouseRect.top then MouseLoc.v := MouseRect.top; + + {now map MouseLoc from MouseRect into DeltaRect...MouseLoc becomes + request from user for new Offset for Pilot} + MapPt(MouseLoc,MouseRect,DeltaRect);{great!,thanks to Bill Atkinson} + end;{else} + {we want change of direction to be smooth so 'damp' by letting MouseLoc + change Dh,Dv only one unit per loop.} + If MouseLoc.h>Dh then inc(Dh) {MouseLoc.h won't be over 4 or under -4} + else if MouseLoc.h < Dh then dec(Dh); + If MouseLoc.v > Dv then inc(Dv) + else if MouseLoc.v < Dv then dec(Dv);{Dh,Dv are rate of pilot movement} +end; + +procedure AnimateStuffInCommon;{for both OneLoop & CoverDoors} +Begin + {test if new location is ok... not outside the chamber area} + tPoint.h := R[Pilot].left + Nh;{tPoint = temporary new pilot location} + tPoint.v := R[Pilot].top + Nv; + If not(PtInRgn(tPoint,LimitRgn)) then begin {new pilot is outside the Chamber!} + Repeat + Nh := Nh div 2;{move half way back to last pilot location} + Nv := Nv div 2; + tPoint.h := R[Pilot].left + Nh; + tPoint.v := R[Pilot].top + Nv; + Until(PtInRgn(tPoint,LimitRgn));{half offset until back inside limitRgn} + + inc(Penalty);{add to penalty,does nothing in CoverDoors} + + If SoundOn and (WhichSound <> Buzz) then begin {write out a buzzsound} + err := PBKillIO(SoundParmBlk,false);{kill current sound} + WriteASoundBlock(ptr(Sound[Buzz]),Buff[Buzz],TickCount); + WhichSound := Buzz; + end;{if soundOn} + end;{of if not PtInRect} + + {locate our 3 rects which move in sequence} + R[Pilot].left := R[Pilot].left + Nh; {faster than an OffsetRect} + R[Pilot].right := R[Pilot].right + Nh; + R[Pilot].top := R[Pilot].top + Nv; + R[Pilot].bottom := R[Pilot].bottom + Nv; + + OffPatch.left := OffPatch.left + Nh; {faster than an OffsetRect} + OffPatch.right := OffPatch.right + Nh; + OffPatch.top := OffPatch.top + Nv; + OffPatch.bottom := OffPatch.bottom + Nv; + + OffPilotMask.left := OffPilotMask.left + Nh; {faster than an OffsetRect} + OffPilotMask.right := OffPilotMask.right + Nh; + OffPilotMask.top := OffPilotMask.top + Nv; + OffPilotMask.bottom := OffPilotMask.bottom + Nv; + + {which shape to draw?,PilotNdx points to one of 9 shapes} + If not(FlipInProgress) then begin + If MouseLoc.h = 0 then PilotNdx.h := 2 + else if MouseLoc.h > 0 then PilotNdx.h := 3 else PilotNdx.h := 1; + If MouseLoc.v = 0 then PilotNdx.v := 2 + else if MouseLoc.v > 0 then PilotNdx.v := 3 else PilotNdx.v := 1; + end;{if not flip} + + If OffStarWindow.left = OffStar.left then + OffSetRect(OffStarWindow,OffStar.right-OffStarWindow.right,0) + Else OffSetRect(OffStarWindow,-1,0);{so we can animate our stars} + + {score ....start in center, if pilot is there fall thru else try next, + TargetRgn is an array of concentric rings} + SubScore := 0; + While (not(PtInRgn(R[Pilot].topleft,TargetRgn[SubScore]))) do begin + inc(SubScore); {find if pilot is in 0-3 target circles} + end; + + CopyBits(OffScreen,myWindow^.portBits,OffNumeral[SubScore], + TargetNum,srcCopy,nil);{proper numeral into target box} +end;{of procedure} + +procedure DrawTheCoverDoors; +Begin + OldBits := myWindow^.portBits; + SetPortBits(InScreen); + + FillRect(TopCover,dkGray); + FillRect(BotCover,dkGray); + MoveTo(TopCover.left,TopCover.bottom); + LineTo(TopCover.right,TopCover.bottom); + MoveTo(BotCover.left,BotCover.top); + LineTo(BotCover.right,BotCover.top); + + SetRectRgn(tRgn,TopCover.left,TopCover.bottom,TopCover.right,BotCover.top); + DiffRgn(CoverRgn,tRgn,tRgn);{find area for mask on coverdoors} + CopyBits(OffScreen,InScreen,OffMask,R[BigChamber],srcBic,tRgn); + + SetPortBits(OldBits); + CopyBits(InScreen,myWindow^.portBits,R[Cover],R[Cover],srcCopy,CoverRgn); +end; + +procedure AnimateOneLoop;{this is the main game loop} +var + i,j:integer; + tRect: Rect; +Begin + {OffPatch floats over OffBigChamber..used to restore background over + last pilot in InScreen} + CopyBits(OffScreen,InScreen,OffPatch,R[Pilot],srcCopy,nil); + lastPilot := R[Pilot];{keep a temporary copy of our last R[Pilot]} + + MapMouseToDelta;{get new Dh,Dv for pilot} + + If Sustain <1 then begin {then go get another Grav record,new force} + Gh := Grav[GravNdx].magH; + Gv := Grav[GravNdx].magV; + Sustain := Grav[GravNdx].duration;{how many loops will grav work} + + FillOval(Lights[LightNdx],dkgray);{erase last light} + LightNdx := Grav[GravNdx].liteNdx;{get next light index} + EraseOval(Lights[LightNdx]); + FrameOval(Lights[LightNdx]);{turn on new light} + If SoundOn then begin {write a Tone Sound for new force} + err := PBKillIO(SoundParmBlk,false);{kill current sound} + i := abs(Gh) + abs(Gv);{watch out for zero magnitude} + Sound[GravTone]^.count := FixRatio(i,2);{let magnitude influence GravTone} + WriteASoundBlock(ptr(Sound[GravTone]),Buff[GravTone],TickCount); + end;{if soundOn} + If GravNdx < 30 then inc(GravNdx) else GravNdx := 1;{30 elements in array} + end{if Sustain} + Else dec(Sustain); + Nh := Dh + Gh;{net effect of flight control and gravity} + Nv := Dv + Gv; + + AnimateStuffInCommon;{stuff in common to OneLoop and CoverDoors} + + Penalty := Penalty + SubScore; + inc(LoopCount); + DrawIntegerIntoBox(Penalty,tinyPenalty); + AvgScore := (Penalty - Bonus)/LoopCount; + If AvgScore < 0 then AvgScore := 0; + DrawScoreIntoBox(AvgScore,tinyScore); + + CopyBits(OffScreen,InScreen,OffStarWindow,InStar,srcCopy,StarRgn); + + If FlipInProgress then begin {draw the Pilot flip shapes, get next} + CopyBits(OffScreen,InScreen,OffFlip[PilotNdx.h], + R[Pilot],notSrcBic,nil); + If RotateRight then + If PilotNdx.h < 7 then inc(PilotNdx.h) {next flip shape} + else begin + FlipInProgress := False; {flip is over.. reset normal status} + Bonus := Bonus + Difficulty;{more points for higher level} + DrawIntegerIntoBox(Bonus,tinyBonus); + end{else} + else + If PilotNdx.h > 1 then dec(PilotNdx.h) {next flip shape} + else begin + FlipInProgress := False; {flip is over.. reset normal status} + Bonus := Bonus + Difficulty; + DrawIntegerIntoBox(Bonus,tinyBonus); + end;{else} + end { if flipinprogress} + Else {normal pilot... not flipping} + CopyBits(OffScreen,InScreen,OffPilot[PilotNdx.h,PilotNdx.v], + R[Pilot],notSrcBic,nil); {modes on page i-157} + + {copy pilot sized piece of mask over the new pilot,OffPilotMask + floats over OffMask shape in synch with Pilot and OffPatch} + CopyBits(OffScreen,InScreen,OffPilotMask,R[Pilot],srcOr,nil); + + UnionRect(LastPilot,R[Pilot],LastPilot);{rect that contains both Pilots} + UnionRect(LastPilot,Instar,LastPilot); {find rect that contains pilots & Stars} + + CopyBits(InScreen,myWindow^.portBits,LastPilot, + LastPilot,srcCopy,ChamberRgn); + + {monitor our game time...indicator below chamber} + If (LoopCount mod 6 = 0) then begin {we only draw a line every 6 loops} + inc(TimeLine);{one step closer to ending game} + + If TimeLine = TimeSlide.right then begin {then end this game } + GameUnderWay := False; { halt animation } + PilotStatus := Resting;{for draw update stuff} + err := PBKillIO(SoundParmBlk,false); + WhichSound := Silence; + ShowCursor; + DeleteMenu(MessageMenu); {remove exit message,i-354} + For i := 1 to 4 do EnableItem(myMenus[i],0);{show other menu options} + DrawMenuBar; + FillOval(Lights[LightNdx],dkGray);{erase last light} + FillRect(TargetNum,Gray);{Cover last Num...} + CloseTheCoverDoors; + DrawTheCoverDoors;{draw cover & mask} + + CopyBits(OffScreen,myWindow^.portBits,OffPod[2], + R[Pod],srcCopy,nil); + If (AvgScore < LoScore^[Difficulty,3].score) and + FigureLoScore then ProcessNewLoScore; + For i := 1 to 3 do HiliteControl(Radio[i],0);{enable controls} + InvalRect(CntlCover);{will force redraw of controls} + InvalRect(R[Title]);{will force redraw of Title over TimerBox} + end{then begin} + Else begin + moveTo(TimeLine,TimeSlide.top); + LineTo(TimeLine,TimeSlide.bottom);{draw the indicator line in timerbox} + end;{else} + end;{if LoopCount mod 6} +End;{of procedure} + +procedure AnimateFreeFlight;{animate Pilot over the entire window} +var + i,j:integer; + tRect: Rect; + Where:Longint; +Begin + MapMouseToDelta; + + tRect := R[BigPilot];{keep last location in temporaryRect} + R[BigPilot].left := R[BigPilot].left + Dh; {faster than an OffsetRect} + R[BigPilot].right := R[BigPilot].right + Dh; + R[BigPilot].top := R[BigPilot].top + Dv; + R[BigPilot].bottom := R[BigPilot].bottom + Dv; + + If not(PtInRect(R[BigPilot].topleft,BorderRect)) then begin{outside,find which} + If R[BigPilot].left > BorderRect.right then + OffSetRect(R[BigPilot],BorderRect.right-R[BigPilot].left,0); + If R[BigPilot].left < BorderRect.left then + OffSetRect(R[BigPilot],BorderRect.left-R[BigPilot].left,0); + If R[BigPilot].top > BorderRect.bottom then + OffSetRect(R[BigPilot],0,BorderRect.bottom-R[BigPilot].top); + If R[BigPilot].top < BorderRect.top then + OffSetRect(R[BigPilot],0,BorderRect.top-R[BigPilot].top); + end;{of if not PtInRect} + + {which shape to draw?} + If not(FlipInProgress) {no flip} then begin + If MouseLoc.h = 0 then PilotNdx.h := 2 + else if MouseLoc.h > 0 then PilotNdx.h := 3 else PilotNdx.h := 1; + If MouseLoc.v = 0 then PilotNdx.v := 2 + else if MouseLoc.v > 0 then PilotNdx.v := 3 else PilotNdx.v := 1; + end;{if not flip} + + CopyBits(OffScreen,InScreen,OffHold,tRect,srcCopy,nil);{restore inscreen} + CopyBits(InScreen,OffScreen,R[BigPilot],OffHold,srcCopy,nil);{save current} + + If FlipInProgress then begin + CopyBits(OffScreen,InScreen,OffBigFlip[PilotNdx.h], + R[BigPilot],notSrcBic,nil); + If RotateRight then + If PilotNdx.h < 7 then inc(PilotNdx.h) {next flip shape} + else FlipInProgress := False {flip is over.. reset freeflight status} + else + If PilotNdx.h > 1 then dec(PilotNdx.h) {next flip shape} + else FlipInProgress := False;{flip is over.. reset freeflight status} + end { if FlipInProgress} + Else {normal pilot... not flipping} + CopyBits(OffScreen,InScreen,OffBigPilot[PilotNdx.h,PilotNdx.v], + R[BigPilot],notSrcBic,nil); + + UnionRect(tRect,R[BigPilot],tRect);{Rect to contain both last and current} + + CopyBits(InScreen,myWindow^.portBits,tRect,tRect,srcCopy,nil); + + If LoopCount < 7 then inc(LoopCount) + else begin + InvertRect(R[Door]);{flashing Airlock door stuff} + LoopCount := 1; + {take care of offscreen stuff in case of invert on screen} + CopyBits(OffScreen,InScreen,OffHold,R[BigPilot],srcCopy,nil);{restore inscreen} + OldBits := myWindow^.portBits; + SetPortBits(InScreen); + InvertRect(R[Door]);{invert the patched Door in InScreen bitmap} + SetPortBits(OldBits); + CopyBits(InScreen,OffScreen,R[BigPilot],OffHold,srcCopy,nil);{save current} + end; + +End;{of procedure} + +procedure DrawAirLockShapes;{tRgn masks frame over or behind pilot} +Begin + CopyBits(OffScreen,InScreen,OffFrame,R[Door],srcCopy,nil); + CopyBits(OffScreen,InScreen,OffDoorSlide,R[Door],srcCopy,DoorRgn); + CopyBits(OffScreen,InScreen,OffBigPilot[PilotNdx.h,PilotNdx.v], + R[BigPilot],notsrcBic,tRgn); + CopyBits(InScreen,myWindow^.portBits,R[Door],R[Door],srcCopy,nil); +end; + +procedure AnimateAirLock; +var + i,j:integer; + MouseLoc,tpoint: Point; + tRect: Rect; +Begin + {find random movement for our Pilot while door is opening/closing} + SetPt(MouseLoc,Random div (Maxint div 2),Random div (Maxint div 2)); + If MouseLoc.h>Dh then inc(Dh) {MouseLoc.h won't be over 4 or under -4} + else if MouseLoc.h < Dh then dec(Dh); + If MouseLoc.v > Dv then inc(Dv) + else if MouseLoc.v < Dv then dec(Dv);{Dh,Dv are rate of pilot movement} + + R[BigPilot].left := R[BigPilot].left + Dh; {faster than an OffsetRect} + R[BigPilot].right := R[BigPilot].right + Dh; + R[BigPilot].top := R[BigPilot].top + Dv; + R[BigPilot].bottom := R[BigPilot].bottom + Dv; + + If R[BigPilot].left < R[Door].left then + OffSetRect(R[BigPilot],R[Door].left-R[BigPilot].left,0) + else If R[BigPilot].right > R[Door].right then + OffSetRect(R[BigPilot],R[Door].right-R[BigPilot].right,0); + If R[BigPilot].top < R[Door].top then + OffSetRect(R[BigPilot],0,R[Door].top-R[BigPilot].top) + else If R[BigPilot].bottom > R[Door].bottom then + OffSetRect(R[BigPilot],0,R[Door].bottom-R[BigPilot].bottom); + + If DoorOpening then begin {The door is opening} + If OffDoorSlide.left > OffDoor.left then begin {still sliding} + OffSetRect(OffDoorSlide,-1,0); {move it one to left} + RectRgn(tRgn,myWindow^.portRect);{whole screen,just so its not nil} + DrawAirLockShapes; + + end {if still sliding} + else begin {begin door closing stuff} + OffSetRgn(DoorSlideRgn,R[Door].right-DoorSlideRgn^^.rgnBBox.left, + R[Door].top-DoorSlideRgn^^.rgnBBox.top);{will mask pilot} + DoorOpening := False; + end;{else} + end {door is opening} + Else begin {Door is Closing} + If OffDoorSlide.right < OffDoor.right then begin {still sliding} + OffSetRect(OffDoorSlide,1,0); {move it one to right} + OffSetRgn(DoorSlideRgn,-1,0); {move it to left, will hide pilot} + DiffRgn(DoorRgn,DoorSlideRgn,tRgn);{pilot appears to be behind door} + + DrawAirLockShapes; + + end {if still sliding} + else InitialThisGame;{will begin coverdoor sequence and game} + end; {else door is closing} +End; { of procedure } + +procedure AnimateCoverDoors; +var + i,j:integer; + tRect: Rect; +Begin + MapMouseToDelta; + Nh := Dh;{net force and flight effects on Pilot} + Nv := Dv; + + AnimateStuffInCommon; + + CopyBits(OffScreen,InScreen,OffBigChamber,R[BigChamber],srcCopy,nil); + CopyBits(OffScreen,InScreen,OffStarWindow,InStar,srcCopy,StarRgn); + + If FlipInProgress then begin + CopyBits(OffScreen,InScreen,OffFlip[PilotNdx.h], + R[Pilot],notSrcBic,nil); {modes on page i-157} + If RotateRight then + If PilotNdx.h < 7 then inc(PilotNdx.h) {next flip shape} + else FlipInProgress := False {flip is over.. reset normal status} + else + If PilotNdx.h > 1 then dec(PilotNdx.h) {next flip shape} + else FlipInProgress := False; {flip is over.. reset normal status} + end { if flipinprogress} + Else {normal pilot... not flipping} + CopyBits(OffScreen,InScreen,OffPilot[PilotNdx.h,PilotNdx.v], + R[Pilot],notSrcBic,nil); {modes on page i-157} + + {copy just pilot sized piece of mask over the new pilot} + CopyBits(OffScreen,InScreen,OffPilotMask,R[Pilot],srcOr,nil); + + TopCover.bottom := TopCover.bottom - 3; + BotCover.top := BotCover.top + 3; + + DrawTheCoverDoors; + + If CoverLoops > 0 then dec(CoverLoops) + Else begin + PilotStatus := InChamber;{doors are open so begin the game animation} + EraseRect(BonusRect); + EraseRect(PenaltyRect); + EraseRect(ScoreRect); + EraseRect(LoScoreRect); + EraseRect(CntlCover); + For i := 1 to 3 do ShowControl(Radio[i]); + DrawScoreIntoBox(LoScore^[Difficulty,1].score,tinyLoScore); + CopyBits(OffScreen,myWindow^.portBits,OffTimerBox, + R[TimerBox],srcCopy,nil);{will erase Titlebox} + {sequence OffPatch and OffPilotMask with current R[Pilot]} + i := R[Pilot].left - Home.h; + j := R[Pilot].top - Home.v; + MoveRect(OffPatch,OffBigChamber.left+i,OffBigChamber.top+j,1); + err := PBKillIO(SoundParmBlk,false);{kill CoverDoors sound} + WhichSound := Game; {will start GameSound} + end;{of Else} +End;{of procedure} + +procedure CreateLightsArray;{array of 17 rects around chamber} +var + dir:real; + mag,i,centerH, + centerV: integer; + tpoint: Point; + tRect:Rect; +begin + SetRect(tRect,0,0,12,12); + dir := 0; + mag := 84;{radius to center of buttons} + centerh := R[BigChamber].left + ((R[BigChamber].right-R[BigChamber].left) div 2); + centerV := R[BigChamber].top + ((R[BigChamber].right-R[BigChamber].left) div 2); + + For i := 0 to 15 do begin + tPoint.h := round(mag * cos(i*2*Pi/16.0))+centerH-6;{-6 for half tRect} + tPoint.v := round(mag * sin(i*2*Pi/16.0))+centerV-6; + MoveRect(tRect,tpoint.h,tpoint.v,1); + Lights[i] := tRect; + end; {for i :=} + Lights[16] := Lights[0]; {0 radians is same as 2*Pi radians} +end; + +Procedure DrawUpDateStuff; +{will draw all our images in response to Update event,an Update event is +waiting for our newly opened window so will draw our first stuff too!} +var + tRect: Rect; + tpoint: Point; + i: integer; +Begin + {if the game is paused the screen has been saved in InScreen Bitmap} + If PauseUnderWay then CopyBits(InScreen,myWindow^.portBits, + InScreen.bounds,myWindow^.portRect,srcCopy,nil) {restore screen} + Else begin + For i := 16 downto 2 do FrameRect(WindFrame[i]); + FillRect(WindFrame[1],dkgray); + FrameRect(WindFrame[1]); + + CopyBits(OffScreen,myWindow^.portBits,OffBigChamber, + R[BigChamber],srcCopy,BigChamberRgn); + + DrawTheCoverDoors;{draw the Covers} + + For i := 0 to 15 do begin + FillOval(Lights[i],dkgray); + end;{for i :=} + + CopyBits(OffScreen,myWindow^.portBits,OffTargetBox, + R[TargetBox],srcCopy,nil); + CopyBits(OffScreen,myWindow^.portBits,OffAirLock, + R[AirLock],srcCopy,nil); + DrawPicture(pPict[CntlBox],R[CntlBox]); + If PilotStatus = FreeFlight then FillRect(CntlCover,gray);{update during freeflight} + CopyBits(OffScreen,myWindow^.portBits,OffScoreBox, + R[ScoreBox],srcCopy,nil); + + CopyBits(OffScreen,myWindow^.portBits,OffFrame, + R[Door],srcCopy,nil); + CopyBits(OffScreen,myWindow^.portBits,OffDoorSlide, + R[Door],srcCopy,DoorRgn);{mask to inside of door frame} + + Case PilotStatus of + InChamber:Begin {draw the timerBox instead of title,due to Switcher} + CopyBits(OffScreen,myWindow^.portBits,OffTimerBox, + R[TimerBox],srcCopy,nil); + tRect := TimeSlide;{indicator area} + tRect.right := TimeLine + 1; + FillRect(tRect,black); + end;{InChamber} + Otherwise CopyBits(OffScreen,myWindow^.portBits,OffTitle, + R[Title],srcCopy,nil); + end;{case Pilotstatus} + + CopyBits(OffScreen,myWindow^.portBits,OffPod[2], + R[Pod],srcCopy,nil); + + DrawIntegerIntoBox(Penalty,tinyPenalty); + DrawIntegerIntoBox(Bonus,tinyBonus); + DrawScoreIntoBox(AvgScore,tinyScore); + DrawScoreIntoBox(LoScore^[Difficulty,1].score,tinyLoScore); + end;{of Else} +end; + +procedure TakeCareUpdates(Event:EventRecord); +var + UpDateWindow,TempPort: WindowPtr; + itemHit: integer; + Test: Boolean; +Begin + UpDateWindow := WindowPtr(Event.message); + GetPort(TempPort); + SetPort(UpDateWindow); + BeginUpDate(UpDateWindow); + EraseRect(UpDateWindow^.portRect); + If UpDateWindow = myWindow then DrawUpDateStuff; + DrawControls(UpDateWindow); + EndUpDate(UpDateWindow); + SetPort(TempPort); +End; + +procedure TakeCareSoundStuff; +Begin + If SoundParmBlk^.ioResult < 1 then {sound is finished find another/play it} + Case WhichSound of + Blue: InitialFourToneSound; {Waltz quit.. so start again} + + Buzz:begin {buzz pilot touching wall sound,finished so go back to game} + WhichSound := Game; + WriteASoundBlock(ptr(Sound[Game]),Buff[Game],TickCount); + end;{buzz} + + AirDoor,BigDoor,Game: + WriteASoundBlock(ptr(Sound[WhichSound]),Buff[WhichSound],TickCount); + end {of case WhichSound} + Else If (WhichSound = Blue) and (TickCount > ToneDelay) then begin + inc(NoteCount); + BlueSound^.sound1Rate := mySongPtr^.pitch[NoteCount,1]; + BlueSound^.sound2Rate := mySongPtr^.pitch[NoteCount,2]; + BlueSound^.sound3Rate := mySongPtr^.pitch[NoteCount,3]; + BlueSound^.sound4Rate := mySongPtr^.pitch[NoteCount,4]; + BlueSound^.duration := 80; + + ToneDelay := mySongPtr^.duration[NoteCount] + TickCount; + + If mySongPtr^.noteCount = NoteCount then NoteCount := 0; + + end; {if TickCount} +End;{of procedure} + +procedure MainEventLoop; +var + myEvent: EventRecord; + i: integer; +Begin + InitCursor; + Repeat + SystemTask; + If GetNextEvent(EveryEvent,myEvent) then + Case myEvent.What of + mouseDown: TakeCareMouseDown(myEvent); + KeyDown: TakeCareKeyDown(myEvent); + ActivateEvt:TakeCareActivates(myEvent); + UpdateEvt:TakeCareUpdates(myEvent); + End {of Case} + Else {no event pending so lets do some game stuff} + Begin + If GameUnderWay then begin + Case PilotStatus of + InChamber: AnimateOneLoop;{animate Pilot in chamber, gamestuff} + OpenDoors: AnimateCoverDoors;{animate Chamber doors opening} + FreeFlight:begin {animate Pilot over entire window} + If not(SoundOn) then begin + aTick := TickCount; + Repeat Until(TickCount > aTick + 2);{slow down} + end; + AnimateFreeFlight; + end;{case 3}; + EnterLock:begin {animate pilot entering airlock door} + If not(SoundOn) then begin + aTick := TickCount; + Repeat Until(TickCount > aTick + 1);{slow down} + end; + AnimateAirLock; + end;{case 4} + end;{case PilotStatus} + + If SoundOn then TakeCareSoundStuff; + + end { if GameUnderWay} + + End; {else no event pending} + Until Finished; +End; + +procedure SetUpMenus; +var + i: integer; +Begin + myMenus[1] := GetMenu(appleMenu); {get menu info from resources} + AddResMenu(myMenus[1],'DRVR'); {add in all the DA's} + myMenus[2] := GetMenu(fileMenu); + myMenus[3] := GetMenu(optionMenu); + myMenus[4] := GetMenu(worksMenu); + myMenus[5] := GetMenu(MessageMenu); {this is the backspace message} + CheckItem(myMenus[3],3,True); {check the Sound item} + SoundOn := True; {sound will start on first begin} + For i := 1 to 4 do + begin + InsertMenu(myMenus[i],0); + end; + DrawMenuBar; +End; + +procedure CloseStuff; +var i:integer; +Begin + err := PBKillIO(SoundParmBlk,false);{kill sound before quitting} + WriteResource(ScoreHandle);{write out if changes were flagged..LoScore} +End; + +PROCEDURE InitThings; +Begin + InitGraf(@thePort); {create a grafport for the screen} + + MoreMasters; {extra pointer blocks at the bottom of the heap} + MoreMasters; {this is 5 X 64 master pointers} + MoreMasters; + MoreMasters; + MoreMasters; + + {get the cursors we use and lock them down - no clutter} + ClockCursor := GetCursor(watchCursor); + HLock(Handle(ClockCursor)); + + {show the watch while we wait for inits & setups} + SetCursor(ClockCursor^^); + + {init everything in case the app is the Startup App} + InitFonts; + InitWindows; + InitMenus; + TEInit;{for the Dialog stuff} + InitDialogs(Nil); + + Finished := False; {set terminator to false} + FlushEvents(everyEvent,0); {clear events} + Screen := ScreenBits.Bounds; { Get screen dimensions from thePort } +End; + +{Main Program begins here} +BEGIN + InitThings; + SetUpMenus; + CreateWindow; {load window,dialogs} + CreateControls; {our buttons, and radio controls} + CreateRegions; {newRgn for all our regions} + CreateOffScreenBitMap; {see Apple tech note 41,create OffScreen,InScreen} + CreatePictures; {load pictures from resources} + DrawPicsIntoOffScreen;{draw the pics into the OffScreen Bitmap} + CreateOffScreenRects; {set all rectangles for 'copybits' shape drawing} + CreateSound; {create all the sound buffers, etc} + OneTimeGameStuff; {Game varibles, scorebox stuff,etc} + CreateLightsArray; {array of rects for drawing indicator lights} + GameUnderWay := False;{click on Pod will begin a game sequence, mousedown event} + WhichSound := Silence; + For Dh := 1 to 3 do ShowControl(Radio[Dh]);{these are our difficulty controls} + MainEventLoop; {manage all user input until quit} + CloseStuff; {do this before quitting... always kill sound before quit} +END. diff --git a/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity:Rsrc b/Duane Blehm's Code/Zero GravityÆ’/ZeroGravity:Rsrc new file mode 100755 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5f4373c --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +# Tested only on Mac OS X, YMMV, etc + +all: unzip detab + +unzip: + unzip -o Duane\ Blehm\'s\ Code.zip >/dev/null + +detab: unzip + cp -Rp Duane\ Blehm\'s\ Code Duane\ Blehm\'s\ Code.bak + ( cd Duane\ Blehm\'s\ Code && find . -name '*.[pP]as' -exec tab2space -lf -t3 {} {} \; ) + ( cd Duane\ Blehm\'s\ Code && find . -name '*.[pP]as' -exec perl -i -p -e "s/ +\Z//" {} \; ) + ( cd Duane\ Blehm\'s\ Code && find . -name '*.[rR]' -exec tab2space -lf {} {} \; ) + ( cd Duane\ Blehm\'s\ Code && find . -name '*.txt' -exec tab2space -lf {} {} \; ) + ( cd Duane\ Blehm\'s\ Code && find . -exec touch -r ../Duane\ Blehm\'s\ Code.bak/{} {} \; ) + rm -r Duane\ Blehm\'s\ Code.bak + diff --git a/README.md b/README.md new file mode 100644 index 0000000..92f6845 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# Duane Blehm's Code + +This is an archive of the Duane Blehm's source code. Duane Blehm was an +early game developer for the Macintosh. He released three games in the +1980's: +[Zero Gravity](http://www.macintoshrepository.org/4249-zero-gravity-), +[StuntCopter](http://www.macintoshrepository.org/5314-stuntcopter), and +[Cairo ShootOut!](http://www.macintoshrepository.org/4856-cairo-shootout-). + +As a preteen computer nerd and budding developer, I not only loved the +games, but also had an interest in how a real application was made. +The About screens for the games offered source code printouts and unlock +codes for a small fee, so I saved up my allowance for a month or two, +stuffed a few bills and a note into an envelope, and mailed it off. + +I received a reply about a year later. It was from Blehm's parents, +gently explaining that their son had passed away and they couldn't offer +the unlock codes. They returned my money. I was too young and lucky to +have much experience with death. When I read the news, I remember +feeling a pang of... something. I never forgot this feeling, and I +never forgot about the source code. + +Some years later (a little over fifteen of them), Apple released a +Newton that ran NeXTSTEP and had cell phone radio. I was done becoming +a programmer and on to becoming a better one. I thought that porting +Zero Gravity to the iPhone would be a fun project, and began looking for +the source code again so that I could get the physics exactly right. + +Despite the best efforts of a small LiveJournal community, I came up +empty. I had enough other projects that I lost interest in that one +for the time being. + +But, just like before, I never stopped thinking about the code. +I decided earlier this year that it had been long enough since my last +try, and in 2016 the venue of choice was Twitter. I pecked out a short +plea for help, and in a hail-mary attempt to reach someone who had some +idea of how to help, I cc'ed Avadis Tevanian, Jr., who most people know +as a longtime VP at Apple but I remember primarily as the developer of +a beloved Missile Command port called MacCommand, and John Calhoun, who +wrote the fantastic paper-airplane game Glider. + +Avi Tevanian doesn't tweet, really. But John Calhoun did. Suddenly I +had the source code. + +So here it is, offered without license or warranty, the code for all +three of Duane Blehm's releases. I've converted line endings to LF and +detabbed the files according to Blehm's preference for three-space tab +stops, and I wrote this README and a top-level Makefile, but the rest was +his and now it's ours. + +Rest in peace, Duane Blehm. Thanks for the games. + + +## Contents + + ├── Duane Blehm's Code Data forks + │   ├── Animation Æ’ + │   ├── Cairo Æ’ + │   ├── Copy Mask Æ’ + │   ├── Drag Piece Æ’ + │   ├── More Info* + │   ├── Regions Æ’ + │   ├── StuntCopter Æ’ + │   └── Zero GravityÆ’ + │ + ├── __MACOSX Resource forks + │   └── Duane Blehm's Code + │   ├── Animation Æ’ + │   ├── Cairo Æ’ + │   ├── Copy Mask Æ’ + │   ├── Drag Piece Æ’ + │   ├── More Info* + │   ├── Regions Æ’ + │   ├── StuntCopter Æ’ + │   └── Zero GravityÆ’ + │ + ├── Duane Blehm's Code.zip Original source dump + │ + ├── Makefile Run `make` to rebuild this archive + │ + └── README.md This file + + +## Maintainer + +Pete Gamache, [pete@gamache.org](mailto:pete@gamache.org). + diff --git a/__MACOSX/._Duane Blehm's Code b/__MACOSX/._Duane Blehm's Code new file mode 100644 index 0000000..0b8a2bf Binary files /dev/null and b/__MACOSX/._Duane Blehm's Code differ diff --git a/__MACOSX/Duane Blehm's Code/._.DS_Store b/__MACOSX/Duane Blehm's Code/._.DS_Store new file mode 100644 index 0000000..09fa6bd Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._.DS_Store differ diff --git a/__MACOSX/Duane Blehm's Code/._Animation Æ’ b/__MACOSX/Duane Blehm's Code/._Animation Æ’ new file mode 100644 index 0000000..2c1a46e Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Animation Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._Cairo Æ’ b/__MACOSX/Duane Blehm's Code/._Cairo Æ’ new file mode 100644 index 0000000..5b7a2ce Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Cairo Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._Copy Mask Æ’ b/__MACOSX/Duane Blehm's Code/._Copy Mask Æ’ new file mode 100644 index 0000000..fd09e1c Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Copy Mask Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._Drag Piece Æ’ b/__MACOSX/Duane Blehm's Code/._Drag Piece Æ’ new file mode 100644 index 0000000..9bf33f7 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Drag Piece Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._More Info* b/__MACOSX/Duane Blehm's Code/._More Info* new file mode 100644 index 0000000..c9f6d6a Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._More Info* differ diff --git a/__MACOSX/Duane Blehm's Code/._Regions Æ’ b/__MACOSX/Duane Blehm's Code/._Regions Æ’ new file mode 100644 index 0000000..b324b0a Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Regions Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._StuntCopter Æ’ b/__MACOSX/Duane Blehm's Code/._StuntCopter Æ’ new file mode 100644 index 0000000..65d88d0 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._StuntCopter Æ’ differ diff --git a/__MACOSX/Duane Blehm's Code/._Zero GravityÆ’ b/__MACOSX/Duane Blehm's Code/._Zero GravityÆ’ new file mode 100644 index 0000000..b51dfc8 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/._Zero GravityÆ’ differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._About Animation.txt b/__MACOSX/Duane Blehm's Code/Animation Æ’/._About Animation.txt new file mode 100644 index 0000000..74b1a77 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._About Animation.txt differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim.R b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim.R new file mode 100644 index 0000000..bf922ab Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim.R differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim:rsrc b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim:rsrc new file mode 100644 index 0000000..0e35676 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Anim:rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation new file mode 100644 index 0000000..3304e30 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation Steps b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation Steps new file mode 100644 index 0000000..2c5e31d Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation Steps differ diff --git a/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation.Pas b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation.Pas new file mode 100644 index 0000000..f31f134 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Animation Æ’/._Animation.Pas differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._About Cairo files.. b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._About Cairo files.. new file mode 100644 index 0000000..220978a Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._About Cairo files.. differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Animation b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Animation new file mode 100644 index 0000000..4c97340 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Animation differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Shapes b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Shapes new file mode 100644 index 0000000..c1eb7c6 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo Shapes differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo ShootOut v1.2L b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo ShootOut v1.2L new file mode 100644 index 0000000..55663fa Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._Cairo ShootOut v1.2L differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoFont.pro b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoFont.pro new file mode 100644 index 0000000..741c199 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoFont.pro differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoShootOut.Pas b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoShootOut.Pas new file mode 100644 index 0000000..849cf5a Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._CairoShootOut.Pas differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo.R b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo.R new file mode 100644 index 0000000..71beef9 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo.R differ diff --git a/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo:rsrc b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo:rsrc new file mode 100644 index 0000000..605fe65 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Cairo Æ’/._aCairo:rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._About CopyMask b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._About CopyMask new file mode 100644 index 0000000..2c5e31d Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._About CopyMask differ diff --git a/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask.R b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask.R new file mode 100644 index 0000000..e4b3b19 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask.R differ diff --git a/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask:rsrc b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask:rsrc new file mode 100644 index 0000000..f20c0b2 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CMask:rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask new file mode 100644 index 0000000..cb273aa Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask differ diff --git a/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask.pas b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask.pas new file mode 100644 index 0000000..1ba2b59 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Copy Mask Æ’/._CopyMask.pas differ diff --git a/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._About DragPiece.txt b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._About DragPiece.txt new file mode 100644 index 0000000..78a5a94 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._About DragPiece.txt differ diff --git a/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPiece animation b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPiece animation new file mode 100644 index 0000000..4c97340 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPiece animation differ diff --git a/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo new file mode 100644 index 0000000..2b3a53c Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo differ diff --git a/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo.Pas b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo.Pas new file mode 100644 index 0000000..9df6fe5 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._DragPieceDemo.Pas differ diff --git a/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._aDragPiece.rsrc b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._aDragPiece.rsrc new file mode 100644 index 0000000..58523f5 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Drag Piece Æ’/._aDragPiece.rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/More Info*/._Programming the Mac.4.6 b/__MACOSX/Duane Blehm's Code/More Info*/._Programming the Mac.4.6 new file mode 100644 index 0000000..0602a06 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/More Info*/._Programming the Mac.4.6 differ diff --git a/__MACOSX/Duane Blehm's Code/More Info*/._TML Std.Procedures b/__MACOSX/Duane Blehm's Code/More Info*/._TML Std.Procedures new file mode 100644 index 0000000..33cef05 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/More Info*/._TML Std.Procedures differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._About Regions b/__MACOSX/Duane Blehm's Code/Regions Æ’/._About Regions new file mode 100644 index 0000000..9596566 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._About Regions differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions b/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions new file mode 100644 index 0000000..7e42530 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions.pas b/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions.pas new file mode 100644 index 0000000..41ab0d8 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._Regions.pas differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._RgnWork.rsrc b/__MACOSX/Duane Blehm's Code/Regions Æ’/._RgnWork.rsrc new file mode 100644 index 0000000..2a15d7b Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._RgnWork.rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions.R b/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions.R new file mode 100644 index 0000000..255058f Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions.R differ diff --git a/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions:Rsrc b/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions:Rsrc new file mode 100644 index 0000000..098c475 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Regions Æ’/._aRegions:Rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._About StuntCopter files.. b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._About StuntCopter files.. new file mode 100644 index 0000000..6e15d6f Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._About StuntCopter files.. differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._Anim.R b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._Anim.R new file mode 100644 index 0000000..c170d8f Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._Anim.R differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.R b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.R new file mode 100644 index 0000000..13f4f2f Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.R differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.pas b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.pas new file mode 100644 index 0000000..b62909b Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.pas differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.shapes b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.shapes new file mode 100644 index 0000000..4c97340 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter.shapes differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter1.5 b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter1.5 new file mode 100644 index 0000000..5bddb05 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter1.5 differ diff --git a/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter:Rsrc b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter:Rsrc new file mode 100644 index 0000000..558b752 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/StuntCopter Æ’/._StuntCopter:Rsrc differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero Animation b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero Animation new file mode 100644 index 0000000..c2a5d9a Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero Animation differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero files.. b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero files.. new file mode 100644 index 0000000..a8d52cc Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._About Zero files.. differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._Game Animation b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._Game Animation new file mode 100644 index 0000000..4c97340 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._Game Animation differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity new file mode 100644 index 0000000..0ca9f97 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.R b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.R new file mode 100644 index 0000000..45982c5 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.R differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.pas b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.pas new file mode 100644 index 0000000..c3fc9b6 Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity.pas differ diff --git a/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity:Rsrc b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity:Rsrc new file mode 100644 index 0000000..1e2987f Binary files /dev/null and b/__MACOSX/Duane Blehm's Code/Zero GravityÆ’/._ZeroGravity:Rsrc differ