-
Notifications
You must be signed in to change notification settings - Fork 1
/
GraphEngine.p
2516 lines (2296 loc) · 79.2 KB
/
GraphEngine.p
1
Unit GraphEngine;(*PURPOSE: gestisce il caching del mondo grafico dove si muovono i giocatori.Gestisce il caricamento dei place e degli scenari.New with v1.1: Gestisce il Dream Database, composto di incantesimi. Evita i conflitti in caso di overriding.New with v1.3 Handles NPCs and the extra empty place menu. Handles places with associated PictNew with 1.5: Handles background MIDI musicNew with 2,0: world can be NIL - inside 3D places.New with 2.1: FixGWorldAndPlotPicture gestisce il dithering da pict in truecolor verso i 256 colori standard. Uno scenario composto di pi places. Il place pi esterno ha ID 1000.Una locazione di un place pu˜ portare a un altro place pi interno (per esempio,muovendosi sull'icona di un tempio si entra nel tempio). Muovendosi oltre i confinidi un place, il giocatore ritorna al place precedente.Se un place ha mostri vaganti, deve essere definita una arena con lo stesso IDdel place, dove si svolgeranno gli incontri, e una risorsa Wndr con l'elenco deitipi di mostri che possono apparire.Ogni locazione di un place caratterizzata (oltre che da un'icona, dal legameeventuale ad un altro place, da alcuni bit di caratteristica (come "isimpassable"), anche da textToShow, il resID di una risorsa TEXT da mostrarequando il giocatore passa di l“; da encounter, il resID di un encounter (checomprende gruppi di mostri e tesori monetari e in oggetti); e da itemNeeded,il resID di un oggetto che il gruppo deve possedere per potere passare di l“.In quest'ultimo caso, se il gruppo possiede l'oggetto accade l'encounter; altrimenti,viene mostrato il testo e il gruppo viene fatto retrocedere alla posizione dipartenza.Si pone il problema di come distinguere i punti dove il giocatore pu˜ muoversiquando non ha ancora trovato alcun membro del gruppo. Per semplicitˆ, valela seguente convenzione: sono accessibili al giocatore quando non ci sonopersonaggi i place con ID inferiore a 2000.*)INTERFACEUSES Types, QuickDraw, { List 2 } Controls, Events, Files, Icons, Lists, OSUtils, QDOffscreen, SegLoad, Windows, ObjIntf, Lista3, TaskMaster3, BinIO, DreamTypes, LowLevel, HiLevel;VAR evento: wmTaskRec; { TaskMaster event. Lo tengo globale, perchŽ altrimenti non possibile distinguere i doppi click (in altri termini, il campo wmClickCount viene azzerato ogni volta, e non raggiunge mai il 2. Inoltre, serve a ClickOnCharWindow per chiamare il list manager e a SetUpShop per impedire che la finestra venga ridimensio- nata durante l'esistenza in vita di un negozio } { File con la bd - andrebbe tenuto locale, ma sinchŽ DoDesignerBid in Game.pÉ} dreamDB: MyFile; dirty: BOOLEAN; { Indica se il gioco va salvato (cio se cambiato dall'ultima volta in cui stato salvato). Va posta a TRUE dal codice esterno quando viene compiuta un'azione qualsiasi. Viene posta a FALSE da DoSaveGame } { Dati sull'NPC, caricati da TPersonaggio.InitFromRes e usati da DoLoadPlace, among others } npcData: RECORD placeForExit, talkOnExit, nctrForExit: Integer END; moveBuffer: String; { Mosse passate via AppleEvent, pi richieste formulate dal codice di basso livello al MainGameLoop } gActualDepth, { Depth of main screen } currentWorldDepth: Integer; { Depth of the offscreen 2D Gworld; -1 if in 3D world } gLast3DMapRefresh: Longint; { Istante in cui abbiamo ridisegnato per l'ultima volta la mappa }PROCEDURE EngineInit;PROCEDURE EngineShutdown;{*** Primitive grafiche ***}Procedure BarInit (whereBar: rect; max: longint);Procedure DrawProgressBar (doneData: longint; doneColor, undoneColor: PixPatHandle);{ Per una barra di progresso che aumenta al procedere di un compito, o peruna barra che mostra una percentuale }PROCEDURE DrawGrayRect (r: Rect);{ Da usare quando lo sfondo grigio chiaro. Disegna i bordi di un rettangolo"tridimensionale" }FUNCTION DragRect (p: Point; r: Rect): Point;{ Dato il punto dove l'utente ha cliccato, e un rettangolo che contiene il punto,questa procedura mostra l'outline del rettangolo che segue il mouse, simile aquella che si vede trascinando una finestra). Permette all'utente di trascianrloaltrove. Restituisce il punto in cui l'utente ha rilasciato il mouse.Tutte le coordinate sono localiSi suppone che PtInRect (p, r) valga true.}FUNCTION DragCicn (p: Point; c: Integer; r: Rect): Point;{ Identica alla precedente, per sole cicn }FUNCTION HasColor (bitsPerPixel: Byte): boolean;{ Indaga per scoprire se uno degli schermi installati supporta il colore a unaprofonditˆ fissata. Passare il numero di bit per pixel richiesto, la funzioneritornerˆ TRUE se esiste almeno uno schermo con almeno quella profonditˆ, FALSEaltrimenti.GOTCHAS: ritorna sempre FALSE se si passa 1 in bitsPerPixel (il che sta bene al chiamante, perchŽ piuttosto scema come richiesta) }{*** Caching della grafica in offscreen gworld ***}PROCEDURE CallMeAtResume;PROCEDURE CallMeAtSuspend;{ Controllano che la profonditˆ dell'offscreen GWorld corrispondaalla profonditˆ dello schermo }PROCEDURE ScrollWorld;{ Da chiamare quando il gruppo si mosso }PROCEDURE FlushGraphCache;{ Da chiamare quando il gruppo entra in un nuovo place }PROCEDURE AddToCache (x, y, locationID: Integer);{ La locazione di coordinate x, y, e i cui dati sono in placeMap^[locationID], rappresentata da un rettangolo nero nella cache. Ora i giocatori l'hannoesplorata: falla apparire. }PROCEDURE ShowAllPlace;{ Rende visibile tutto il place corrente }PROCEDURE FixGWorldAndPlotPicture (VAR g: GWorldPtr; pictID: Integer; f: WindowPtr);{ Va caricata la picture p nel gworld g, che sarˆ poi associato alla finestra f. Preparati.Se pictID 0, assegna la palette standard. Se pictID 0 ma esiste una CLUT di numero pictID, usa quella. Se non esiste una CLUT precalcolata, creane dinamicamente una ottimale.}PROCEDURE DoDrawMap;PROCEDURE DoDrawShop;{ defProcs che disegnano il main pane della finestra principale }{*** Interfaccia utente ***}PROCEDURE CustomAbout (thePict: INTEGER);{*** Gestione delle risorse specifica del gioco ***}PROCEDURE ChooseAndStartTheMusic;{ Fa partire una colonna sonora. La proceduraidentificherˆ una musica adeguata al luogo in cui mi trovo. }FUNCTION DoLoadScenario (whichScenario: FSSpecPtr): IoResult;{ If whichScenario is NIL, asks the user for a scenario to "load".Otherwise "loads" the scenario file passed as a parameter.(In practice, a scenario is just a resFile which we add to our resource files chain.Called when the user selects "open scenario" from a menu, or by DoLoadPlace, or whenloading a saved game.Returns TRUE if scenario was located and loaded, FALSE otherwise }PROCEDURE ThisPlaceIsBeingShutdown;{ Procedura interna a DoLoadPlace. Viene esteriorizzata in modo da consentirne la chiamata quandol'utnte sceglie Open dal menu File senza aver terminato lo scenario. }PROCEDURE DoLoadPlace (placeRef: integer; direction: char);{ Loads a place from the scenario. Direction indica la direzione dalla qualeprovengo, usando la stessa logica che regola il MainEventLoop, e cio sevale '8' indica che sto muovendo da sud a nord, '2' viceversa,eccetera.Se in direction viene passato '5', e placeRef vale -1, DoLoadPlace fa uscire ilgiocatore dalla stessa direzione da cui entrato (se era entrato con un '6'lo fa uscire con un '4' eccetera). ALGORITMI, SCELTE E COSí VIA.DoLoadPlace deve gestire tutta una serie di casi complessi. Posti dentro posti, en-trata in posti, uscita da posti, passaggi ad altri scenari, e "popback".Il "popback" accade quando io entro in un posto speciale, come il non-posto dovevengo mandato per acciuffare i tesori dopo un incontro, oppure un negozio delquale esiste una singola descrizione ma che viene riutilizzato in pi puntidello scenario. In questi casi, la risorsa che descrive il posto non pu˜ dirmidove debbo spedire il giocatore in uscita, perchŽ questo dipende da dove provenga.¥ SCELTA IMPLEMENTATIVA: un popback, o ritorno al posto di provenienza, vieneottenuto passando -1 come placeref a DoLoadPlace. Altri valori possibili di placeRefsono numeri positivi, per indicare lo ID della risorsa 'Plac' da caricare. Il numerozero non va mai usato: viene usato nelle risorse 'Plac' per indicare le locazioniche non sono collegate ad alcun altro posto.¥ CONVENZIONE: Il primo posto dove si viene mandati in uno scenario il postoID = 1000. (Come sempre negli scenari di Dream, possono esserci altri posti, madebbono avere ID superiore al 1000. La direzione dalla quale si proviene non fissa(posso giungere a un nuovo scenario cavalcando da sud o nuotando da est).¥ CONVENZIONE: Se si entra in uno scenario aprendolo (e non aprendo un savegamefile o entrandovi proveniendo da uno scenario precedente), DoLoadPlace caricala locazione ID 1000 e mette il simbolo del gruppo alla posizione 2, 2.¥ SCELTA IMPLEMENTATIVA: Quando entro in un posto, Dream mi mette a una icona didistanza dal bordo del posto (come in Ultima V). Andare sulla singola icona piverso l'esterno significa che voglio uscire dal posto. Il costruttore di scenaridovrˆ fare in modo che quelle locazioni abbiano un indicatore goto diverso da zero.ALGORITMO BASE: DoLoadPlace tiene uno stack di posti visitati. Quando entro in unposto, questo viene pushed su stack. Quando ne esco (con popback), DoLoadPlacefa un pop dallo stack e mi rispedisce nel place da cui provengo.Se lo stack vuoto e si tenta un popback (cio se si esce dalla pi esternadelle locazioni esistenti), DoLoadPlace chiude lo scenario attuale,permette all'utente di sceglierne un altro, carica la risorsa 'Plac' con ID 1000da quello scenario, e posiziona il gruppo l“.}IMPLEMENTATIONUses Appearance, Drag, Errors ,FixMath ,Fonts, GestaltEqu ,Icons { Icon manager del System 7 - vedi technote "icons in 7" } ,Memory ,Menus ,MixedMode ,PictUtils ,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 } ,Processes, MoreFilesExtras, MusicEngine, Galatea, Cilindro, DialogLord4, Dream3Display_Tipi, TalkEngine, Engine3D, DreamIO, Characters;CONST kGWorldSide = 1024; { Dim in pixel del lato del GWorld (quadrato) }VAR placeRect, { Rettangolo di coordinate 1, 1, placeWidth, placeHeight } worldRect: rect; { Parte del place corrente che si trova cached nell'offscreen GWorld. Calcolato da ScrollWorld } startX, startY: Integer; { Coordinates where group appears at new scenario load } minDreamVersion: Integer; { Versione minima di Dream. Usata da DoLoadScenario e DoLoadPlace } rGroupIcon: Integer; { ID dell'icona usata per rappresentare il gruppo } world: GWorldPtr; { Cache grafica }{------------- Dream DB Handling ------------------}Procedure SpellDBStartup; EXTERNAL; { Defined inside DreamMagic }Procedure SpellDBInit; EXTERNAL; { Defined inside DreamMagic }Procedure SpellDBShutdown; EXTERNAL; { Defined inside DreamMagic }{$S UtilInit}PROCEDURE InitDreamDB;VAR dbName: Str255; myFileList: SFTypeList; myReply: StandardFileReply; p: point; PROCEDURE DeathBecauseUnavailable; VAR justOK: Family; dummy: Integer; BEGIN ClearFamily (justOK); justOK[kStdOkItemIndex] := TRUE; ParamText (dbName, '', '', ''); dummy := AlertLord (rAlertWriteProtected, 1, justOK); gQuit := TRUE; ExitToShell END;BEGIN { Find name of DB file } dbName := GetString(rNameOfDBFile)^^; { Open it } ResetR (dreamDB, dbName); CASE dreamDB.errore OF noErr :; fnfErr: BEGIN { cercalo! } ParamText (dbName, '', '', ''); myFileList[0] := fileTypeDB; p.v := -1; p.h := -1; CustomGetFile (nil, 1, @myFileList, myReply, customGetFileDITL, p, NIL, NIL, NIL, NIL, NIL); IF myReply.sfGood THEN BEGIN ResetRByFSS (dreamDB, myReply.sfFile); IF dreamDB.errore <> noErr THEN DeathBecauseUnavailable END ELSE DeathAlert (errCantLoadDB, 0) END; wPrErr, fLckdErr, vLckdErr, fBsyErr, afpLockErr, afpAccessDenied: DeathBecauseUnavailable; OTHERWISE DeathAlert (errCantLoadDB, dreamDB.errore) END; { case } { Check that it is unlocked - new for v2.1 } IF (FSpCheckObjectLock (dreamDB.FSS) <> noErr) OR (CheckVolLock (NIL, dreamDB.FSS.vrefNum) <> noErr) THEN DeathBecauseUnavailable; SpellDBInitEND;{$S Magic}PROCEDURE ShutdownDreamDB;BEGIN SpellDBShutdown;END;{$S Magic}PROCEDURE StartupDreamDB;BEGIN SpellDBStartupEND;{$S Magic}FUNCTION ScenarioIsBrandNew: Boolean;{ Controlla se la scenarioSignature giˆ presente nella risorsa Scendel Dream database. In questo caso lo scenario giˆ stato giocato, e nonbisogna fare nulla di nuovo (restituisci FALSE).Altrimenti bisognerˆ copiare alcune cose nel Dream Database e controllareche non ci siano collisioni di ID (restituisci TRUE) }VAR scenHandle: Handle; scenScanner: Ptr; result: Boolean; oneSignature: OSType;BEGIN { Load scenario list resource in memory } scenHandle := MyGetResource (resScenList, rScenListID, TRUE, FALSE); HLock (scenHandle); scenScanner := scenHandle^; { Init result variable } result := TRUE; { Loop and search for the signature } WHILE result AND (scenScanner^ <> 0) DO BEGIN { Fetch one signature } oneSignature := OSType (GetLongintFromRes (scenScanner)); { If identical to current signature, then scenario was already opened some time ago } result := (oneSignature <> scenarioSignature) END; { Free memory } ReleaseResource (scenHandle); { Return } ScenarioIsBrandNew := resultEND;FUNCTION DoScenarioDesignerBiddings: Boolean;EXTERNAL;{ Sta in Game.p, per ora }{------------------ Program user interface ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ}{$S AboutBox}PROCEDURE CustomAbout (thePict: INTEGER);LABEL 998, 999;CONST rAboutDLOG = 129; kDlogWidth = 310;VAR { Per la shareware notice } shareTEHandle: TEHandle; shareTextHandle: Handle; { Handle alla risorsa TEXT } shareStylHandle: StScrpHandle; { Handle alla risorsa styl } shareWindow, laFinestra: WindowPtr; { Old grafport save } h :PicHandle; picR, animR, srcRect, lineR: Rect; d: DialogPtr; displace: integer; myUpdateRgn: RgnHandle; { Per scrollrect } { Per il ciclo di ritardo } i: integer; ticks: longint; oldPort: CGrafPtr; oldGdev: GDHandle; aboutWorld: GWorldPtr; BEGIN { Fa partire la musica dell'about box } IF gMusicIsOn THEN i := QTMusicPlay (138); { 138 = rAboutMusic } GetPort (laFinestra); { Recupera dal ramo risorse la finestra } d := GetNewDialog (rAboutDLOG, nil, WindowPtr (-1)); if d = nil THEN BEGIN NewErrorAlert (kAlertStopAlert, errMissingApplRes, resNotFound); exit (CustomAbout) END; { Recupera il testo della shareware notice } { 1. Troviamo lo spazio sotto l'about box dove mettere la notice. } picR := WindowPeek (d)^.strucRgn^^.rgnBBox; { trova spazio occupato da dialog } picR.top := picR.bottom; { Metti subito sotto } IF qd.screenBits.bounds.bottom > 385 THEN picR.bottom := picR.bottom + 120 ELSE picR.bottom := 384; { Min vsize for 12" monitors } IF picR.right + picR.left > 639 THEN BEGIN { Screen width is 640 or better } i := (picR.right + picR.left) DIV 2; { Mid of screen } picR.right := i + 320; picR.left := i - 320 END ELSE BEGIN picR.right := picR.right + picR.left; { Occupa tutto orizzontalmente } picR.left := 0 END; { 2. Crea la finestra per la shareware notice } shareWindow := NewWindow(NIL, picR, '', TRUE, altDBoxProc, WindowPtr(-1), FALSE, kSharewareNoticeWindow); SetPort (shareWindow); { 3. Prendi il rect in cui scrivere } picR := shareWindow^.portRect; { 4. Recupera il testo } shareTextHandle := MyGetResource ('TEXT', thePict, TRUE, thePict>999); { Leggi dalle risorse lo stile } shareStylHandle := StScrpHandle (MyGetResource ('styl', thePict, FALSE, thePict>999)); { Metti il tutto nel TextEdit record } shareTEHandle := TEStyleNew (picR, picR); HLock (shareTextHandle); TEStyleInsert (shareTextHandle^, GetHandleSize (shareTextHandle), shareStylHandle, shareTEHandle); { 5. Liberati delle risorse } IF shareStylHandle = NIL THEN BEGIN TextFont (stdFontID); TextSize (stdFontSize); END ELSE ReleaseResource (Handle (shareStylHandle)); ReleaseResource (shareTextHandle); { 6. Mostra il testo } TEUpdate (picR, shareTEHandle); TEDelete(shareTEHandle); SetPort (d); h := GetPicture (thePict); if h = nil THEN BEGIN NewErrorAlert (kAlertStopAlert, errMissingScenRes, resNotFound); exit (CustomAbout) END; { Inizializza le variabili: picR: rettangolo che ingloba l'intera picture animR: rettangolo in cui avviene lo scrolling lineR: ultima riga del rettangolo } picR := h^^.picFrame; { Check to be sure that the rect has top = 0, left = 0 } OffsetRect (picR, -picR.left, -picR.top); { Crea uno offscreen bitmap. Abbonda sulle dimensioni orizzontali, per il caso in cui la larghezza della bitmap non sia un multiplo esatto di otto. Lo spazio in memoria richiesto relativamente ingente, quindi se siamo in condizioni di ristrettezza libera un po' di spazio swappando il segmento codice pi grande. Sarˆ automaticamente ricaricato all'uscita. }{$IFC MAC68K} if MaxBlock < 65537 THEN UnloadSeg (@StatusLine);{$ENDC} (* USANDO GWORLD *) i := NewGWorld (aboutWorld, 8 { bit per pixel }, picR, NIL, NIL, 0); IF i <> noErr THEN GOTO 999; GetGWorld (oldPort, oldGdev); SetGWorld (aboutWorld, NIL); if not LockPixels (aboutWorld^.portPixMap) then goto 998; EraseRect (aboutWorld^.portRect); ClipRect (picR); DrawPicture (h, picR); IF QDError <> noErr THEN GOTO 998; { Ecco fatto. Ora torniamo al dialogo } SetGWorld (oldPort, oldGdev); { calcola quanto spazio lasciare tra il bordo della finestra e quello della PICT } displace := (kDlogWidth - (picR.right - picR.left)) DIV 2; SetRect (animR, displace, 5, kDlogWidth - displace, 205); { Visto che usiamo ScrollRect per aumentare la velocitˆ dell'animazione, prepara un secondo rettangolo, ad uso di CopyBits, che comprende solo una riga, l'ultima, che sarˆ l'unica a venire effettivamente disegnata Ð le altre sono scrolled } lineR := animR; lineR.top := lineR.bottom - 1; { Setta la clipping region al rettangolo anim, di modo che i disegni non sconfinino } ClipRect (animR); { Inizializza il rettangolo sorgente che deve ¥ Avere le stesse dimensioni di animRect (verticale 1, orizzontale come la pict) ¥ Avere left = 0 } SetRect (srcRect, 0, 0, picR.right, 1); { Prepara gli altri parametri per Scrollrect } myUpdateRgn := NewRgn; CloseRgn (myUpdateRgn); if myUpdateRgn = nil THEN GOTO 998; GetClip (myUpdateRgn); { Animazione! } ticks := TickCount + 6; { Una riga ogni sei tick = 10 righe al secondo } HideCursor; REPEAT ScrollRect (animR, 0, -1, myUpdateRgn); IF srcRect.bottom <= picR.bottom THEN CopyBits(bitmapptr(aboutWorld^.portPixMap^)^, qd.thePort^.portBits, srcRect, lineR, srcCopy, nil); { else leave the white line created by ScrollRect } OffsetRect (srcRect, 0, 1); { Rallentiamolo sulle macchine pi veloci } WHILE ticks > TickCount DO QTMusicIdle; { Wait more } ticks := TickCount + 5; { Fine ciclo di ritardo } IF srcRect.bottom = picR.bottom + 200 THEN SetRect (srcRect, 0, 0, picR.right, 1); UNTIL Button; { Liberiamoci dell'offscreen bitmap e del port extra } DisposeRgn (myUpdateRgn);998: UnlockPixels (aboutWorld^.portPixMap); DisposeGWorld (aboutWorld);999: ReleaseResource (Handle (h)); { liberiamoci del dialogo } SetPort (laFinestra); DisposeDialog (d); { Liberiamoci della shareware notice } DisposeWindow (shareWindow); { Fa riprendere la musica } ChooseAndStartTheMusicEND;{ Da IM-Text }{$S UtilInit}FUNCTION GetFontNumber(fontName: Str255; VAR fontNum: Integer): Boolean; {MyGetFontNumber returns in the fontNum parameter the number for } { the font with the given font name. If thereÕs no such font, } { it returns FALSE.}VAR systemFontName: Str255;BEGIN GetFNum(fontName, fontNum); IF fontNum = 0 THEN BEGIN {either the font was not found, or it is the system font} GetFontName(0, systemFontName); GetFontNumber := EqualString(fontName, systemFontName, FALSE, FALSE); END{ if theNum was not 0, the font is available } ELSE GetFontNumber := TRUE;END;{$S UtilInit}FUNCTION FindMostDesirableFont (listOfChoice: INTEGER): INTEGER;{ Data una lista di font, in ordine dalla preferita alla meno adatta (che deve essereChicago), salvata in una risorsa STR#, la funzione trova quale tra i font installatisu questa macchina preferibile, e ne restituisce il numero }VAR fontSearch, correctFontID: Integer; fontName: Str255;BEGIN fontSearch := 0; REPEAT fontSearch := fontSearch + 1; GetIndString(fontName, listOfChoice, fontSearch) UNTIL GetFontNumber (fontName, correctFontID); FindMostDesirableFont := correctFontIDEND;{$S UtilInit}PROCEDURE DrawSplash (where: DialogPtr; item: INTEGER);CONST rSplashScreen = 147;VAR aPort: GrafPtr; splashPic: PicHandle; picRect, centerPosRect: rect; title: Str255; smallestSize, biggestSize, correctSize, currentWidth: Integer; found: Boolean;BEGIN {$UNUSED item} GetPort (aPort); SetPort (where); FillRect(qd.screenBits.bounds,qd.black); { Make it all black } splashPic := PicHandle (MyGetResource ('PICT', rSplashScreen, TRUE, FALSE)); picRect := splashPic^^.picFrame; { Calcola il punto dove deve apparire la pict } WITH centerPosRect DO BEGIN top := (qd.screenBits.bounds.bottom - picRect.bottom + 20) DIV 2; { 20 per menu bar } bottom := qd.screenBits.bounds.bottom - top; left := (qd.screenBits.bounds.right - picRect.right) DIV 2; right := qd.screenBits.bounds.right - left END; DrawPicture(splashPic,centerPosRect); { Trova cosa scrivere } title := (GetString (rSplashScreen)^^); { Trova un font nel quale scriverlo. Ho una lista di font, in ordine dal pi desiderabile al meno desiderabile, dentro l'applicazione } TextFont (FindMostDesirableFont (rSplashScreen)); TextFace ([outline]); { Trova la dimensione corretta da usare - dicotomica } smallestSize := 12; biggestSize := 1000; REPEAT correctSize := (smallestSize + biggestSize) DIV 2; TextSize (correctSize); currentWidth := StringWidth (title); IF currentWidth > qd.screenBits.bounds.right THEN BEGIN found := FALSE; biggestSize := correctSize END ELSE IF (currentWidth + 20 < qd.screenBits.bounds.right) & (correctSize+1 < biggestSize) THEN BEGIN found := FALSE; smallestSize := correctSize END ELSE found := TRUE; UNTIL found; { Scrivi } MoveTo ((qd.screenBits.bounds.right - currentWidth) DIV 2, qd.screenBits.bounds.bottom DIV 8 * 7); { ForeColor (WhiteColor); } DrawString (title); { Esci } SetPort (aPort); ReleaseResource (Handle(splashPic))END;VAR allCursors: ARRAY [130..131] OF CCrsrHandle;{$S UtilInit}FUNCTION PseudoFilter (theDialog: DialogPtr; VAR theEvent: EventRecord; VAR itemHit: Integer): Boolean;BEGIN {$UNUSED theDialog, itemHit} { Tieni conto di quando siamo apparsi per la prima volta } IF startY = -1 THEN BEGIN { First call ever } IF theEvent.what = updateEvt THEN startY := 0; { Signal that next time through we may put up flashing cursor } END ELSE IF startY = 0 THEN BEGIN { Init flashing cursor } tickleTime := TickCount; SetCCursor (allCursors[130]); startY := 130 END ELSE IF TickCount - tickleTime > 45 THEN BEGIN { Anima il cursore } startY := 261 - startY; { 130->131; 131->130} SetCCursor (allCursors[startY]); tickleTime := TickCount END; { Se ha cliccato esci } IF theEvent.what = mouseDown THEN BEGIN CursorInit; PseudoFilter := TRUE END ELSE PseudoFilter := FALSEEND;{$S UtilInit}PROCEDURE SplashScreen;CONST rSplashScreen = 147;VAR i, itemHit: Integer; splashDlog: DialogPtr; myFilter: ModalFilterUPP; splashPalette: PaletteHandle;BEGIN splashDlog := GetNewDialog(rSplashScreen,NIL,WindowPtr(-1)); { Add correct palette for 256 colors } splashPalette := GetNewPalette (rSplashScreen); SetPalette (splashDlog, splashPalette, TRUE); ActivatePalette (splashDlog); { Set the draw procedures for my user item. } SetItemProcedure (splashDlog, 1, @DrawSplash); {install draw proc} ShowWindow(splashDlog); {make it visible} startY := -1; { Bug fix 1.6: shorten splash screen time } FOR i := 130 TO 131 DO allCursors[i] := GetCCursor (i); myFilter := NewModalFilterProc (@PseudoFilter); ModalDialog(myFilter,itemHit); { Grazie, il filtro non serve pi } DisposeRoutineDescriptor (myFilter); FOR i := 130 TO 131 DO DisposeCCursor (allCursors[i]); ResetItemProcedure (splashDlog, 1); DisposePalette (splashPalette); DisposeDialog (splashDlog); CursorAnimate;END;{-------------- High level resource handling ---------------}{$S GraphEngine}PROCEDURE DoLoadShop (shopListID: INTEGER);VAR theShop: Handle; shopScanner: Ptr; i, id: INTEGER;BEGIN theShop := MyGetResource (resShop, shopListID, TRUE, TRUE); shopScanner := StripAddress(theShop^); { la prima cosa nella risorsa il numero degli oggetti in vendita. Si conta da 0} gNumItemsForSell := GetIntegerFromRes (shopScanner); IF gNumItemsForSell > kMaxItemsInshop THEN BEGIN NewErrorAlert (kAlertStopAlert, errTooManyItems, gNumItemsForSell); gNumItemsForSell := kMaxItemsInshop; { Bug fix 2.0 } END; { OK, cominciamo la lettura } FOR i := 0 TO gNumItemsForSell DO BEGIN id := GetIntegerFromRes (shopScanner); ItemID2ItemData (i, ID, placeID <> rEncounterTreasure); END; { Ciclo lettura oggetti } FOR i := gNumItemsForSell+1 TO kMaxItemsInShop DO BEGIN itemsForSell[i].itemPrice := -1; { No more! } itemsForSell[i].iconID := 0; { No more! } END; ReleaseResource (theShop) { It is supposed it won't be needed soon }END;{$S GraphEngine}PROCEDURE DoSetupShop (whatPlaceRef: INTEGER);{ Shop is now loaded. Have it show up }CONST kIconCellHorzSize = 64;VAR r: rect; c: Cell; numCellsX, numCellsY, i, minVSizeForItems, minVSizeForChars: integer; PROCEDURE AddButton (btnID: Integer); VAR pulsante: ControlHandle; BEGIN pulsante := GetNewControl (btnID, mainWindow); if pulsante = nil then DeathAlert (errMissingApplRes, resNotFound); { I pulsanti sono stati creati in Dream 1.0 con l'idea che la finestra fosse larga 275 x 387 pixel. Ora pu˜ avere dimensioni diverse, e i pulsanti vanno dunque spostati in luogo acconcio. Non un problema trovare la posizione y: 25 pixel sopra il bordo inferiore. } WITH pulsante^^.contrlRect DO BEGIN top := mainWindow^.portRect.bottom - 25; bottom := mainWindow^.portRect.bottom - 5; { Trovare la pos. x pi complesso. Bisogna scoprire di quanti pixel a sinistra del bordo destro andavano posizionati, e poi metterli a sinistra del vero bordo destro di quel numero di pixel l“. } right := right + mainWindow^.portRect.right - 387; left := left + mainWindow^.portRect.right - 387; END { with } END; BEGIN { Quante celle ci stanno in orizzontale? } numCellsX := (qd.screenBits.bounds.right - kMWLeft - kCWWidth) DIV kIconCellHorzSize; { In verticale qualte ne servono? } numCellsY := gNumItemsForSell DIV numCellsX; IF gNumItemsForSell MOD numCellsX <> 0 THEN numCellsY := numCellsY + 1; { Una riga mezza vuota } { OK, ma ci stanno? } i := (qd.screenBits.bounds.bottom { Spazio verticale sullo schermo } - kBottomSpaceForButtons { Spazio riservato in basso nella finestra } - kMWTop { Spazio riservato in alto nella finestra } - 50); { Barra Menu, titolo finestra etc } IF numCellsY = 0 THEN numCellsY := 1; { Capita se salva dentro un treasure dispatching place giˆ vuotato } minVSizeForItems := numCellsY * kIconCellVertSize + kBottomSpaceForButtons; IF minVSizeForItems > i THEN minVSizeForItems := i; { Clip alle dim schermo } minVSizeForChars := kIconHeight * numPC; { Ora ridimensioneremo la finestra in modo da far apparire tutti gli oggetti, e se possibile anche tutti i personaggi. Se i personaggi son pochi, fissiamo il massimo uguale al minimo per coerenza } IF minVSizeForChars < minVSizeForItems THEN minVSizeForChars := minVSizeForItems; IF minVSizeForChars > i THEN minVSizeForChars := i; { Clip alle dim schermo } { Resize main window - new for v2. Have numCellsX in horizontal, numCellsY in vertical } TMNewWindow (mainWindow, fInfoBar, kMainWindowRefCon, minVSizeForChars, { data height } kMWLeft+numCellsX*kIconCellHorzSize, { data width } minVSizeForChars, { max height } kMWLeft+numCellsX*kIconCellHorzSize, { max width } minVSizeForItems, { min height } kMWLeft+numCellsX*kIconCellHorzSize, { min width } 0, 0, 0, 0, kStatusLineHeight, { Info bar height } DrawStatusLine, DrawMainWindow, NIL); { TMNewWindow pu˜ ridimensionare la finestra sotto TaskMaster 3. Informiamone noi stessi } ResizeMainWindow (TRUE); amtTilesInMap.right := numCellsX; { serve al codice ClickOnControl dentro Game.p } r := paneRect; r.bottom := r.bottom - kBottomSpaceForButtons; SetPort (mainWindow); shopSellingList := NuovaLista (mainWindow, r, numCellsX, WantVScroll+WantAutoScroll+WantCustomLDEF+wantTaskMaster, LOnlyOne + LNoNilHilite); c.v := 0; c.h := 0; { se gli oggetti sono parecchi, il ciclo sfarfalla. Quindi disabilito temporaneamente il ridisegno sulla lista } LSetDrawingMode (FALSE, shopSellingList.theList); { Aggiungiamo in lista tutti gli oggetti in vendita } FOR i := 0 TO gNumItemsForSell DO BEGIN NuovaCCella (shopSellingList, c, itemsForSell[i].iconID, @itemsForSell[i].itemName); WITH c DO BEGIN h := succ (h); IF h = numCellsX THEN BEGIN h := 0; v := succ (v) END; { if } END; { with } END; { for } { OK, ridisegna pure } { LSetDrawingMode (TRUE, shopSellingList.theList); } { Non va bene: ridisegna immediatamente la scrollbar nel sistema di coordinate sbagliato! } { New for v1.1. Impedisci di ridimensionare la finestra mentre c' uno shop } evento.wmTaskMask := evento.wmTaskMask - tmGrow; { Add buttons. If this is a real shop, buttons are called "Buy" and "Exit". Otherwise, their name is "Take" and "Exit". } IF whatPlaceRef = rEncounterTreasure THEN FOR i := rButtonTake TO rButtonGetInfo DO AddButton (i) ELSE BEGIN FOR i := rButtonBuy TO rButtonExit DO AddButton (i); AddButton (rButtonGetInfo) END; { Background must be white from now on } BackPat (qd.white); { Keep note of the setup, then exit } TMInvalRect (paneRect); EraseRect (paneRect)END;{$S GraphEngine}PROCEDURE ShutdownShop;VAR i: Integer;BEGIN SetPort (mainWindow); { Kill the list } ListaShutdown (shopSellingList); { Kill the buttons } KillControls (mainWindow); { Kill all items and related windows, if appropriate - new for v 1.1} FOR i := 1 TO gNumItemsForSell DO IF itemsForSell[i].item <> NIL THEN itemsForSell[i].item.Free; { New for v1.1. Permetti di ridimensionare la finestra mentre c' uno shop } evento.wmTaskMask := evento.wmTaskMask + tmGrow; { OK, go ahead } TMInvalRect (paneRect)END;{$S GraphEngine}FUNCTION DoLoadScenario (whichScenario: FSSpecPtr): IoResult;var myFileList: SFTypeList; myReply: StandardFileReply; result: IoResult; infoHandle: Handle; infoPtr: Ptr; scenVersion: Integer; infoLength, minMemory, totMemory: Longint; r: Rect; FUNCTION MyMemory: Longint; { restituisce le dimensioni della mia partizione, cos“ come assegnata nel Finder Gotcha: funziona solo quando io sono in foreground } VAR myProcessInfo: ProcessInfoRec; dummyErrCode: OSErr; myPSN: ProcessSerialNumber; BEGIN dummyErrCode := GetCurrentProcess (myPSN); WITH myProcessInfo DO BEGIN processInfoLength := SizeOf (ProcessInfoRec); processName := NIL; { Roba che non interessa } processAppSpec := NIL END; IF GetProcessInformation (myPSN, myProcessInfo) = noErr THEN MyMemory := myProcessInfo.processSize ELSE MyMemory := 0 { Vai avanti e fottitene } END; begin REPEAT result := allRight; { Supponiamo di riuscirci } { Did caller specify a scenario to be opened? } IF whichScenario = NIL THEN BEGIN { No, so ask user } { Specify filetypes which I can read } myFileList[0] := fileTypeScenario; myFileList[1] := fileTypeScenario3D; StandardGetFile (nil, 2, @myFileList, myReply); whichScenario := @myReply.sfFile; { File found! } IF NOT myReply.sfGood THEN result := userCancel; { If user cancelled, then no hope } END; { Do open it. } IF result = allRight THEN BEGIN { Se ho un vecchio scenario da chiudere, chiudiamolo. } IF currentScenarioFile.resFork <> 0 THEN BEGIN { Zero indica che non c' scenario prec. } Close (currentScenarioFile); ShutdownDreamDB END; { Apri il nuovo file. } ResetRByFSS (currentScenarioFile, whichScenario^); IF (currentScenarioFile.errore <> noErr) THEN result := failedRetry; { Cercane un altro! (Segue messaggio d'errore) } IF result = allRight THEN BEGIN { E' la prima procedura dove possiamo verificare la registrazione.} IF Verifica THEN { Ringraziamo gli onesti } AddToTranscript (userName, ktThankYou, '', 0); { Get scenario info } infoHandle := MyGetResource (resScenarioInfo, rScenarioInfo, FALSE, TRUE); IF infoHandle <> NIL THEN BEGIN infoLength := GetHandleSize (infoHandle); infoPtr := StripAddress (infoHandle^); minDreamVersion := GetIntegerFromRes (infoPtr); IF minDreamVersion > kCurrentDreamVersion THEN BEGIN NewErrorAlert (kAlertStopAlert, errScenarioTooRecent, minDreamVersion); whichScenario := NIL; { Force choosing another } result := failedRetry; END; IF result = allRight THEN BEGIN scenarioName := GetStringFromRes (infoPtr); SetWTitle (mainWindow, scenarioName); AddToTranscript ('', ktYouEntered, scenarioName, 0); suggestedScreenDepth := GetIntegerFromRes (infoPtr); languageCode := GetIntegerFromRes (infoPtr); { Segue "scenario version" che per ora non uso } scenVersion := GetIntegerFromRes (infoPtr); nomeDesigner := GetStringFromRes (infoPtr); { Ora che ho il nome del designer faccio il check sulla profonditˆ dello schermo } IF NOT HasColor (suggestedScreenDepth) THEN DepthAlert; minCharNumber := GetByteFromres (infoPtr); maxCharNumber := GetByteFromres (infoPtr); minCharLevel := GetByteFromres (infoPtr); maxCharLevel := GetByteFromres (infoPtr); startingCharLevel := GetByteFromres (infoPtr); IF NOT Verifica THEN startingCharLevel := 1; { New for 2.1 } { Startup coordinates only from version 1.1 onward } IF minDreamVersion >= 101 THEN BEGIN startX := GetByteFromres (infoPtr); startY := GetByteFromres (infoPtr); END ELSE BEGIN startX := 2; startY := 2 END; { New for v1.1 } IF Longint (infoPtr) - Longint (infoHandle^) < infoLength THEN scenarioSignature := OSType (GetLongintFromRes (infoPtr)) ELSE { Usa le prime quattro lettere del nome } scenarioSignature := OSType(LongintPtr(@scenarioName[1])^); { New for v1.3 } IF Longint (infoPtr) - Longint (infoHandle^) < infoLength THEN gPercentWM := GetByteFromres (infoPtr) ELSE gPercentWM := 16; { One in six, as per v 1.0 docs } { New for v2.1 - min memory requirement } IF Longint (infoPtr) - Longint (infoHandle^) < infoLength THEN BEGIN { C' anche la dimensione minima in byte richiesta } minMemory := GetIntegerFromRes (infoPtr); totMemory := MyMemory; IF totMemory < minMemory * 1024 THEN BEGIN NewErrorAlert (kAlertStopAlert, errScenarioTooBig, 0); DoLoadScenario := failedRetry; Close (currentScenarioFile); whichScenario := NIL; { Bug fix 2.2 } Exit (DoLoadScenario) END { not enough memory } END { v2.1 format } END; { If version is OK } ReleaseResource (infoHandle); { Rebuild Dream database in memory } StartupDreamDB; { Check if scenario was opened for the very first time } IF ScenarioIsBrandNew THEN IF NOT DoScenarioDesignerBiddings THEN BEGIN whichScenario := NIL; result := failedRetry; { Trovane un altro! } END END; { If Info resource was found } IF (result = allRight) & NOT gThreeDInited & ScenarioIsThreeD(currentScenarioFile.FSS) THEN BEGIN { 3D code init } Engine3D_Init; Engine3D_SetInertia (2); WITH r DO BEGIN top := kMWTop; left := kMWLeft; bottom := top + kThreeDDisplayHeight; right := left + kThreeDDisplayWidth END; {$PUSH} {$H-} Engine3D_Set3DPtrAndRect (r, CGrafPtr(mainWindow)); WITH r DO BEGIN top := bottom + 1; left := kMWLeft; bottom := top + kThreeDMapHeight; right := left + kThreeDMapWidth END; Engine3D_SetMapPtrAndRect (r, CGrafPtr(mainWindow)); {$POP} gThreeDInited := TRUE END; END ELSE BEGIN NewErrorAlert (kAlertStopAlert, errChooseAnother, currentScenarioFile.errore); whichScenario := NIL END END; UNTIL (result <> failedRetry) AND (result <> failedContinue); DoLoadScenario := resultEND;{$S GraphEngine}PROCEDURE ChooseAndStartTheMusic;CONST rTempleMusic = 128; rShopMusic = 129; rBarMusic = 130; rBrothelMusic = 131; rBankMusic = 132; rMageMusic = 133; rShrineMusic = 134; rStandardMusic = 135; rExploreMusic = 136; rCombatMusic = 137; { Usato da Fighting System } rAboutMusic = 138; { Usato da CustomAbout } rXMasMusic = 139; { Solo per Natale }VAR theMusicID, err: Integer; secs: Longint; d: DateTimeRec;BEGIN IF gMusicIsOn THEN BEGIN CASE placeKind OF Temple: theMusicID := rTempleMusic; Shop: theMusicID := rShopMusic; Bar: theMusicID := rBarMusic; Brothel: theMusicID := rBrothelMusic; Bank: theMusicID := rBankMusic; MageTower: theMusicID := rMageMusic; Shrine: theMusicID := rShrineMusic; OTHERWISE BEGIN GetDateTime(secs); SecondsToDate(secs,d); WITH d DO IF (month = 12) AND (day > 23) THEN { Christmas time! } theMusicID := rXmasMusic ELSE IF placeData[0] AND NOT placeData[5] THEN { No wandering monsters, already known: safe place } theMusicID := rStandardMusic ELSE theMusicID := rExploreMusic END; { Standard case } END; { case } IF morePlaceData[13] THEN theMusicID := placeID; { New for 2.1 } err := QTMusicPlay (theMusicID); CASE err OF noErr :; -1: NewErrorAlert (kAlertStopAlert, errMusicNotFound, theMusicID); -2: NewErrorAlert (kAlertStopAlert, errNoMemoryForMusic, 0); OTHERWISE NewErrorAlert (kAlertStopAlert, errUnknownMusicError, 0) END { case } ENDEND;PROCEDURE Make2DWorld; FORWARD; { Usata qui, codificata oltre, insieme al graph engine }PROCEDURE NPCMoveAfterMath; EXTERNAL;{ Usata qui, codificata in Game.p }{$S GraphEngine} PROCEDURE MyDisposeMenu (id: Integer; dynamical: Boolean); { Bug fix 2.1 } VAR myMenu: MenuHandle; BEGIN myMenu := GetMenuHandle (id); DeleteMenu (id); IF myMenu <> NIL THEN IF dynamical THEN DisposeMenu (myMenu) ELSE ReleaseResource (Handle (myMenu)); END;{$S GraphEngine}PROCEDURE ThisPlaceIsBeingShutdown;VAR x: Integer; myMenu: MenuHandle;BEGIN IF (groupX > 0) AND PlaceData[2] THEN BEGIN { Yes, we were in a special place of sorts. First, delete hierarchical menus } CASE placeKind OF MageTower: FOR x := kComp1Menu TO kComp8Menu DO MyDisposeMenu (x, FALSE); Temple: MyDisposeMenu (kComp9Menu, FALSE) END; { case } { Then delete the add-on place menu } IF placeKind = prison THEN MyDisposeMenu (kSpecialMenu, TRUE) ELSE MyDisposeMenu (kTempleMenu + ord(placeKind), FALSE); { Put back there the empty place menu } myMenu := GetMenu(kFakeMenu); IF myMenu = NIL THEN DeathAlert (errMissingApplRes, resNotFound); InsertMenu (myMenu, 0); DrawMenuBar END; { If we were in a 3D place, dispose the environment } IF (groupX > 0) AND (placeKind = threeD) THEN Engine3D_DisposeEnvironment; { Impedisci che si passi di nuovo di qui } placeKind := Standard; placeData[2] := FALSEEND;{$S GraphEngine}PROCEDURE DoLoadPlace (placeRef: integer; direction: char);CONST kPoppingBackID = -1; { Called asks for a location pop }VAR oldPlaceKind: SpecialPlace; perRakku: DoublePoint; err: OSErr; startRect, endRect: Rect; { For zooming } myPlaceResource: Handle; myMenu: MenuHandle; x, y, shopListID: INTEGER; { ID of the 'Shop' resource with items for sell } scanner: Ptr; redrawWorld, { If true, then offscreen world must be redrawn } poppingBack: BOOLEAN; { True if we are popping back to last place we were in } PROCEDURE FixColorWorldFor3D; VAR hisClut: CTabHandle; oldPalette, myPalette: PaletteHandle; BEGIN { motore 3D, dammi la tua clut } hisClut := Engine3D_GetBestClut; { finestra di primo piano, qual la tua palette attuale? } oldPalette := GetPalette (mainWindow); { calcola una palette migliore in funzione della clut } myPalette := NewPalette (256, hisClut, pmTolerant, $1000); { assegna la nuova palette alla finestra principale } IF oldPalette <> NIL THEN DisposePalette (oldPalette); SetPalette (mainWindow, myPalette, TRUE); END; PROCEDURE LoadCustomPlaceMenu (menuRsrcID: Integer); { Carica un menu le cui voci sono specificate dal designer } CONST kNumItemsInMenu = 2; {Talk and join, for now} VAR buffer: Str255; definition: Handle; scanner: Ptr; i: Integer; BEGIN definition := MyGetResource (resNPCMenu, menuRsrcID, TRUE, TRUE); scanner := definition^; buffer := GetStringFromRes (scanner); myMenu := NewMenu (kSpecialMenu, buffer); FOR i := 1 TO kNumItemsInMenu DO BEGIN buffer := GetStringFromRes (scanner); IF length (buffer) > 0 THEN InsertMenuItem(myMenu,buffer,i-1) END; InsertMenu (myMenu, 0); ReleaseResource (definition) END; PROCEDURE ResizeAndShowThePlace; { Ora ridimensioneremo la finestra in modo da far apparire il place, e se possibile anche tutti i personaggi.} VAR vSpaceForChars, { Spazio richiesto per le icone personaggi } myNumPC, maxVSize, minVSize, maxHSize, minHSize, idealHSize, idealVSize: INTEGER; BEGIN { Calcola minimo, massimo e ideale X e Y per quanto riguarda il place } IF placeKind = threeD THEN BEGIN maxVSize:= kThreeDDisplayHeight+kThreeDMapHeight; minVSize:= kThreeDDisplayHeight; idealVSize := kThreeDDisplayHeight+kThreeDMapHeight; maxHSize:= kMWLeft+kThreeDDisplayWidth; minHSize:= kMWLeft+kThreeDDisplayWidth; idealHSize := kMWLeft+kThreeDDisplayWidth; END ELSE BEGIN maxVSize:= placeH*32+64; { includes the white border } minVSize:= kMainWindowMinHeight; idealVSize := kMWLeft+placeW*32; maxHSize:= kMWLeft+64+placeW*32; { includes the white border } minHSize:= kMainWindowMinWidth; idealHSize := placeH*32; END; { NumPC non considera lo NPC, dunqueÉ } myNumPC := kNPCReference; WHILE (Mondo[myNumPC] = NIL) AND (myNumPC > 0) DO myNumPC := myNumPC - 1; { Aggiusta in funzione dei personaggi } vSpaceForChars := kIconHeight * myNumPC; IF idealVSize < vSpaceForChars THEN idealVSize := vSpaceForChars; IF maxVSize < vSpaceForChars THEN maxVSize := vSpaceForChars; { The merry conclusion: have the new place drawn } SetPort (mainWindow); TMInvalRect (paneRect); TMNewWindow (mainWindow, fInfoBar+fGrow, kMainWindowRefCon, idealVSize, idealHSize, maxVSize, maxHSize, minVSize, minHSize, 0, 0, 0, 0, kStatusLineHeight, { Info bar height } DrawStatusLine, DrawMainWindow, NIL); { TMNewWindow pu˜ ridimensionare la finestra sotto TaskMaster 3. Informiamone noi stessi } ResizeMainWindow (FALSE); { invalidare la cache grafica } IF (placeKind <> threeD) THEN FlushGraphCache; END; PROCEDURE Fix3DMenu; VAR currentDetailLevel: Integer; variousParams: Family; BEGIN Engine3D_GetCurrentPrefs (currentDetailLevel, variousParams); CASE currentDetailLevel OF E3D_DetailHigh: BEGIN DoCheckItem (kThreeDMenu, kHighDetail, TRUE); DoCheckItem (kThreeDMenu, kLowDetail, FALSE); DoCheckItem (kThreeDMenu, kVarDetail, FALSE); END; E3D_DetailMedium: BEGIN DoCheckItem (kThreeDMenu, kHighDetail, FALSE); DoCheckItem (kThreeDMenu, kLowDetail, TRUE); DoCheckItem (kThreeDMenu, kVarDetail, FALSE); END; E3D_DetailVariable: BEGIN DoCheckItem (kThreeDMenu, kHighDetail, FALSE); DoCheckItem (kThreeDMenu, kLowDetail, FALSE); DoCheckItem (kThreeDMenu, kVarDetail, TRUE); END; END; { case } DoCheckItem (kThreeDMenu, kClouds, variousParams[1]); DoCheckItem (kThreeDMenu, k3DMipMap, variousParams[2]); DoCheckItem (kThreeDMenu, kLensFlare, variousParams[3]); END;BEGIN { The setting have changed, so remember to save! } dirty := TRUE; { PHASE ONE. GOODBYE FROM THE PLACE WE ARE EXITING. GroupX is set to 0 when the game is initializing. This way we know that the place globals and the current placeMap is invalid, and do not try to access them } { If this place must be saved then do save it } IF (groupX > 0) & ((placeDirty & (currentplaceHandle <> NIL)) | (placekind = threeD)) THEN BEGIN DetachResource(currentPlaceHandle); { Serve nel caso in cui fosse ancora parte dello scenario } WriteRes (currentSavegameFile, placeID, resPlace, IToS (kCurrentDreamVersion), { New for final 2.1 - useful in checking corrupted saved games } currentPlaceHandle); IF placeKind = threeD THEN Engine3D_SaveData (placeID); END; { Then get rid of it } IF currentPlaceHandle <> NIL THEN BEGIN ReleaseResource (currentPlaceHandle); { Must be released, or LowLevelRestart leaks memory - bug fix 2.1 } currentPlaceHandle := NIL; placeMap := NIL; END; placeDirty := FALSE; { If we were in a special place, delete all data structures associated with that place } oldPlaceKind := placeKind; ThisPlaceIsBeingShutdown; { If we were in a shop, remove the shop-associated list structure } redrawWorld := TRUE; IF (groupX > 0) AND placeData [3] THEN BEGIN ShutdownShop; redrawWorld := FALSE { Exiting from a shop we may still use the old world } END; { Are we popping back? } SetPort (mainWindow); poppingBack := (placeRef = kPoppingBackID); IF poppingBack THEN BEGIN IF GetGestaltResult (gestaltDragMgrAttr) <> 0 THEN BEGIN { Effetto visivo } startRect := paneRect; endRect := vicinityRect[0,0]; LocalToGlobal (startRect.topLeft); LocalToGlobal (startRect.botRight); LocalToGlobal (endRect.topLeft); LocalToGlobal (endRect.botRight); err := ZoomRects (startRect, endRect, 15, zoomDecelerate); END; IF locationStackPointer = 0 THEN BEGIN { Popping back and stack is empty. Close scenario } Close (currentScenarioFile); { Signal to main code to prompt user for another } moveBuffer := '°'; Exit (DoLoadPlace) END { If popping back into void } ELSE BEGIN { Do pop } { PROBLEMA: se lui esce premendo 7, 9, 1 o 3 io non so con certezza da che direzione lui provenga, e quindi non so dove posizionarlo. SOLUZIONE: mappo quei tasti sui tasti 2, 4, 6 e 8 quando appropriato } CASE direction OF '1': BEGIN IF (groupX = 1) & (groupY <> placeH) THEN direction := '4'; IF (groupY = placeH) & (groupX <> 1) THEN direction := '2'; END; '3': BEGIN IF (groupX = placeW) & (groupY <> placeH) THEN direction := '6'; IF (groupY = placeH) & (groupX <> placeW) THEN direction := '2'; END; '7': BEGIN IF (groupX = 1) & (groupY <> 1) THEN direction := '4'; IF (groupY = 1) & (groupX <> 1) THEN direction := '8'; END; '9': BEGIN IF (groupY = 1) & (groupX <> placeW) THEN direction := '8'; IF (groupX = placeW) & (groupY <> 1) THEN direction := '6'; END; END; { case } placeRef := locationStack[locationStackPointer].placeID; groupX := locationStack[locationStackPointer].x; groupY := locationStack[locationStackPointer].y; IF direction = '5' THEN { Il chiamante vuole che esca da dove entrato (se entrato con '4' deve uscire con '6', eccetera). Io ho memorizzato la direzione con la quale entrato, e ricavo facilmente quella opposta, poichŽ si ottiene facendo 10 - dirProvenienza. P. es. 10 - 4 = 6. } direction := char (ord('1') + ord ('9') - ord(locationStack[locationStackPointer].from)); locationStackPointer := pred (locationStackPointer); { Ora, a seconda del lato dal quale lui uscito, posizioniamolo a nord, sud, est od ovest dell'icona del luogo dal quale esce. Nel caso del non-luogo per la consegna del tesoro, il chiamante mi passerˆ 5 e io lo lascer˜ immobile. } CASE direction OF '1': BEGIN groupX := pred (groupX); groupY := succ (groupY); END; '2': groupY := succ (groupY); '3': BEGIN groupX := succ (groupX); groupY := succ (groupY); END; '4': groupX := pred (groupX); '6': groupX := succ (groupX); '7': BEGIN groupX := pred (groupX); groupY := pred (groupY); END; '8': groupY := pred (groupY); '9': BEGIN groupX := succ (groupX); groupY := pred (groupY); END; END; { case } END { Else - Pop } END { if popping } ELSE BEGIN { Push - stiamo entrando in un posto nuovo } IF (groupX > 0) THEN BEGIN IF GetGestaltResult (gestaltDragMgrAttr) <> 0 THEN BEGIN { Effetto visivo } startRect := vicinityRect[0,0]; endRect := paneRect; LocalToGlobal (startRect.topLeft); LocalToGlobal (startRect.botRight); LocalToGlobal (endRect.topLeft); LocalToGlobal (endRect.botRight); err := ZoomRects (startRect, endRect, 15, zoomDecelerate); END; { SE PROVENIAMO DA QUALCHE PARTE, segnati da dove } IF locationStackPointer = kMaxPlacesOnStack THEN DeathAlert (errLocStackFull, 0); locationStackPointer := succ (locationStackPointer); locationStack[locationStackPointer].placeID := placeID; locationStack[locationStackPointer].x := groupX; locationStack[locationStackPointer].y := groupY; IF (oldPlaceKind = threeD) THEN { Bug fix 2.1: non entro sempre e solo da '8'! } locationStack[locationStackPointer].from := Engine3D_GetAngleKey ELSE locationStack[locationStackPointer].from := direction; { IL RESTO DELLE OPERAZIONI PUO PROCEDERE SOLO DOPO AVER CARICATO LA NUOVA LOCAZIONE!!! } END END; { PHASE TWO: Load new place (from external file, if needed } myPlaceResource := MyGetResource (resPlace, placeRef, TRUE, TRUE); { Load new place } scanner := StripAddress(myPlaceResource^); { Start reading } placeName := GetStringFromRes (scanner); placeID := placeRef; placeW := GetIntegerFromRes (scanner); placeH := GetIntegerFromRes (scanner); placeTime := GetIntegerFromRes (scanner); placeTEXTIn := GetIntegerFromRes (scanner); placePICTin := GetIntegerFromRes (scanner); { Spare. Was "place text out" } placePICTin := GetIntegerFromRes (scanner); placeViewPict := GetIntegerFromRes (scanner); placeData := GetBBFromRes (scanner); morePlaceData := GetWBFromRes (scanner); { E ora la mappa } placeMap := MapPtr (scanner); currentPlaceHandle := myPlaceResource; gMoneySpentHere := 0; AddToTranscript ('', ktYouEntered, placeName, 0); IF placeData [6] {light needed} & (artificialLight < 1) THEN GenericDreamAlert (kPitchBlack); { PHASE THREE: MAKE THE FEATURES OF THE NEW PLACE APPEAR. } { Set correct icon for the group (new for v1.4) } IF morePlaceData[14] { Large scale } THEN rGroupIcon := rGroupIconWhenLarge ELSE IF morePlaceData[15] THEN rGroupIcon := rGroupIconWhenIn ELSE rGroupIcon := rGroupIconWhenOut; { Se un posto speciale, aggiungiamo il menu caratteristico } IF placeData [2] THEN BEGIN { Per risparmiare spazio (e per motivi storici), nel caso di posti speciali trovo dati aggiuntivi in placeTime. QuindiÉ } placeKind := SpecialPlace (placeTime); placeTime := 1; { Togli il fake menu } MyDisposeMenu (kFakeMenu, FALSE); { Carica il menu speciale } IF placeKind = Prison THEN LoadCustomPlaceMenu (placeRef) ELSE BEGIN { Load place menu } myMenu := GetMenu(kTempleMenu + ord(placeKind)); IF myMenu <> NIL THEN BEGIN InsertMenu (myMenu, 0); CASE placeKind OF MageTower: FOR x := kComp1Menu TO kComp8Menu DO BEGIN myMenu := GetMenu(x); IF myMenu = NIL THEN DeathAlert (errMissingApplRes, resNotFound); InsertMenu (myMenu, -1); END; { MageTower } Temple: BEGIN myMenu := GetMenu(kComp9Menu); IF myMenu = NIL THEN DeathAlert (errMissingApplRes, resNotFound); InsertMenu (myMenu, -1); END; { Temple } ThreeD: Fix3DMenu; END { case } END { If there is a special menu (3D place has none) } END; { Load of special menu } DrawMenuBar END { If place is not standard } ELSE placeKind := Standard; { Graphics GWorld handling (new for v2,0 } IF placeKind = threeD THEN BEGIN IF currentWorldDepth > 0 THEN BEGIN DisposeGWorld (world); currentWorldDepth := -1; { Clean up } SetPort (mainWindow); EraseRect (mainWindow^.portrect) END; IF poppingBack THEN Engine3D_NewEnvironment (placeRef, direction) ELSE Engine3D_NewEnvironment (placeRef, '5'); { Prendi l'angolo di visuale dal 3DEn, non dalla posizione del giocatore } FixColorWorldFor3D; END ELSE IF currentWorldDepth < 0 THEN BEGIN Engine3D_DisposeEnvironment; Make2DWorld; END; { Readjust the abilities for the characters. Bug fix 2.0: use CalcTHACO, or the bonus will be added multiple times if the player saves and reloads the game multiple times } IF (groupX > 0) THEN IF morePlaceData [15] THEN { We are now indoors } FOR x := numPC-1 DOWNTO 0 DO BEGIN IF (Mondo[x].razza = Nano) OR (Mondo[x].razza = Gnomo) THEN Mondo[x].THACO := CalcTHACO (Mondo[x].classe, Mondo[x].livello, Mondo[x].forza) - 1; IF (Mondo[x].razza = Elfo) THEN Mondo[x].THACO := CalcTHACO (Mondo[x].classe, Mondo[x].livello, Mondo[x].forza) + 1; END ELSE { We are outdoors } FOR x := numPC-1 DOWNTO 0 DO BEGIN IF (Mondo[x].razza = Nano) OR (Mondo[x].razza = Gnomo) THEN Mondo[x].THACO := CalcTHACO (Mondo[x].classe, Mondo[x].livello, Mondo[x].forza) + 1; IF (Mondo[x].razza = Elfo) THEN Mondo[x].THACO := CalcTHACO (Mondo[x].classe, Mondo[x].livello, Mondo[x].forza) - 1; END; { Se un negozio, carica la finestra tipica e mostra la lista degli oggetti che si possono comprare qui } IF placeData [3] THEN BEGIN { Per risparmiare spazio (e per motivi storici), nel caso di negozi trovo lo ID della lista di oggetti venduti in placeTime. QuindiÉ } shopListID := placeTime; placeTime := 1; { Metti il gruppo nell'unica locazione che sappiamo per certo esistere } groupX := 1; groupY := 1; { If this is a real shop, then load shop list. Load items data. (Otherwise, this is a fake place created for dispatching the treasure) } IF placeRef <> rEncounterTreasure THEN DoLoadShop (shopListID); { Setup shop } DoSetupShop (placeRef); { There's no need to create a new gworld } redrawWorld := FALSE END ELSE IF NOT poppingBack THEN { TERMINE DELLE OPERAZIONI INIZIATE IN FASE UNO } CASE direction of { Qui c'era un bug in 1.0: con (p.es.) larghezza sette ed entrando con mossa '2' finivamo in x=3 anzichŽ x=4 come sarebbe corretto. Il calcolo corretto viene effettuato solo per scenari creati per Dream 1.5, in modo da mantenere la compatibilitˆ con quelli vecchi } '1': BEGIN groupX := placeW-1; groupY := 2 END; '2': BEGIN IF minDreamVersion < 110 THEN groupX := placeW DIV 2 ELSE groupX := (placeW+1) DIV 2; groupY := 2 END; '3': BEGIN groupX := 2; groupY := 2 END; '4': BEGIN groupX := placeW-1; IF minDreamVersion < 110 THEN groupY := placeH DIV 2 ELSE groupY := (placeH+1) DIV 2; END; '5': IF groupX = 0 THEN BEGIN { Per l'inizio di un nuovo scenario } groupX := startX; groupY := startY END; '6': BEGIN groupX := 2; IF minDreamVersion < 110 THEN groupY := placeH DIV 2 ELSE groupY := (placeH+1) DIV 2; END; '7': BEGIN groupX := placeW-1; groupY := placeH-1 END; '8': BEGIN IF minDreamVersion < 110 THEN groupX := placeW DIV 2 ELSE groupX := (placeW+1) DIV 2; groupY := placeH-1 END; '9': BEGIN groupX := 2; groupY := placeH-1 END; END; { case } IF placeKind = threeD THEN BEGIN TaskPriority (taskHighPriority); { Mac OS, give me max speed } EraseRect (paneRect); IF poppingBack THEN BEGIN { New for v2: reload 3D location, if any } Engine3D_LoadData (placeID); perRakku.h := groupX; perRakku.v := groupY; shopListID := Engine3D_SetViewPoint (perRakku, FALSE); END ELSE BEGIN { Bug fix 2.1: load real group coords, even if from "-1" pop-out location } Engine3D_GetViewInfos (perRakku, x); groupX := perRakku.h; groupY := perRakku.v END; TMBeginDirectDraw (mainWindow); shopListID := Engine3D_Scan (RotateAll); { Per costringere il motore 3D a calcolare l'angolo di vista } { If "show all on startup", show all map.New for 2.1 } IF placeData[0] THEN Engine3D_MakeAllWallsVisible; { Handle map } Engine3D_ScanMap; TMEndDirectDraw (mainWindow); gLast3DMapRefresh := TickCount; { Last refresh of map is now } END { if 3D } ELSE { No 3D. The standard process speed will do } TaskPriority (taskMiddlePriority); { If the place we have entered has a welcome speech, say it } IF placeTEXTin <> 0 THEN BEGIN DoSpeechAsync (placeTEXTin); TextOut (placeTEXTin, TRUE); END ELSE IF NOT placeData [3] THEN BEGIN KillText; { Non ha molto senso lasciare in vista il testo del luogo precedente } END; { If the place has a welcome pict, show it } IF (placePICTin <> 0) AND NOT placeData[1] THEN PictOut (placePICTin); IF placeKind <> threeD THEN IF placeData[0] THEN { All place should be shown on entry } FOR x := 1 TO placeW*placeH DO placeMap^[x].characteristics[0] := TRUE ELSE { The following code is here so that I'll see the bare minimum entering a new place } FOR y := -1 TO 1 DO IF (groupY + y > 0) & (groupY + y <= placeH) THEN FOR x := -1 TO 1 DO IF (groupX + x > 0) & (groupX + x <= placeW) THEN placeMap^[groupX+x+(groupY+y-1)*placeW].characteristics[0] := TRUE; { Let the music be! } ChooseAndStartTheMusic; IF NOT placeData [3] THEN { not a shop } ResizeAndShowThePlace; { New for v2 } NPCMoveAfterMath; { Se l'NPC deve andarsene, che se ne vada! }END;{============== Primitive grafiche di basso livello ================}{$S GraphEngine}FUNCTION HasColor (bitsPerPixel: Byte): boolean;VAR wideRect: Rect; maxDevice: GDHandle;BEGIN SetRect (wideRect, -maxint, -maxint, maxint, maxint); IF gQDVersion > kQDOriginal THEN BEGIN maxDevice := GetMaxDevice (wideRect); HasColor := maxdevice^^.gdPMap^^.pixelSize >= bitsPerPixel END ELSE HasColor := FALSEEND;{$S GraphEngine}PROCEDURE Make2DWorld;VAR err: OSErr; worldBoundsInPixel: Rect;BEGIN SetRect(worldBoundsInPixel,0,0,kGWorldSide,kGWorldSide); currentWorldDepth := 8; WHILE NOT HasColor (currentWorldDepth) DO currentWorldDepth := currentWorldDepth DIV 2; err := NewGWorld (world, currentWorldDepth, worldBoundsInPixel, NIL, NIL, 0); IF err <> noErr THEN DeathAlert (errOutOfMemory, err);END;{$S GraphEngine}PROCEDURE DrawGrayRect (r: Rect);VAR whiteRGB, dkGrayRGB, userColor: RGBColor; userPen: PenState;BEGIN { Prepara i colori } WITH whiteRGB DO BEGIN red := $FFFF; green := $FFFF; blue := $FFFF END; WITH dkGrayRGB DO BEGIN red := $7FFF; green := $7FFF; blue := $7FFF END; GetForeColor (userColor); RGBForeColor (dkGrayRGB); GetPenState (userPen); PenSize (2, 2); { Disegna } WITH r DO BEGIN MoveTo (left, bottom); LineTo (left, top); LineTo (right, top); RGBForeColor (whiteRGB); LineTo (right, bottom); LineTo (left, bottom) END; { Rimetti le cose com'erano } RGBForeColor (userColor); SetPenState (userPen)END;{$S GraphEngine}FUNCTION DragCicn (p: Point; c: Integer; r: Rect): Point;{ Tecnica descritta nella Nota Tecnica TB.WMgrPort }VAR iconHandle: CIconHandle; sourceRectRgn, originalVisRgn: RgnHandle; everywhere, rCopy: rect; resultPoint: longint; finalResult, puttanadelreggimento: Point; guestPort: GrafPtr; totalPort: GrafPort; err: OSErr;begin rCopy := r; { Saved for later } { Recupera l'icona } iconHandle := GetCIcon (c); IF iconHandle = NIL THEN BEGIN DragCicn := DragRect (p, rCopy); { default to old style dragging } Exit (DragCicn); END; { L'icona non ha i campi validi sinchŽ non viene disegnata } SetRect (everywhere, 30000, 30000, 30064, 30064); PlotCIcon(everywhere, iconHandle); GetPort (guestPort); { Porta i param in globali } LocalToGlobal (r.topLeft); LocalToGlobal (r.botRight); puttanadelreggimento := p; LocalToGlobal (puttanadelreggimento); { Crea una regione che contenga il rettangolo } sourceRectRgn := NewRgn; CloseRgn (sourceRectRgn); err := BitmapToRegion (sourceRectRgn, iconHandle^^.iconMask); IF err = noErr THEN BEGIN { Sposta la rgn nella stessa posizione del rect } OffsetRgn (sourceRectRgn, r.left, r.top); { Crea un nuovo GrafPort nel quale disegnare } OpenPort(@totalPort); originalVisRgn := totalPort.visRgn; totalPort.visRgn := GetGrayRgn; everywhere := totalPort.visRgn^^.rgnBBox; totalPort.portRect := everywhere; { Usual grafPort gimmick } SetPort (@totalPort); { Chiedi al toolbox di fare il lavoro } resultPoint := DragGrayRgn (sourceRectRgn, puttanadelreggimento, everywhere, everywhere, noConstraint, NIL); { Il risultato il delta, quindiÉÊ} finalResult.v := HiWrd (resultPoint) + p.v; finalResult.h := LoWrd (resultPoint) + p.h; { Restituisci il risultato } DragCicn := finalResult; { GrafPrt again } SetPort (guestPort); totalPort.visRgn := originalVisRgn; ClosePort(@totalPort); SetPort (guestPort); { Lo so che non dovrebbe servire, maÉÊ} END ELSE DragCicn := DragRect (p, rCopy); { default to old style dragging } { Liberati della memoria allocata } DisposeRgn (sourceRectRgn); DisposeCicon (iconHandle)end;{$S GraphEngine}FUNCTION DragRect (p: Point; r: Rect): Point;{ Tecnica descritta nella Nota Tecnica TB.WMgrPort }VAR sourceRectRgn, originalVisRgn: RgnHandle; everywhere: rect; resultPoint: longint; finalResult, puttanadelreggimento: Point; guestPort: GrafPtr; totalPort: GrafPort;begin GetPort (guestPort); { Porta i param in globali } LocalToGlobal (r.topLeft); LocalToGlobal (r.botRight); puttanadelreggimento := p; LocalToGlobal (puttanadelreggimento); { Crea una regione che contenga il rettangolo } sourceRectRgn := NewRgn; CloseRgn (sourceRectRgn); RectRgn (sourceRectRgn, r); { Crea un nuovo GrafPort nel quale disegnare } OpenPort(@totalPort); originalVisRgn := totalPort.visRgn; totalPort.visRgn := GetGrayRgn; everywhere := totalPort.visRgn^^.rgnBBox; totalPort.portRect := everywhere; { Usual grafPort gimmick } SetPort (@totalPort); { Chiedi al toolbox di fare il lavoro } resultPoint := DragGrayRgn (sourceRectRgn, puttanadelreggimento, everywhere, everywhere, noConstraint, NIL); { Il risultato il delta, quindiÉÊ} finalResult.v := HiWrd (resultPoint) + p.v; finalResult.h := LoWrd (resultPoint) + p.h; { GrafPrt again } SetPort (guestPort); totalPort.visRgn := originalVisRgn; ClosePort(@totalPort); SetPort (guestPort); { Lo so che non dovrebbe servire, maÉÊ} { Restituisci il risultato } DragRect := finalResult; { Liberati della memoria allocata } DisposeRgn (sourceRectRgn)end;{----------------------------------------------------------}{ Codice di basso livello per la gestione di progress bar }{----------------------------------------------------------}var { Per la progress bar } progressBarPos, innerRect: rect; totalData: longint; barWidth: integer;{$S GraphEngine}Procedure BarInit (whereBar: rect; max: longint);begin progressBarPos := whereBar; totalData := max; { Delimita l'area da colorare } innerRect := progressBarPos; InsetRect (innerRect, 1, 1); { Calcola la larghezza in pixel della sbarra } barWidth := innerRect.right - innerRect.left;end;{$S GraphEngine}Procedure DrawProgressBar (doneData: longint; doneColor, undoneColor: PixPatHandle);{ Gotchas: Usa solo integer e longint per non richiedere SANE }var lastDonePixel: integer; paintedRect: rect;begin { Sanity check } if doneData > totalData then doneData := totalData; { mostra il contorno nero della sbarra } FrameRect (progressBarPos); { Calcola quanti pixel debbono apparire scuri ("fatto") } lastDonePixel := (barWidth * doneData) div totalData; { Trova il rettangolo "fatto" } paintedRect := innerRect; paintedRect.right := paintedRect.left + lastDonePixel; PenPixPat (doneColor); PaintRect (paintedRect); { trova il rettangolo "da fare" } IF doneData = 0 THEN paintedRect.left := innerRect.left ELSE paintedRect.left := paintedRect.right + 1; paintedRect.right := innerRect.right; PenPixPat (undoneColor); PaintRect (paintedRect); PenPat (qd.black)end;{*** Caching ***}{$S DefProcs}PROCEDURE DoDrawMap;LABEL 100;VAR err: OSErr; destRect, sourceRect, availableRoomOnScreen: Rect; dummy: Boolean; okRgn, blackRgn: RgnHandle;BEGIN { Caso speciale: non c' luce } IF (currentWorldDepth < 0) | (placeData[6] & (artificialLight < 1)) THEN BEGIN {Sanity check v2} FillRect(paneRect, qd.black); Goto 100 END; { Ora disegna il mondo } SetRect(sourceRect, { Rect del gworld dal quale copiare } groupX-amtTilesInMap.left, groupY-amtTilesInMap.top, { Se questo andasse copiato tutto, } groupX+amtTilesInMap.right, { finirebbe pari pari in paneRect } groupY+amtTilesInMap.bottom); availableRoomOnScreen := sourceRect; { Fanne una copia per dopo } dummy := SectRect (sourceRect, placeRect, sourceRect); { Escludi i pixel inesistenti } { Trova dove va messo questo coso, perchŽ se ho appena escluso la fascia in alto e a sinistra o quella in basso a destra non posso semplicemente metterlo su paneRect. (Una cosa del genere succede quando sono vicino al bordo, quando non trasferisco dalla cache un rettangolo di dimensioni piene, ma solo la fetta d'angolo). } SubPt(sourceRect.topLeft,availableRoomOnScreen.topLeft); { Trova di quanto stato spostato a destra e in basso l'angolo superiore sinistro } SubPt(sourceRect.botRight,availableRoomOnScreen.botRight); destRect := paneRect; WITH destRect DO BEGIN top := top + BSL(-availableRoomOnScreen.top, 5); left := left + BSL(-availableRoomOnScreen.left, 5); bottom := bottom - BSL(availableRoomOnScreen.bottom, 5); right := right - BSL(availableRoomOnScreen.right, 5); END; { Passa da coordinate a pixel, ricordando che nel gworld ci sono solo le locazioni cached in worldrectÉ } WITH sourceRect DO BEGIN left := BSL (left-worldRect.left, 5); top := BSL (top-worldRect.top, 5); right := BSL (right-worldRect.left+1, 5); bottom := BSL (bottom-worldRect.top+1, 5); END; { La parte di window che fa parte di paneRect ma non di destRect va coperta in BIANCO: si tratta dello spazio che non fa parte della mappa della locazione } blackRgn := NewRgn; CloseRgn (blackRgn); okRgn := NewRgn; CloseRgn (okRgn); RectRgn (blackRgn, paneRect); RectRgn (okRgn, destRect); DiffRgn (blackRgn, okRgn, blackRgn); { New for v1.1 - also remove the grow box rgn } DiffRgn (blackRgn, tmAuxRecordHandle(WindowPeek(mainWindow)^.refCon)^^.wScrollBarsSpace, blackRgn); FillRgn (blackRgn, qd.white); DisposeRgn (blackRgn); DisposeRgn (okRgn); IF LockPixels (world^.portPixMap) THEN BEGIN CopyBits(bitmapptr(world^.portPixMap^)^, mainWindow^.portBits, sourceRect, destRect, srcCopy, { New for v1.1: avoid overwriting the grow box } tmAuxRecordHandle(WindowPeek(mainWindow)^.refCon)^^.wContentSpace); UnlockPixels (world^.portPixMap); END;100: { Group symbol } err := PlotIconID (vicinityRect[0,0], atNone, ttNone, rGroupIcon);END;{$S DefProcs}PROCEDURE DoDrawShop;VAR c: Cell; s: Str255; itemSelected: Integer; r: Rect;BEGIN LSetDrawingMode (TRUE, shopSellingList.theList); { L'avevo lasciato settato su false } Ridisegna (shopSellingList); IF DammiCella (shopSellingList, c) THEN BEGIN itemSelected := c.v*amtTilesInMap.right+c.h; r := paneRect; { CODICE COPIATO DA HANDLECLICK DENTRO GAME } r.top := r.bottom - kBottomSpaceForButtons + 2; r.right := mainWindow^.portRect.right - 387 + 219; { Get Info btn comincia a 220 secondo le rez } EraseRect (r); IF itemsForSell[itemSelected].itemPrice >= 0 THEN BEGIN GetIndString(s, rUserIntfStrings, kCanBeYoursFor); MoveTo (paneRect.left+10, paneRect.bottom - 7); DrawString (s); DrawString (IToS (itemsForSell[itemSelected].itemPrice)); GetIndString(s, rUserIntfStrings, kGoldenEagles); DrawString (s) END END; ValidRect (paneRect);END;{$S GraphEngine}PROCEDURE FixGWorldAndPlotPicture (VAR g: GWorldPtr; pictID: Integer; f: WindowPtr);CONST rStandardColors = 999; { Quelli da usare in accoppiata alle icone }VAR p: PicHandle; myPictInfo: PictInfo; flags: GWorldFlags; err, err1: OSErr; oldPalette: PaletteHandle; oldCTable: CTabHandle; screensDevice: GDHandle; oldGD : gdhandle; oldWorld: GWorldPtr; resAttributes: Integer; (* FUNCTION TryDeepGWorld (bestPalette: PaletteHandle): BOOLEAN; { True: non ci sono riuscito; FALSE: ci sono riuscito e PaletteHandle (che deve esistere ed essere valida prima della chiamata) ora punta alla miglior parelle possibile per la nuova immagine dithered } LABEL 998, 999; VAR dummy: Size; result: Boolean; tempWorld: GWorldPtr; BEGIN result := TRUE; { default non ci riesco } OffsetRect (p^^.picFrame, -p^^.picFrame.left, - p^^.picFrame.top); { Normalizza pict rect } IF TempMaxMem (dummy) < p^^.picFrame.bottom * p^^.picFrame.right * 4 THEN { Not enough memory } Goto 999; { Alloca GWorld temporaneo } err := NewGWorld (tempWorld, 32, p^^.picFrame, NIL, NIL, useTempMem); IF err <> noErr THEN Goto 999; { Prepara offscreen GWorld } GetGWorld (oldWorld, oldGD); SetGWorld (tempWorld, NIL); IF LockPixels (tempWorld^.portPixMap) THEN BEGIN { Plotta la picture in offscreen GWorld } DrawPicture (p, p^^.picFrame); { Posterizza offscreen GWorld } flags := UpdateGWorld (tempWorld, 8, p^^.picFrame, NIL, NIL, clipPix+ditherPix); IF BAnd (flags, gwFlagErr) <> 0 THEN BEGIN err := QDError; Goto 998; END; { bada alla palette } CTab2Palette (tempWorld^.portPixMap^^.pmTable, bestPalette, pmTolerant, $1000); { e alla CTable del GWorld permanente } Palette2CTab (bestPalette, g^.portPixMap^^.pmTable); { Copia dal GWorld posterizzato a quello permanente in otto bit } CopyBits(bitmapptr(tempWorld^.portPixMap^)^, bitmapptr(g^.portPixMap^)^, p^^.picFrame, p^^.picFrame, srcCopy, nil); result := FALSE; { Ce l'abbiamo fatta! }998: UnlockPixels (tempWorld^.portPixMap) END; { else ricadi nel codice sotto, che fa il dispose e restituisce l'errore di default } { Grazie e arrivederci } SetGWorld (oldWorld, oldGD); DisposeGWorld (tempWorld);999: TryDeepGWorld := result END; *) FUNCTION IsSystemResource (r: Handle): Boolean; VAR dummyType: resType; dummyName: Str255; resId: Integer; BEGIN GetResInfo(r, resID, dummyType, dummyName); IsSystemResource := (resId < 128) AND (resID >= 0) END; BEGIN { Calcola palette e color table } WITH myPictInfo DO IF pictID = 0 THEN BEGIN thePalette := GetNewPalette (rStandardColors); { palette standard } theColorTable := NIL END ELSE BEGIN p := PicHandle (MyGetResource ('PICT', pictID, TRUE, TRUE)); { C' una palette giˆ pronta? } theColorTable := GetCTable (pictID); IF theColorTable = NIL THEN BEGIN { No, non c' CLUT precotta. } { Calcolane una su misura } err1 := GetPictInfo (p, myPictInfo, returnColorTable+returnPalette, 254, medianMethod, 0); IF err1 <> noErr THEN BEGIN theColorTable := NIL; thePalette := GetNewPalette (rStandardColors); { palette standard } END ELSE BEGIN { cSeed } { Set the color table's ctSeed equal to that of the main screen's GDevice. This tells QuickDraw not to do color mapping. Color mapping is not needed because the palette matches the color table. } screensDevice := GetMainDevice; IF (screensDevice <> nil) THEN theColorTable^^. ctSeed := screensDevice^^.gdPMap^^.pmTable^^.ctSeed; END { cSeed } END { if clut not found } ELSE { Clut found, create equivalent palette for window } thePalette := NewPalette (254, theColorTable, pmTolerant, $1000); END; { if pictID 0 } { Aggiorna il gworld } oldCTable := g^.portPixMap^^.pmTable; flags := UpdateGWorld (g, 8, g^.portRect, myPictInfo.theColorTable, NIL, 0); IF BAnd (flags, gwFlagErr) <> 0 THEN err := QDError; { potr˜ liberarmi della vecchia CTable solo se ha adottato quella nuova } { Liberiamoci della CTable precedente, (ma solo se non di sistema) grazie e arrivederci } IF (oldCTable <> NIL) AND (err = noErr) AND NOT IsSystemResource (Handle (oldCTable)) THEN DisposeCTable (oldCTable); { Devo anche plottare una picture? } IF pictID <> 0 THEN (* { Per caso, ho abbastanza memoria libera per lavorare in truecolor? } IF TryDeepGWorld (myPictInfo.thePalette) THEN *) BEGIN { Se no, limitiamoci a copiare la picture nel GWorld a 8 bit } GetGWorld (oldWorld, oldGD); SetGWorld (g, NIL); IF LockPixels (g^.portPixMap) THEN BEGIN DrawPicture (p, p^^.picFrame); UnlockPixels (world^.portPixMap) END; SetGWorld (oldWorld, oldGD); ReleaseResource (Handle (p)) END; { Occupiamoci della palette della finestra } SetPort (f); oldPalette := GetPalette (f); SetPalette (f, myPictInfo.thePalette, TRUE); ActivatePalette (f); resAttributes := GetResAttrs (Handle (oldPalette)); IF (oldPalette <> NIL) AND (BAnd (resAttributes, resSysHeap) = 0) THEN DisposePalette (oldPalette);END;{$S GraphEngine}PROCEDURE CacheIt;{ v1.3: gestisce le pict }{ Mette nel GWorld una immagine del place, compresa nelle coordinateindicate nella globale worldRect }VAR i, j, locationID: Integer; oldPort: CGrafPtr; oldGdev: GDHandle; r: Rect; err: OSerr;BEGIN CursorInit; { C' una Pict associata? } IF NOT placeData[1] THEN placeViewPict := 0; { Non serve, ma tanto per star certi } CursorAnimate; { Gestisci il colore al meglio } FixGWorldAndPlotPicture (world, placeViewPict, mainWindow); CursorAnimate; GetGWorld (oldPort, oldGdev); SetGWorld (world, NIL); IF LockPixels (world^.portPixMap) THEN BEGIN { C' una Pict associata? } IF NOT placeData[1] THEN BEGIN { No picture, make map using icons } BackPat (qd.black); EraseRect (world^.portRect); WITH worldRect DO FOR i := left TO right DO BEGIN SetRect (r, BSL(i-left, 5), 0, BSL(i-left+1, 5), 32); FOR j := top TO bottom DO BEGIN locationID := i+(j-1)*placeW; IF placeMap^[locationID].characteristics[0] {known} THEN err := PlotIconID (r, atNone, ttNone, placeMap^[locationID].icon); OffsetRect(r,0,32); END; { for j } CursorAnimate END { for i } END; { No picture } UnlockPixels (world^.portPixMap); END; { If LockPixels OK } { Ecco fatto. Ora torniamo a bomba } SetGWorld (oldPort, oldGdev);END;{$S GraphEngine}PROCEDURE AddToCache (x, y, locationID: Integer);VAR r: rect; err: OSerr; oldPort: CGrafPtr; oldGdev: GDHandle;BEGIN GetGWorld (oldPort, oldGdev); SetGWorld (world, NIL); if LockPixels (world^.portPixMap) THEN BEGIN WITH r DO BEGIN top := BSL (y - worldRect.top, 5); left := BSL (x - worldRect.left, 5); bottom := top + 32; right := left + 32 END; err := PlotIconID (r, atNone, ttNone, placeMap^[locationID].icon); UnlockPixels (world^.portPixMap); END; { If LockPixels OK } { Ecco fatto. Ora torniamo a bomba } SetGWorld (oldPort, oldGdev);END;{$S GraphEngine}PROCEDURE CallMeAtResume;BEGIN { Forse durante la sospensione l'utente ha cambiato la profonditˆ dello schermo? } gActualDepth := 8; WHILE NOT HasColor (gActualDepth) DO gActualDepth := gActualDepth DIV 2; IF (currentWorldDepth > 0) AND (gActualDepth <> currentWorldDepth) THEN BEGIN CursorInit; { Si. Uccidi il vecchio GWorld e creane un altro pi adatto } DisposeGWorld (world); CursorAnimate; Make2DWorld; CursorAnimate; currentWorldDepth := gActualDepth; { Aggiorna la cache } FlushGraphCache; SetPort (mainWindow); TMInvalRect (paneRect); { Se ci sono troppo pochi colori, avvisa l'utente } IF gActualDepth < suggestedScreenDepth THEN DepthAlert END; IF placeKind = threeD THEN TaskPriority (taskHighPriority) { Mac OS, give me max speed } ELSE TaskPriority (taskMiddlePriority)END;{$S GraphEngine}PROCEDURE CallMeAtSuspend;BEGIN TaskPriority (taskLowPriority) { Mac OS, let me go slowly }END;{$S GraphEngine}PROCEDURE ScrollWorld;{ Il gruppo si mosso, e si trova ora in groupX, groupY.Se necessario, aggiorna la cache grafica }VAR viewRect: rect; dummy: Boolean;BEGIN { Rettangolo visibile a video, ammesso e non concesso che non sia vicino ai bordi (se lo fosse ovvio che Ñ per esempio Ñ la locazione -1, -2 fa parte di questo rettangolo ma non cached comunque } WITH amtTilesInMap DO SetRect (viewRect, groupX-left, groupY-(bottom+1), groupX+right, groupY+top); { Ora elimina eventuali locazioni insignificanti dal rect } dummy := SectRect (viewRect, placeRect, viewRect); { Questo rettangolo interamente contenuto nel rect che cached? Prima di fare questo calcolo bisogna tenere presente che per QuickDraw il punto di coordinate 7, 7 non fa parte del rettangolo 1, 1, 7, 7, e quindi bisogna barare un pochino. } viewRect.bottom := pred (viewRect.bottom); viewRect.right := pred (viewRect.right); IF NOT (PtInRect (viewRect.topLeft, worldRect) & PtInRect (viewRect.botRight, worldRect)) THEN BEGIN { Se no, aggiorna lo spazio cached } { 1. Calcola un nuovo worldRect che minimizzi le possibilitˆ di un prossimo cache miss } SetRect (worldRect, groupX-15, groupY-16, groupX+16, groupY+15); WITH worldRect DO BEGIN IF bottom > placeH THEN BEGIN bottom := placeH; top := bottom-31; END; IF right > placeW THEN BEGIN right := placeW; left := placeW-31 END; IF top < 1 THEN BEGIN top := 1; bottom := 32 END; IF left < 1 THEN BEGIN left := 1; right := 32; END END; { with } { 2. Provoca il ridisegno del gworld } CacheIt; END;END;{$S GraphEngine}PROCEDURE FlushGraphCache;{ Provoca il ridisegno della cache grafica. Da chiamare quando si passati adaltro place }BEGIN { Aggiorna il placeRect, rettangolo che contiene il place } SetRect (placeRect, 1, 1, placeW, placeH); { Invalida il worldRect, rettangolo che contiene le locazioni del place che hanno trovato spazio nella cache. worldRect sarˆ ricalcolato da ScrollWorld } SetRect (worldRect, 0, 0, -1, -1); { Chiama ScrollWorld ad aggiornare la cache } ScrollWorld;END;{$S GraphEngine}PROCEDURE ShowAllPlace;VAR locationID: Integer;BEGIN FOR locationID := 1 TO placeW*placeH DO placeMap^[locationID].characteristics[0] := TRUE; IF placeKind = threeD THEN Engine3D_MakeAllWallsVisible ELSE { Redraw GWorld } CacheIt; { Show it } SetPort (mainWindow); TMInvalRect (paneRect)END;{$S GraphEngine}PROCEDURE EngineInit;CONST rListOfFontsForTalkEngine = 133; kSizeOfFontForTalkEngine = 14;BEGIN placeKind := Standard; { Per evitare che al primo refresh qualcuno chiami il codice 3D } placeData[6] := TRUE; { Bug fix 1.6: impedisce l'apparire di } { garbage attorno all'icona del gruppo nelo splash screen } placeData [2] := FALSE; SplashScreen; SetRect(amtTilesInMap,5,4,5,3); Make2DWorld; { Init Talk Engine } TalkEngine_Init; TalkEngine_SetFont (FindMostDesirableFont (rListOfFontsForTalkEngine), kSizeOfFontForTalkEngine); { Init DreamDB data structures in memory } InitDreamDB; { Init group icon } rGroupIcon := rGroupIconWhenLargeEND;{$S GraphEngine}PROCEDURE EngineShutdown;BEGIN { Free memory allocated to the Dream database } ShutdownDreamDB; Close (dreamDB)END;END. { Unit }