-
Notifications
You must be signed in to change notification settings - Fork 1
/
LowLevel.p
1284 lines (1141 loc) · 35.9 KB
/
LowLevel.p
1
Unit LowLevel;(* PURPOSE:Definisce le procedure base di accesso alla struttura dati definita in DreamTypes. *)InterfaceUSES Types, QuickDraw, { List 2 } Controls, Events, OSUtils, SegLoad, { List 3 - needs List 1/2 types } Files, { needs OSUtils, SegLoad } Windows, { needs Events, Controls } { List 4 - needs List 1/2/3 types } (* Lists, Lista3, *) BinIO, DreamTypes;VAR transcriptWindow, { The transcript windoid } mainWindow: WindowPtr; { The main window } message: string; { String inside the status line } timePlace, { Space on main window where time is shown. } paneRect, { Space on main window where map is shown } groupRect: Rect; { Space on main window where group icons are shown } watchRect: Rect; { Spazio dove appare il cerchio che si riempie } vicinityRect: { Space on main window for group symbol and immediate surroundings } ARRAY [-1..1, -1..1] OF Rect; amtTilesInMap: Rect; { How many tiles above and beyond group in main window. New for v1.1 } kMWHeight, kMWWidth: INTEGER; { Erano costanti in 1.0 } Ora: INTEGER; Giorno: INTEGER; { Lighting conditions } artificialLight: integer; { Numero di sorgenti di luce attive } lightRange: integer; { The location where the group is } groupX, groupY: integer; currentSavegameFile, currentScenarioFile: MyFile; { The scenario where the group is } { The place where the group is } currentPlaceHandle: Handle; placeDirty: Boolean; { Set if place should be saved (group discovered some heretofore unknown location) } placeID: INTEGER; { Res ID of place } placeMap: MapPtr; { The map of the place where the group is } placeName: String; placeW, placeH: integer; { Should be byte, butÉ } placeTime: integer; { Time to move one unit } placeTEXTIn, placePICTin, placeViewPict: integer; placeData: BitsInByte; { [7] must be saved on exit [6] light is needed here [5] has wandering monsters [4] is underground [3] is shop [2] is special [1] Use Pict instead of icons to represent [0] Show all immediately upon entry } morePlaceData: BitsInWord; { [15]: is indoors [14] Please use abstract (large) group icon in it [13] Use custom music, not standard one } { RESOURCE CONVENTION: If place is shop, then placeTime is shop item list resID. If place is special (shrine, temple, etc) then placeTime is kind of place (that is, ord(SpecialPlace)). DoLoadPlace, when loading, rationalizes things using the following globals:} placeKind: SpecialPlace; { The status of the game } gGameRunning, gSoundIsOn, gSpeechIsOn, gTranscriptIsOn, gNotificationIsOn, gMusicIsOn, gQuickTimeIsOn: Boolean; { Font info - system independent } smallFontID, stdFontID, smallFontSize, stdFontSize: Integer; { Per DoLoadPlace - sono qui perchŽ sia possibili salvarli } locationStack: TLocationStack; locationStackPointer: Integer {0..kMaxPlacesOnStack}; { Punta all'ultima locazione riempita. Cresce verso i numeri pi alti. } { Per le finestre di testo e grafica } { sono qui perchŽ sia possibili salvarne la posizione } PICTwindow, TEXTwindow: WindowPtr; tickleTime: longint; { Istante in cui stata cliccata la barra di status. Mi serve per fare in modo che il nome del posto dove mi trovo appaia per dieci secondi al pi. } { Tratti dalle Info dello scenario, e caricati da DoLoadScenario in GraphEngine } scenarioName, nomeDesigner: String; { Info sullo scenario. Altre sono in LowLevel.p } suggestedScreenDepth: Integer; { Screen depth suggested for current scenario } minCharNumber, maxCharNumber, { Per le routine che creano personaggi } minCharLevel, maxCharLevel, startingCharLevel: Byte; languageCode: Integer; { Per uso di Galatea } scenarioSignature: OSType; gPercentWM: Byte; { % che appaiano mostri vaganti } { Per la protezione dalla copia } userName, password: Str255;{*** Inizializzazione e shutdown ***}PROCEDURE LowLevelInit;{ Init Macintosh toolbox, TaskMaster and other support code }PROCEDURE LowLevelRestart;{ Parte di LowLevelInit: va richiamata quando si vuole caricare un altro gioco }PROCEDURE LowLevelShutdown;{*** Gestione di basso livello delle risorse ***}Function GetLongintFromRes (VAR scanner: Ptr): Longint;Function GetIntegerFromRes (VAR scanner: Ptr): integer;Function GetByteFromRes (VAR scanner: Ptr): byte;Function GetStringFromRes (VAR scanner: Ptr): String;Function GetWBFromRes (VAR scanner: Ptr): BitsInWord;Function GetBBFromRes (VAR scanner: Ptr): BitsInByte;Function MyGetResource (resKind: ResType; ref: INTEGER; isFatal: boolean; isScenario: boolean): Handle;{ Chiamata al posto di GetResource (i primi due parametri sono i medesimi).Bisogna mettere isFatal a true se la risorsa indispensabile alla prosecuzionedel programma (normalmente lo ). Se MyGetResource non trova una risorsa fataleavvisa l'utente dell'errore ed esce. In questo caso, per comunicargli da cosadipende l'errore, controlla il valore di isScenario: se true incolpa loscenario, altrimenti incolpa l'applicazione stessa.Se il caricamento ha funzionato, MyGetResource restituisce la handle alla risorsacaricata, locked. Altrimenti (ma pu˜ succedere solo se isFatal false) ritorna NIL }FUNCTION OldKindOfAttackToKindOfAttackv21 (old: Integer): KindOfAttack;{ Nel passare alla versione 2.1 ho eliminato i set (perchŽ CodeWarrior una merda).Nel passare ai set cambiato l'ordine dei bit dentro i PowerSet (vedi note in DreamTypes.p)Allora ho modificato l'enum del KindOfAttack per ripristinare la coerenza.Di conseguenza devo trasformare l'enumerato vecchia maniera nel nuovo formato }{*** Funzioni di basso livello per l'uso dei moduli superiori ***}PROCEDURE BarmanTalks (hisPhrase: INTEGER);PROCEDURE SellAlert (id: INTEGER);{ Finestra di dialogo che avvisa di un problema nell'acquisto o vendita }PROCEDURE GenericDreamAlert (id: INTEGER);{ Finestra di dialogo che avvisa di un problema }PROCEDURE DepthAlert;{ Finestra di dialogo che suggerisce di modificare la screen depth }Function Dado (numeroDadi, numeroFacce: integer): integer;{ Lancia un dado o un gruppo di dadi }Function Best3OutOf (n: integer): integer;{ Da usare per creare le caratteristiche di un personaggio: tira n dadia 6 facce, e restituisce la somma dei 3 tiri pi alti. }Procedure FailNil (o: UNIV handle);{ Da chiamare dopo aver invocato New. Termina il programma se l'allocazione non ha avuto successo }{*** Gestione del suono ***}{ Per una gestione ottimale del suono, che non consumi tempo macchina, eccol'interfaccia: chiamare la seguente procedura per avviare un suono: }PROCEDURE DoSoundAsync (soundRef: INTEGER);{ E la seguente procedura per pronunziare una frase: }PROCEDURE DoSpeechAsync (textRef: INTEGER);{ All'inizializzazione della macchina, e dopo un evento Resume, chiamare: }PROCEDURE SetupSoundChannel;{ In questo programma, SetupSoundChannel chiamato automaticamente da LowLevelInit }{ Allo shutdown e quando arriva un evento suspend, chiamare: }PROCEDURE KillSoundChannel;{ In questo programma, KillSoundChannel chiamato automaticamente da LowLevelShutdown }{*** Interfaccia utente di bassa manovalanza ***}Procedure CursorInit;{ Init animated "wait" cursor }Procedure CursorAnimate;{ Animates cursor }PROCEDURE TextOut (textID: Integer; foreground: Boolean);{ Se il parametro diverso da zero, fa apparire una finestra con quel messaggio.Altrimenti esce senza fare nulla.Se foreground TRUE, porta la finestra del testo in primo piano }PROCEDURE KillTEXT;{ Makes the windoid with the text disappear }PROCEDURE PictOut (pictID: Integer);{ Analogo a Textout }PROCEDURE KillPICT;{ Analogo a KillText }PROCEDURE AddToTranscript (s1: String; resStr1: Integer; s2: String; resStr2: Integer);{ Aggiunge una riga al transcript. Le stringhe vengono inserite verbatim nell'ordinespecificato. I numeri sono intesi come indici nello STR# di ID rTranscriptMessages.é possibile passare stringhe nulle nei primi, e il numero 0 nei secondi, per indicareche una parte delle sostituzioni non vanno effettuate, in stile ParamText }PROCEDURE StatusLine (what: string);{ Impone l'apparizione di un messaggio sulla status line.Se "what" la stringa nulla, lascia che il messaggio sia quello standard (data e ora).La chiamata di questa procedure impone il ridisegno della status line alprimo ciclo eventi }{*** Roba Macintosh ***}FUNCTION MyFindControl (controlRefCon: Integer): ControlHandle;{ Dato un refcon, restituisce la Controlhandle del controllo con quel refcon se si trovanel port corrente. IL CONTROLLO DEVE ESISTERE, SENNO' BOOOM }PROCEDURE DoCheckItem (myMenu, myItem: integer; check: boolean);{ Mette (o toglie, se check = false) il segno di spunta a una voce di menu }Function CommandPeriod: boolean;{ Restituisce true se stato premuto mela-punto.Tiene conto delle tastiere nazionalizzate }Procedure MyMoveWindow (w: WindowPtr; pos: Point; update: Boolean);{ Rimpiazza MoveWindow del toolbox. Ha lo stesso effetto, ma controllaprima di spostare che la nuova posizione sia valida - se non lo , noneffettua lo spostamento. }FUNCTION GiveBackWindowPositionOnScreen (w:WindowPtr): Point;{ restituisce la coordinata dell'angolo in alto a sinistradella finestra sullo schermo }FUNCTION GetIndNrect (nrectID, serial: Integer): Rect;{ Legge un rettangolo da una risorsa "nrct" specificata }PROCEDURE ResizeMainWindow (noChunkyMode: Boolean);{ Da chiamare quando la finestra principale stata ridimensionata. Normalmente,questa procedura aggiusta le dimensioni perchŽ siano un multiplo esatto di 32pixel. Se noChunkyMode TRUE, ci˜ viene evitato (serve nei negozi) }PROCEDURE PresentaRichiestaRegistrazione;FUNCTION Verifica: BOOLEAN;{ Controllo del numero di serie }ImplementationUses Appearance, Errors ,FixMath ,Fonts ,GestaltEqu ,Icons { Icon manager del System 7 - vedi technote "icons in 7" } ,Memory ,Menus ,QDOffscreen ,QuickDrawText ,Resources ,Sound ,TextEdit ,TextUtils ,ToolUtils { List 3 - needs List 1/2 types } ,Aliases { (3.2) needs Memory } ,Script { needs OSUtils, FixMath } { List 4 - needs List 1/2/3 types } ,Dialogs { needs TextEdit, Windows } ,Palettes { needs windows } ,StandardFile { (3.2) needs Aliases } , MusicEngine, Galatea, Cilindro, DialogLord4, TaskMaster3;VAR { For the sound routines } mySoundHandle: Handle; myChan: SndChannelPtr; { Per le finestre di testo e grafica } PICTinTheWindow: PICHandle;{$S UtilMain}FUNCTION Verifica: BOOLEAN;CONST kVideoComSignature = 'VC'; kSharewareSignature = 'SD'; kNumeroMagicoVideocom = 67279; kEnormeNumeroPrimo = 14983;VAR i, coppiaDiByte: Integer; checksum, numeroDesiderato: Longint; stringToCheck: Str255;BEGIN IF Copy (password, 1, 2) = kVideoComSignature THEN BEGIN numeroDesiderato := kNumeroMagicoVideocom; stringToCheck := Copy (password, 3, length (password)-2) END ELSE IF Copy (password, 1, 2) = kSharewareSignature THEN BEGIN stringToCheck := userName; numeroDesiderato := SToI (Copy (password, 3, length (password)-2)) END ELSE BEGIN Verifica := FALSE; Exit (verifica) END; FOR i := length(stringToCheck)+1 TO length(stringToCheck)+3 DO stringToCheck[i] := chr (0); checksum := 0; FOR i := 1 TO (length(stringToCheck)+1) DIV 2 DO BEGIN BlockMoveData (@stringToCheck [i*2], @coppiaDiByte, 2); checksum := checksum + coppiaDiByte END; IF Copy (password, 1, 2) = kVideoComSignature THEN verifica := (checksum = numeroDesiderato) ELSE BEGIN checksum := Abs (BitXor (checksum * kEnormeNumeroPrimo, $A5A5A5A5)); verifica := (checksum = numeroDesiderato) END;END;{$S LowLevel}PROCEDURE PresentaRichiestaRegistrazione;CONST rRegDialog = 200; rPassword = 6; rUserName = 4; kMaxLenForLongintInDecimal = 10;VAR ev: EventRecord; theDialog: DialogPtr; i: Integer; empty, buttons, textOnly, limitLen: family; PROCEDURE EnableRegistrationButtonMaybe; { Richiede: nulla } VAR pwd, nam: Str255; { buffer } BEGIN { Lettura dei nomi battuto nel dialogo } GetItemText (theDialog, rPassword, pwd); GetItemText (theDialog, rUserName, nam); IF (length (pwd) < 11) OR (length (nam) < 4) THEN DisableDialogItem (theDialog, kStdOkItemIndex) ELSE EnableDialogItem (theDialog, kStdOkItemIndex); END;BEGIN InitCursor; theDialog := GetNewDialog(rRegDialog,NIL,WindowPtr(-1)); IF theDialog = NIL THEN DeathAlert (errMissingApplRes, ResError); SetPort (theDialog); DefaultButton (theDialog, TRUE); ClearFamily (empty); ClearFamily (textOnly); ClearFamily (limitLen); textOnly[rUserName] := TRUE; limitLen[rPassword] := TRUE; buttons := textOnly; buttons[kStdOkItemIndex] := TRUE; buttons[kStdCancelItemIndex] := TRUE; buttons[rPassword] := TRUE; { Cos“ verifico la lunghezza } SetItemText (theDialog, rUserName, userName); REPEAT EnableRegistrationButtonMaybe; i := DialogLord (theDialog, rPassword, empty, empty, buttons, empty, empty, textOnly, limitLen, kMaxLenForLongintInDecimal+2, ev); UNTIL i <= kStdCancelItemIndex; GetItemText (theDialog, rPassword, password); GetItemText (theDialog, rUserName, userName); DisposeDialog (theDialog);END;{------------------------------------------------------}{ Codice di basso livello per la gestione del cursore }{------------------------------------------------------}const rFirstCursor = 989; rLastCursor = 996;var animatedCursor: integer; allCursors: ARRAY [rFirstCursor..rLastCursor] OF CCrsrHandle; theTick: longint;{$S GraphEngine}Procedure CursorInit;VAR i: Integer;begin theTick := TickCount; animatedCursor := rFirstCursor; { If not yet loaded, do load } IF allCursors[animatedCursor] = NIL THEN FOR i := rFirstCursor TO rLastCursor DO allCursors[i] := GetCCursor (i); { OK now? } IF allCursors[animatedCursor] = NIL THEN DeathAlert (errMissingApplRes, ResError) ELSE SetCCursor (allCursors[rFirstCursor])end;{$S GraphEngine}Procedure CursorAnimate;var now: longint;begin now := TickCount; if theTick + 20 < now then begin if animatedCursor = rLastCursor then animatedCursor := rFirstCursor else animatedCursor := animatedCursor + 1; SetCCursor (allCursors[animatedCursor]); theTick := now endend;{------------------ Program user interface ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ}{$S LowLevel}PROCEDURE DepthAlert;VAR i: Integer; okButton: Family; FUNCTION Depth2NumColors: Longint; { Trasforma una profonditˆ, salvata nella globale suggestedScreenDepth, nel numero di colori corrispondente. Per esempio, depth 8 = 256 colori } VAR i: Integer; result: Longint; BEGIN result := 2; FOR i := 2 TO suggestedScreenDepth DO result := result * 2; Depth2NumColors := result END;BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; ParamText (nomeDesigner, IToS (Depth2NumColors), '', ''); i := AlertLord (rScreenDepthAlert, 1, okButton)END;{$S LowLevel}PROCEDURE BarmanTalks (hisPhrase: INTEGER);VAR frase: Str255; i: Integer; okButton: Family;BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; GetIndString(frase,rBarmanStrings,hisPhrase); ParamText (frase, '', '', ''); i := AlertLord (rBarmanAlert, 1, okButton)END;{$S LowLevel}PROCEDURE SellAlert (id: INTEGER);{ Finestra di dialogo che avvisa di un problema nell'acquisto o vendita }VAR s: Str255; i: INTEGER; okButton: Family;BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; DoSoundAsync (sndAttention); GetIndString(s,rAlertMessages,id); ParamText (s, '', '', ''); i := AlertLord (rBuyNSellAlert, 1, okButton)END;{$S LowLevel}PROCEDURE GenericDreamAlert (id: INTEGER);{ Finestra di dialogo che avvisa di un problema }VAR s: Str255; i: INTEGER; okButton: Family;BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; DoSoundAsync (sndAttention); GetIndString(s,rAlertMessages,id); IF gNotificationIsOn THEN BEGIN ParamText (s, '', '', ''); i := AlertLord (rDreamAlert, 1, okButton) END ELSE AddToTranscript (s, 0, '', 0);END;{$S LowLevel}PROCEDURE AddToTranscript (s1: String; resStr1: Integer; s2: String; resStr2: Integer);VAR rs1, rs2, buffer: Str255;BEGIN IF gTranscriptIsOn THEN BEGIN { Get strings from the resources } IF resStr1 > 0 THEN GetIndString(rs1,rTranscriptMessages,resStr1) ELSE rs1 := ''; IF resStr2 > 0 THEN GetIndString(rs2,rTranscriptMessages,resStr2) ELSE rs2 := ''; { Add them all to the transcript text } buffer := Concat (chr(13), s1, rs1, s2, rs2); TEAppendTextRun (transcriptWindow, buffer); ENDEND;{$S LowLevel}FUNCTION GiveBackWindowPositionOnScreen (w:WindowPtr): Point;VAR winPos: Point;BEGIN SetPort (w); Longint(winPos) := 0; LocalToGlobal (winPos); GiveBackWindowPositionOnScreen := winPosEND;{$S LowLevel}FUNCTION GetIndNrect (nrectID, serial: Integer): Rect;{ New for v.2,0, grabbed and adapted from Dream II v2.0d22 }CONST resNrect = 'nrct';VAR result: Rect; i: Integer; myNrects: Handle; myScanner: Ptr;BEGIN { Load nrect } myNrects := MyGetResource (resNrect, nrectID, TRUE, FALSE); HLock (myNRects); myScanner := myNRects^; { La posizione del primo, secondo, terzo... ennesimo nrect 2, 10, 18... 2+(n-1)*8 } i := GetIntegerFromRes (myScanner); { Leggi il rect } WITH result DO FOR i := 0 TO serial DO BEGIN Longint (topLeft) := GetLongintFromRes (myScanner); Longint (botRight) := GetLongintFromRes (myScanner); END; HUnLock (myNRects); OffsetRect (result, -2, kCWTopOfFreeSpace-1); { Offset for space on top } GetIndNrect := resultEND;{$S LowLevel}Procedure MyMoveWindow (w: WindowPtr; pos: Point; update: Boolean);VAR theDesktop: RgnHandle;BEGIN theDesktop := GetGrayRgn; { For sanity checks } IF (Longint (pos) <> 0) & PtInRect (pos, theDesktop^^.rgnBBox) & PtInRgn (pos, theDesktop) THEN MoveWindow(w, pos.h, pos.v, update);END;{$S LowLevel}FUNCTION MyFindControl (controlRefCon: Integer): ControlHandle;VAR result: ControlHandle; w: GrafPtr;BEGIN GetPort (w); result := WindowPeek(w)^.controlList; WHILE GetControlReference(result) <> controlRefCon DO result := result^^.nextControl; MyFindControl := resultEND; {$S LowLevel}PROCEDURE ResizeMainWindow (noChunkyMode: Boolean);VAR r: Rect; requestedWidth, requestedHeight, i, j: Integer;BEGIN requestedWidth := mainWindow^.portRect.right; requestedHeight := mainWindow^.portRect.bottom; IF noChunkyMode THEN BEGIN kMWHeight := requestedHeight; kMWWidth := requestedWidth END ELSE BEGIN { Arrotonda ai 32 pixel pi vicini } kMWHeight := ((requestedHeight-(kStatusLineHeight+3)+16) DIV 32) * 32 +(kStatusLineHeight+3); kMWWidth := ((requestedWidth-kMWLeft+16) DIV 32) * 32 + kMWLeft; { Ridimensiona la finestra } IF (requestedWidth <> kMWWidth) OR (requestedHeight <> kMWHeight) THEN TMSizeWindow(mainWindow, kMWWidth, kMWHeight, FALSE); END; { Ricalcola quante icone si vedono adesso } WITH amtTilesInMap DO BEGIN { Ricalcola quante icone si vedono adesso } r.right := (kMWWidth-kMWLeft) DIV 32; r.bottom := (kMWHeight-(kStatusLineHeight+3)) DIV 32; { Dividile equamente tra destra e sinistra, sopra e sotto } right := r.right DIV 2; left := r.right - right - 1; top := r.bottom DIV 2; bottom := r.bottom - top - 1 END; { with } { Ricalcola i rect che definiscono la main window, IN COORDINATE LOCALI (bug fix 2.2) } SetRect (watchRect, kMWWidth-18, 0, kMWWidth-2, 16); SetRect (paneRect, kMWLeft,kMWTop,kMWWidth,kMWHeight-kStatusLineHeight-3); SetRect (timePlace, 0, 0, kMWWidth, kStatusLineHeight); TMInvalRect (paneRect); SetRect (groupRect, 0, kMWTop, kMWLeft-3, kMWHeight-kStatusLineHeight-3); FOR i := -1 TO 1 DO FOR j := -1 TO 1 DO SetRect (vicinityRect[i,j], kMWLeft+(amtTilesInMap.left+i)*32, kMWTop+(amtTilesInMap.top+j)*32, kMWLeft+(amtTilesInMap.left+1+i)*32, kMWTop+(amtTilesInMap.top+1+j)*32);END;{$S LowLevel}procedure DoCheckItem (myMenu, myItem: integer; check: boolean);var mh: MenuHandle;begin mh := GetMenuHandle (myMenu); if mh = nil THEN DeathAlert (errMissingApplRes, resNotFound); CheckItem (mh, myItem, check)end;{ ============================= Uso delle risorse =============================== }{$S LowLevel}Function MyGetResource (resKind: ResType; ref: INTEGER; isFatal: boolean; isScenario: boolean): Handle;LABEL 999;VAR myHandle: handle; itsSize, myMem, myBlock: Longint; myErr: INTEGER; s: String[4];BEGIN { Is there memory enough to load this resource? } SetResLoad (FALSE); { Load essential data only from disk } myHandle := GetResource (resKind, ref); { Did we find anything? } myErr := ResError; { Tell resource manager that we are finished doodling } SetResLoad (TRUE); { Check error conditions } IF (myHandle = NIL) & isFatal THEN BEGIN { errMissingApplRes viene subito prima di errMissingScenRes } BlockMove (@resKind, @s[1], 4); s[0] := chr(4); ErrorMessage (s, ref); DeathAlert (errMissingApplRes + ORD (isScenario), 0); END; itsSize := GetResourceSizeOnDisk (myHandle); myMem := FreeMem; myBlock := MaxBlock; IF (myErr <> noErr) THEN DeathAlert (errScenarioDamaged, myErr); IF ((itsSize > myMem - MAXINT) | (itsSize > myBlock)) & (MaxMem (myMem) < itsSize) THEN BEGIN ReleaseResource (myHandle); myHandle := NIL; IF isFatal THEN DeathAlert (errOutOfMemory, 0) ELSE BEGIN NewErrorAlert (kAlertStopAlert, errOutOfMemory, 0); Goto 999 END; END; { Everything is OK. Do load the data, lock them, then exit } LoadResource (myHandle); IF resKind = ResPlace THEN MoveHHi (myHandle); { Places are big and kept around awhile, soÉÊ} HLock (myHandle); HPurge (myHandle); { Questo dovrebbe essere inutile, ma se l'autore dello scenario ha dimenticato di rendere purgabili le risorse vien comodo }999:MyGetResource := myHandleEND;{------------- Synth sound routines --------------------}(* Tecnica studiata dopo attenta lettura degli articoli *)(* in Comp.Sys.Mac.Prog sul sound manager *){$S LowLevel}PROCEDURE SoundUnavailable;{ New for v1.3§2 }BEGIN gSoundIsOn := FALSE; mySoundHandle := NIL; myChan := NIL; SetItemMark (GetMenuHandle (kGameMenu), kSound, char (noMark)); DisableItem (GetMenuHandle (kGameMenu), kSound);END;{$S LowLevel}PROCEDURE SetupSoundChannel;VAR mySoundErr: OSErr;BEGIN mySoundHandle := NIL; IF myChan = NIL THEN mySoundErr := SndNewChannel (myChan, sampledSynth, initMono, nil) ELSE mySoundErr := 0; IF (myChan = NIL) OR (mySoundErr <> noErr) THEN BEGIN SoundUnavailable; NewErrorAlert (kAlertNoteAlert, errCantDoSound, mySoundErr) ENDEND;{$S LowLevel}PROCEDURE KillSoundChannel;VAR err: OSErr;BEGIN IF myChan <> NIL THEN BEGIN (* err := SndChannelStatus (myChan, sizeof (SCStatus), @chanStatus); IF chanStatus.scChannelBusy THEN BEGIN { Bug fix 1.6.1 } { stop the sound } WITH statteZitto DO BEGIN cmd := quietCmd; param1 := 0; param2 := 0 END; err := SndDoImmediate (myChan, statteZitto); END; err := SndDisposeChannel (myChan, false); *) err := SndDisposeChannel (myChan, TRUE); IF err = noErr THEN myChan := NIL END; IF mySoundHandle <> NIL THEN BEGIN HUnlock (mySoundHandle); HPurge (mySoundHandle); mySoundHandle := NIL END;END;{$S LowLevel}PROCEDURE DoSpeechAsync (textRef: INTEGER);BEGIN IF gSpeechIsOn THEN BEGIN SelectLanguageAndGender (languageCode, textRef MOD 10); GalateaSpeak (textRef) END;END;{$S LowLevel}PROCEDURE DoSoundAsync (soundRef: INTEGER);VAR mySoundErr: OSErr; chanStatus: SCStatus; finalTicks: Longint;BEGIN { Wait for previous sound to complete } IF gSoundIsOn & (mySoundHandle <> NIL) & (myChan <> NIL) THEN BEGIN REPEAT Delay (3, finalTicks); { Tentativo per evitare la morte del sonoro, 1.3§3 } mySoundErr := SndChannelStatus (myChan, sizeof (SCStatus), @chanStatus); UNTIL (mySoundErr = noErr) AND NOT chanStatus.scChannelBusy; { Bug fix 1.6.1 } IF mySoundErr <> noErr THEN BEGIN NewErrorAlert (kAlertNoteAlert, errCantDoSound, mySoundErr); SoundUnavailable; Exit (DoSoundAsync) END; { I won't dispose the finished sound, but let it around in the heap, as a purgeable resource. Should I need it again in a whileÉ } HPurge (mySoundHandle); mySoundHandle := NIL END; IF gSoundIsOn & (myChan <> NIL) THEN BEGIN mySoundHandle := MyGetResource (soundListRsrc, soundRef, FALSE, FALSE); IF mySoundHandle <> NIL { data is here } THEN BEGIN { No need to keep it locked, but don't purge it. } HNoPurge (mySoundHandle); HUnlock (mySoundHandle); { qui sotto, Bug fix 1.6 per Univ Interfaces 2.0a3 } mySoundErr := SndPlay (myChan, SndListHandle(mySoundHandle), true); END; END { if sound is on }END; { Procedure }{------------- Synth sound routines end --------------------}{$S LowLevel}Function GetLongintFromRes (VAR scanner: Ptr): Longint;TYPE LongintPtr = ^Longint;BEGIN GetLongintFromRes := LongintPtr (scanner)^; scanner := Ptr (ord4 (StripAddress(scanner)) + sizeof (Longint))END;{$S LowLevel}Function GetIntegerFromRes (VAR scanner: Ptr): integer;BEGIN GetIntegerFromRes := IntegerPtr (scanner)^; scanner := Ptr (ord4 (StripAddress(scanner)) + sizeof (integer))END;{$S LowLevel}Function GetByteFromRes (VAR scanner: Ptr): byte;VAR result: Byte;BEGIN {$R-} result := scanner^; IF result >= 0 THEN GetByteFromRes := result ELSE GetByteFromRes := 256 + result; scanner := Ptr (ord4 (StripAddress(scanner)) + 1) { sizeof (Byte) RESTITUISCE 2!!! } {$R+}END;{$S LowLevel}Function GetStringFromRes (VAR scanner: Ptr): String;VAR temp: String; length: Integer;BEGIN length := GetByteFromRes(scanner); temp[0] := chr (length); BlockMove (scanner, @temp[1], length); scanner := Ptr (ORD4 (StripAddress(scanner)) + length); GetStringFromRes := tempEND;{$S LowLevel}Function GetWBFromRes (VAR scanner: Ptr): BitsInWord;TYPE WBPtr = ^BitsInWord;BEGIN GetWBFromRes := WBPtr (scanner)^; scanner := Ptr (ord4 (StripAddress(scanner)) + sizeof (BitsInWord))END;{$S LowLevel}Function GetBBFromRes (VAR scanner: Ptr): BitsInByte;TYPE BBPtr = ^BitsInByte;BEGIN GetBBFromRes := BBPtr (scanner)^; scanner := Ptr (ord4 (StripAddress(scanner)) + sizeof (BitsInByte))END;{$S LowLevel}FUNCTION OldKindOfAttackToKindOfAttackv21 (old: Integer): KindOfAttack;BEGIN old := BAnd (old, $003F); OldKindOfAttackToKindOfAttackv21 := KindOfAttack ( (3 - old DIV 8) * 8 + old MOD 8 )END;{-------------------------------------------------------}{ Codice di basso livello per la gestione dei errori }{-------------------------------------------------------}{$S LowLevel}Function CommandPeriod: boolean;const kMaskModifier = $FE00; kMaskVirtualKey = $0000FF00; kMaskASCII1 = $00FF0000; kMaskASCII2 = $000000FF; kKeyUpMask = $0080;var keyCode: integer; virtualKey, keyInfo, lowChar, highChar, state, keyCId: longint; hKCHR: handle; e: EventRecord; result: boolean;begin result := false; if EventAvail (keyUpMask+keyDownMask+autoKeyMask, e) then begin if BAnd (e.modifiers, cmdKey) <> 0 then begin virtualKey := Band (e.message, kMaskVirtualKey) DIV 256; keyCode := BAnd (e.modifiers, kMaskModifier); keyCode := BOr (keyCode, kKeyUpMask); keyCode := BOr (keyCode, virtualKey); state := 0; keyCId := GetScriptVariable (GetScriptManagerVariable (smKeyScript), smScriptKeys); hKCHR := GetResource ('KCHR', keyCId); if hKCHR <> NIL then begin keyInfo := KeyTranslate (hKCHR^, keyCode, state); { Tech note 1 says not to release system resources ReleaseResource (hKCHR) } end else keyInfo := e.message; lowChar := BAnd (keyInfo, kMaskASCII2); highChar := BSR (BAnd (keyInfo, kMaskASCII1), 16); if (lowChar = ord ('.')) or (highChar = ord ('.')) then result := true end end; CommandPeriod := resultend;{$S LowLevel}Procedure FailNil (o: UNIV handle);BEGIN IF (o = NIL) | (o^ = NIL) THEN DeathAlert (errOutOfMemory, 999)END;(******* Gestione dei windoid con testo e grafica *********){$S LowLevel}PROCEDURE TextOut (textID: Integer; foreground: Boolean);BEGIN { sanity check } IF (textID = 0) THEN Exit (TextOut); KillText; TMLoadTextFromRes (TEXTWindow, textID); { Mostra la finestra } ShowWindow (TEXTWindow); IF foreground THEN TMSelectWindow (TEXTWindow)END;{$S LowLevel}PROCEDURE KillTEXT;BEGIN { Nascondi la finestra } HideWindow (TEXTwindow);END;{$S DefProcs}PROCEDURE DrawPICTWindow (myWin: WindowPtr);VAR r: Rect;BEGIN{$UNUSED myWin} r := PICTinTheWindow^^.picFrame; DrawPicture(PICTinTheWindow, r);END;{$S LowLevel}PROCEDURE PictOut (pictID: Integer);CONST resPicture = 'PICT';VAR myClip: RgnHandle;BEGIN { sanity check } IF (pictID = 0) | (PICTinTheWindow <> NIL) THEN Exit (PictOut); SetPort (PICTWindow); { Leggi la picture dalle risorse } PICTinTheWindow := GetPicture(pictID); IF PICTinTheWindow = NIL THEN DeathAlert (errMissingScenRes, resError); { Ridimensiona la finestra di modo che appaia correttamente il pict } HLock (Handle(PICTinTheWindow)); WITH PICTinTheWindow^^.picFrame DO BEGIN TMSizeWindow(PICTWindow, right-left, bottom-top, TRUE); { Se ho ridimensionato ingrandendo, va ridefinita la clipRgn } myClip := NewRgn; CloseRgn (myClip); SetRectRgn(myClip,left,top,right,bottom); SetClip (myClip); DisposeRgn (myClip) END; HUnlock (Handle(PICTinTheWindow)); { Mostra la finestra } ShowWindow (PICTWindow); TMSelectWindow (PICTWindow)END;{$S LowLevel}PROCEDURE KillPICT;BEGIN IF PICTinTheWindow <> NIL THEN BEGIN { Nascondi la finestra } HideWindow (PICTwindow); { Liberati della picture } ReleaseResource (Handle(PICTinTheWindow)); { Housekeeping } PICTinTheWindow := NIL; END;END;(******* FINE Gestione dei windoid con testo e grafica *********){$S LowLevel}PROCEDURE StatusLine (what: string);VAR p: GrafPtr;BEGIN IF Length (what) = 0 THEN { Reset al display di data ed ora, valido indefinitamente } tickleTime := $7FFFFFFF ELSE { Messaggio nella display line diverso da quello di default } { Fai partire il countdown } tickleTime := TickCount; message := what; GetPort (p); SetPort (mainWindow); InvalRect (timePlace); SetPort (p)END;{--------------------------Low Level Initialization ----------------------}{$S LowLevel}PROCEDURE LowLevelRestart;{ La parte di LowLevelInit che va richiamata quando si vuole caricare un altro gioco }BEGIN { Init global variables } message := ''; currentPlaceHandle := NIL; placeMap := NIL; locationStackPointer := 0; artificialLight := 0;END;{$S UtilInit}Procedure LowLevelInit;CONST rMenuBar = 128; rSpeechWelcome = 129; { TEXT resource with welcome } kScrollBarThickness = 15;VAR i, j: integer; smallFont, gestResponse: Longint; err: OSErr;BEGIN { Initialize all the needed managers. } StandardInitialization (20); { Test the computer to be sure we can do color. If not we would crash. Test also for System 7, and other needed features. } if gSystemVersion < $700 then DeathAlert (errNeedSys7, 0); err := Gestalt (gestaltQuickTimeVersion, gestResponse); { New for v2.2 } IF (err <> noErr) | (gestResponse < $02000000) THEN DeathAlert (errNeedSys7, 0); if gQDVersion = kQDOriginal THEN DeathAlert (errNeedColor, 0); InitPalettes; StandardMenuSetup (rMenuBar, kAppleMenu); { La prima volta che viene chiamato CursorInit carica in memoria i cursori a colori. Metto la handle al primo a NIL per consentirgli di riconoscere che questa la prima volta } allCursors[rFirstCursor] := NIL; CursorInit; { Get me a sound channel, buddy } myChan := NIL; SetupSoundChannel; CursorAnimate; { Can we do speech? } IF SpeechAvailable THEN BEGIN GalateaStartup; SelectGender (2); { Female } GalateaSpeak (rSpeechWelcome); gSpeechIsOn := TRUE END ELSE BEGIN { Galatea indisponibile. Ingrigisci la voce di menu relativa } gSpeechIsOn := FALSE; SetItemMark (GetMenuHandle (kGameMenu), kSpeech, char (noMark)); DisableItem (GetMenuHandle (kGameMenu), kSpeech); END; CursorAnimate; { Start the music engine } gMusicIsOn := QTMusicInit; gQuickTimeIsOn := gMusicIsOn; IF NOT gMusicIsOn THEN BEGIN { QT 2.0 unavailable. } SetItemMark (GetMenuHandle (kGameMenu), kMusic, char (noMark)); DisableItem (GetMenuHandle (kGameMenu), kMusic); { New for v 2.1 } SetItemMark (GetMenuHandle (kGameMenu), kQuickTime, char (noMark)); DisableItem (GetMenuHandle (kGameMenu), kQuickTime); END; { Init global variables } gSoundIsOn := true; gTranscriptIsOn := true; gNotificationIsOn := true; { Location stack and scenario ref init (see DoLoadPlace) } currentScenarioFile.resFork := 0; { Queste erano costanti nella versione 1.0 } kMWHeight := 275; kMWWidth := 387; { For the fonts } { Tecnica di develop, n¡ 14, pag. 14-15, per trovare i font piacevoli alla vista in qualsivoglia dimensione } smallFont := GetScriptVariable (smSystemScript, smScriptAppFondSize); stdFontID := HiWrd (smallFont); stdFontSize := LoWrd (smallFont); smallFont := GetScriptVariable (smSystemScript, smScriptSmallFondSize); smallFontID := HiWrd (smallFont); smallFontSize := LoWrd (smallFont); { } { initialize TaskMaster } { } InitTaskMaster (taskMiddlePriority); { Fa apparire, invisibili, le finestrelle per testo e grafica. Le versioni per Mac OS 8 hanno ID superiore di 1000 } PICTWindow := GetNewCWindow(rPICTWindow + ord (gHasThemes)*1000, nil, nil); if PICTWindow = nil then DeathAlert (errMissingApplRes, resNotFound); TMNewWindow (PICTWindow, fIsWindoid, kPictWindoidRefCon, { ref con } 128, 128, { data height, width } 480, 640, { Max height, width } 48, 64, { Min height, width } 0, 0, 0, 0, 0, { Info bar height } NIL, DrawPICTWindow, NIL); PICTinTheWindow := NIL; CursorAnimate; TEXTWindow := GetNewCWindow(rTEXTWindow + ord (gHasThemes)*1000, nil, nil); if TEXTWindow = nil then DeathAlert (errMissingApplRes, resNotFound); TMNewWindow (TEXTWindow, fGrow+fRScroll+fContainsTextEdit+fIsWindoid, kTextWindoidRefCon, { ref con } 350, kStandardWindoidWidth-16, { data height, data width (16 for scrollbar) } 1024, kStandardWindoidWidth-16, { Max height, width } 100, kStandardWindoidWidth-16, { Min height, width } 12, 64, { Scroll ver, hor } 0, 0, { Page ver, hor } 0, { Info bar height } NIL, NIL, NIL); CursorAnimate; { E ora la finestra di transcript } transcriptWindow := GetNewCWindow(rTranscriptWindow + ord (gHasThemes)*1000, nil, nil); if transcriptWindow = nil then DeathAlert (errMissingApplRes, resNotFound); SetPort (transcriptWindow); TextFont (smallFontID); TextSize (smallFontSize); TMNewWindow (transcriptWindow, fGrow+fRScroll+fContainsTextEdit+fIsWindoid, kTranscriptWindoidRefCon, 100, 300-16, 1024, 300-16, 64, 300-16, 12, 0, 0, 0, 0, { Info bar height } NIL, NIL, NIL); SetRect (watchRect, kMWWidth-18, 0, kMWWidth-2, 16); SetRect (timePlace, 0, 0, kMWWidth, kStatusLineHeight); SetRect (paneRect, kMWLeft,kMWTop,kMWWidth,kMWHeight); FOR i := -1 TO 1 DO FOR j := -1 TO 1 DO SetRect (vicinityRect[i,j], kMWLeft+(5+i)*32,kMWTop+(4+j)*32, kMWLeft+(6+i)*32, kMWTop+(5+j)*32); SetRect (groupRect, 0, kMWTop, kMWLeft-3, kMWHeight); CursorAnimate; { To make the Random sequences truly random. } GetDateTime (qd.RandSeed); CursorAnimate; { Verification system } userName := ''; password := ''; { Complete initalization } LowLevelRestartEND; { LowLevelInit }{$S LowLevel}PROCEDURE LowLevelShutdown;BEGIN QTMusicQuit; KillSoundChannel; GalateaShutdownEND;{====================== Miscellanea =======================}{$S LowLevel}Function Dado (numeroDadi, numeroFacce: integer): integer;VAR i, risultatoTot: integer;BEGIN risultatoTot := 0; FOR i := 1 to numeroDadi DO risultatoTot := risultatoTot + (ABS(Random) MOD numeroFacce + 1); Dado := risultatoTotEND;{$S LowLevel}Function Best3OutOf (n: integer): integer;VAR tiri: ARRAY [1..10] of integer; i, j, k, tiro: integer;BEGIN FOR i := 1 to n DO BEGIN { n tiri di dado, esegui lo i-esimo } tiro := Dado (1, 6); { Straight insertion. Find in the array the smallest nr which is bigger than this nr } j := i-1; while (j > 0) & (tiro > tiri[j]) DO j := pred (j); { Shift smaller numbers to the end of the array } FOR k := i DOWNTO j+2 DO tiri[k] := tiri[k-1]; tiri[j+1] := tiro END; Best3OutOf := tiri[1]+tiri[2]+tiri[3]END;END. { Unit }