blehm/Duane Blehm's Code/Cairo ƒ/CairoShootOut.Pas

2812 lines
115 KiB
Plaintext
Executable File

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.