-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathGame.p
3552 lines (3377 loc) · 113 KB
/
Game.p
1
Unit Game;InterfaceUses Types, QuickDraw;TYPE TExplorationResult = (yesYouMayExplore, yesButStopInThere, noYouCant);PROCEDURE All;FUNCTION MainEventLoop (whereAmI: Point): Char;{ Viene chiamata anche da TPersonaggio.Move in Characters.p, quindi va dichiarata }PROCEDURE NPCMoveAfterMath;{ Va chiamato da DoLoadPlace, ma scritta qui perchŽ effettua chiamate di alto livello }FUNCTION DoScenarioDesignerBiddings: Boolean;{ Il Conflict resolutor. Temporaneamente qui sinchŽ non avr˜ il tempo e lavogli di alzare il livello di TCreatura. A quel punto potrˆ venire rimessodentro graphEngine, dove sta da un punto di vista logico.restituisce TRUE se tutto andato bene e si pu˜ proseguire }FUNCTION MayIExploreThere (x, y: Integer; VAR direzione: Char): TExplorationResult;{ Restituisce yesYouMayExplore se il giocatore pu˜ muoversi nel luogo indicato;yesButStopInThere se pu˜ entrarci, ma una vota l“ gli accadrˆ qualcosa;noYouCant altrimenti }ImplementationUSES Appearance, AppleEvents, Controls, EPPC, Errors, Events, { GestaltEqu, { (3.2) } Icons, Memory, Menus, OSUtils, Resources, SegLoad, TextEdit, TextUtils, { List 3 - needs List 1/2 types } Aliases, { (3.2) needs Memory } Files, { needs OSUtils, SegLoad } Windows, { needs Events, Controls } { List 4 - needs List 1/2/3 types } Dialogs, { needs TextEdit, Windows } Folders, { (3.2) needs Files } Lists, StandardFile, { (3.2) needs Aliases } ObjIntf, MoreFilesExtras, MusicEngine, Galatea, Cilindro, Lista3, DialogLord4, BinIO, TaskMaster3, Dream3Display_Tipi, Engine3D, Engine3D_SetupPrePalette, TalkEngine, DreamTypes, LowLevel, GraphEngine, HiLevel, DreamMagic, Characters, DreamMonsters, DreamIO;{$S Characters}PROCEDURE HandleTalk (talkID: Integer);VAR nameOfTalker: String; enoughCharisma: Boolean; resultOfTalk, iconOfTalker, myCharSelected: Integer;BEGIN IF gActiveChars THEN BEGIN myCharSelected := gCharSelected; IF myCharSelected = kNoPCSelected THEN BEGIN myCharSelected := 0; WHILE Mondo[myCharSelected].status[IsDead] AND (myCharSelected <= kNPCReference) DO myCharSelected := myCharSelected + 1; IF myCharSelected > kNPCReference THEN { Should NOT happen } Exit (HandleTalk); END; { Forced talker selection } nameOfTalker := Mondo[myCharSelected].nome; IF gCharSelected < kNPCReference THEN iconOfTalker := Mondo[myCharSelected].icon ELSE { se un NPC non sono certo che l'icona sia una cicn. In uno scenario creato per Dream 1.x sarebbe una icl8. Per prudenza... } iconOfTalker := 0; { Check contro il carisma } enoughCharisma := Mondo[myCharSelected].carisma > Dado (1, 20); resultOfTalk := TalkEngine_Play (talkID, nameOfTalker, iconOfTalker, enoughCharisma, NOT placeData [7]); { Stesso trattamento degli encounter } IF resultOfTalk <> 0 THEN FightingSystem (resultOfTalk, TRUE, talkID); END { if num PC > 0 }END;{$S Magic}FUNCTION RiddleResolutor (riddleID: Integer): Boolean;{ New for v 1.3. Implementa i Riddle.Restituisce TRUE se il gruppo va lasciato transitare per la locazione } CONST rRiddleDialog = 149; rAnswerItem = 3; VAR theRidlHandle: Handle; theRidlDialog: DialogPtr; scanner: Ptr; mayProceed: Boolean; options: BitsInByte; question, answer1, answer2, userAnswer: Str255; i: Integer; results: ARRAY [1..3] OF Integer; empty, myEditText, okButton: Family; ev: EventRecord; LABEL 1; BEGIN IF (riddleID = 0) { Non dovrebbe servire, ma in alcuni vecchi scenari il bit is Riddle settato } OR NOT gActiveChars THEN BEGIN { Un riddle pu˜ ativare un encounter. Se non ci sono personaggiÉÊ} RiddleResolutor := TRUE; Exit (RiddleResolutor); END; { Salvo eccezioni, un riddle non bloccante } mayProceed := TRUE; { Load the Ridl description } theRidlHandle := MyGetResource (resRiddle, riddleID, TRUE, TRUE); scanner := theRidlHandle^; options := GetBBFromRes (scanner); i := GetByteFromres (scanner); { spare } { Maybe the Ridl springs only once and it already did? } IF options[0] { already sprung } THEN Goto 1; question := GetStringFromRes (scanner); answer1 := GetStringFromRes (scanner); answer2 := GetStringFromRes (scanner); { Leggi i tre possibili esiti } FOR i := 1 TO 3 DO results[i] := GetIntegerFromRes (scanner); { Prepara la domanda } ParamText (question, '', '', ''); { Indica che il riddle scattato } IF options [6] THEN BEGIN theRidlHandle^^ := 1; SetHandleSize(theRidlHandle,1); DetachResource (theRidlHandle); WriteRes (currentSavegameFile, riddleID, resRiddle, '', theRidlHandle) END; { Gestisci il dialogo } theRidlDialog := GetNewDialog(rRiddleDialog,NIL,WindowPtr(-1)); IF theRidlDialog = NIL THEN DeathAlert (errMissingApplRes, ResError); SetPort (theRidlDialog); DefaultButton (theRidlDialog, TRUE); ClearFamily (empty); ClearFamily (myEditText); ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; myEditText[rAnswerItem] := TRUE; i := DialogLord (theRidlDialog, rAnswerItem, empty, empty, okButton, empty, empty, empty, myEditText, 30, ev); GetItemText (theRidlDialog, rAnswerItem, userAnswer); DisposeDialog (theRidlDialog); IF IUEqualString (userAnswer, answer1) = 0 THEN BEGIN IF results[1] <> 0 THEN FightingSystem (results[1], TRUE, 0) END ELSE IF (length (answer2) > 0) AND (IUEqualString (userAnswer, answer2) = 0) THEN BEGIN IF results[2] <> 0 THEN FightingSystem (results[2], TRUE, 0) END ELSE BEGIN mayProceed := NOT options [7]; IF results[3] <> 0 THEN FightingSystem (results[3], TRUE, 0) END;1: HUnlock (Handle (theRidlHandle)); RiddleResolutor := mayProceed END;(*** Do Scenario designer Biddings ***){$S Magic}FUNCTION DoScenarioDesignerBiddings: Boolean;TYPE LongintPtr = ^Longint;CONST rAlertNoFreeSpace = 156;VAR addHandle, otherHandle: Handle; addScanner: Ptr; i, j, numResources, resID, aNewID: Integer; justOK: Family; theResType: OSType;PROCEDURE CheckRefToSpelInObj (theObj: Handle; objID: Integer);TYPE IntPtr = ^Integer;CONST kOffsetToSpellField = $0004;VAR iconID, spellID, specifics: Integer; objScanner: Ptr; field: IntPtr;BEGIN {$UNUSED objID} objScanner := StripAddress(theObj^); iconID := GetIntegerFromRes (objScanner); specifics := GetIntegerFromRes (objScanner); spellID := GetIntegerFromRes (objScanner); { If it's a magic scroll... } IF BAnd (specifics, $8020) = $8020 THEN IF spellID = resID THEN BEGIN { Gotcha! } field := IntPtr(Longint(theObj^) + kOffsetToSpellField); field^ := aNewID; ChangedResource (theObj) ENDEND;{ Basato sull'omonima procedura di ScenarioMaker d14 } PROCEDURE CheckObjIcons (theObj: Handle; objID: Integer);TYPE IntHandle = ^IntPtr; IntPtr = ^Integer;VAR idPtr: IntHandle;BEGIN {$UNUSED objID} idPtr := IntHandle(theObj); IF idPtr^^ = resID THEN BEGIN { Gotcha! } idPtr^^ := aNewID; ChangedResource (theObj) ENDEND;{ Basato su CheckSpells di ScenarioMaker d14 } PROCEDURE CheckRefToObjInSpel (theSpel: Handle; spelID: Integer);TYPE IntPtr = ^Integer;CONST kOffsetToCompField = $000D;VAR field: IntPtr;BEGIN {$UNUSED spelID} { Trova il componente materiale } field := IntPtr(Longint(theSpel^) + kOffsetToCompField); IF field^ = resID THEN BEGIN { Gotcha! } field^ := aNewID; ChangedResource (theSpel) ENDEND;{ Basato su CheckSpells di ScenarioMaker d14 } PROCEDURE CheckSpellIcons (theSpel: Handle; spelID: Integer);TYPE IntPtr = ^Integer;CONST kOffsetToIconField = $0012; kOffsetToNameField = $0011;VAR field: IntPtr; j: Byte;BEGIN {$UNUSED spelID} { Trova la lunghezza del nome } j := Ptr(Longint(theSpel^) + kOffsetToNameField)^; { L'icona sta dopo il nome } field := IntPtr(Longint(theSpel^) + kOffsetToIconField + j); IF field^ = resID THEN BEGIN { Gotcha! } field^ := aNewID; ChangedResource (theSpel) ENDEND;PROCEDURE CheckRAMCharSpells (t: TPersonaggio);VAR i: Integer;BEGIN { in memoryÉÊ} FOR i := 1 TO kMaxSpellInMemory DO IF t.spellsInMemory[i] = resID THEN t.spellsInMemory[i] := aNewID; { in spellbookÉ } FOR i := 1 TO t.numKnownSpells DO IF t.knownSpells[i] = resID THEN t.knownSpells[i] := aNewID;END;PROCEDURE ForAllCharsInRAMDoThis (PROCEDURE This (t:TFantasy));VAR i: EntityRef;BEGIN FOR i := numPC-1 DOWNTO 0 DO This (Mondo[i])END;PROCEDURE CheckRAMObjectIcons (t: TItem);BEGIN IF t.icon = resID THEN t.icon := aNewIDEND;PROCEDURE CheckRAMObjIds (t: TItem);BEGIN IF t.id = resID THEN t.id := aNewIDEND;PROCEDURE ForAllObjsInRAMDoThis (PROCEDURE This (t:TItem));VAR i: EntityRef; s: Storage;BEGIN FOR i := numPC-1 DOWNTO 0 DO FOR s := Testa TO Sacco6 DO IF Mondo[i].Equipaggiamento[s] <> NIL THEN This (Mondo[i].Equipaggiamento[s])END;{ Basato sull'omonima procedura di ScenarioMaker d14 } PROCEDURE OpenAllResAndDoThis (theType: ResType ; PROCEDURE This (theHandle: Handle; theID: Integer) );VAR myHandle: handle; numEntities, i, resID: INTEGER; dummy: Str255;BEGIN numEntities := Count1Resources(theType); FOR i := 1 TO numEntities DO BEGIN myHandle := Get1IndResource(theType, i); IF myHandle <> NIL THEN BEGIN GetResInfo(myHandle,resID,theType,dummy); This (myHandle, resID); END; ReleaseResource (myHandle) END;END; PROCEDURE CopyToDB; { Copies the resType-resID resource from the scenario to the Dream DB, resolving ID conflicts } VAR theResource, theConflict: Handle; curResFileID: Integer; BEGIN theResource := MyGetResource (theResType, resID, TRUE, TRUE); { Does a similar resource exist inside the dream DB? } curResFileID := CurResFile; UseResFile (dreamDB.resFork); theConflict := Get1Resource(theResType,resID); UseResFile (curResFileID); IF theConflict <> NIL THEN BEGIN { Yes, a conflict exists! } { Find a different valid ID for the OLD resource } REPEAT aNewID := UniqueID (theResType) UNTIL (aNewID > 999) AND (aNewID < 10000); SetResInfo(theConflict,aNewID,''); ChangedResource (theConflict); { Which resource is causing the conflict? } IF theResType = large1BitMask THEN BEGIN { I must give the same number to icl8 and icl4 too } UseResFile (dreamDB.resFork); theConflict := Get1Resource('icl4',resID); IF theConflict <> NIL THEN BEGIN SetResInfo(theConflict,aNewID,''); ChangedResource (theConflict); END; theConflict := Get1Resource('icl8',resID); IF theConflict <> NIL THEN BEGIN SetResInfo(theConflict,aNewID,''); ChangedResource (theConflict); END; { If the old number was used by some other entity, modify it } OpenAllResAndDoThis ('Spel', CheckSpellIcons); OpenAllResAndDoThis ('Obj ', CheckObjIcons); ForAllObjsInRAMDoThis (CheckRAMObjectIcons); ForAllCharsInRAMDoThis (CheckRAMObjectIcons); { I WON'T check inside Placs and Arens, since those are supposed not to be found inside the dream database } { That's all, folks } UseResFile (curResFileID); END { icons } ELSE IF theResType = 'Spel' THEN BEGIN { If the old number was used by some other entity, modify it } OpenAllResAndDoThis ('Obj ', CheckRefToSpelInObj); ForAllCharsInRAMDoThis (CheckRAMCharSpells); { I WON'T check inside Shops and Nctrs, since those are supposed not to be found inside the dream database } END ELSE IF theResType = 'Obj ' THEN BEGIN { If the old number was used by some other entity, modify it } OpenAllResAndDoThis ('Spel', CheckRefToObjInSpel); ForAllObjsInRAMDoThis (CheckRAMObjIds); { I WON'T check inside Shops and Nctrs, since those are supposed not to be found inside the dream database } END; { The following resource types are not checked, since it is supposed that they won't find a place inside the DREAM DATABASE: Wndr, Mstr, Trap, Shop, Plac, Aren, Mdel, Lelv, XP, Smkr, Nctr } END; { If there was a conflict } { Copy the required resource inside the Dream DB } DetachResource (theResource); WriteRes (dreamDB, resID, theResType, scenarioName, theResource); END; BEGIN (* 0. Preflight the graphical resources needed by the 3D engine (new for 2.1) 1. Parse and execute the AdDB resource 2. Check for conflicting IDs and resolve them 3. Add the ScenarioSignature to the Scen resource *) { 0. Preflight the graphical resources needed by the 3D engine (new for 2.1) } ClearFamily (justOK); justOK[kStdOkItemIndex] := TRUE; IF (FSpCheckObjectLock (currentScenarioFile.FSS) <> noErr) OR (CheckVolLock (NIL, currentScenarioFile.FSS.vrefNum) <> noErr) THEN BEGIN ParamText (currentScenarioFile.FSS.name, '', '', ''); i := AlertLord (rAlertWriteProtected, 1, justOK); DoScenarioDesignerBiddings := FALSE; Exit (DoScenarioDesignerBiddings) END; numResources := CountResources ('clut') - 1; { Sovrastimiamo } IF GetKFreeSpace (currentScenarioFile.FSS.vRefNum) < numResources * 128 THEN BEGIN ParamText (IToS (numResources * 128), currentScenarioFile.FSS.name, '', ''); i := AlertLord (rAlertNoFreeSpace, 1, justOK); DoScenarioDesignerBiddings := FALSE; Exit (DoScenarioDesignerBiddings) END; Engine3D_CreatePalettes (currentScenarioFile.resFork); { 1. Parse and execute the AdDB resource } addHandle := MyGetResource (resDesignOrder, rOrdersID, FALSE, FALSE); IF addHandle <> NIL THEN BEGIN HLock (addHandle); addScanner := addHandle^; { Get number of resources to copy } numResources := GetIntegerFromRes (addScanner); FOR i := 0 TO numResources DO BEGIN theResType := OSType (GetLongintFromRes (addScanner)); resID := GetIntegerFromRes (addScanner); { 2. Check for conflicting IDs and resolve them } CopyToDB END; ReleaseResource (addHandle) END; { 3. Add the scenarioSignature to the Scen resource } { Load scenario list resource in memory } addHandle := MyGetResource (resScenList, rScenListID, TRUE, FALSE); i := GetHandleSize (addHandle); j := SizeOf (scenarioSignature); { Should be 4 } otherHandle := NewHandle (i + j); HLock (addHandle); HLock (otherHandle); { Add it as first, because re-opening is bound to be frequent. This shortens search times } LongintPtr (otherHandle^)^ := Longint (scenarioSignature); BlockMove (addHandle^, Ptr(Longint(StripAddress(otherHandle^))+ j), i); ReleaseResource (addHandle); WriteRes (dreamDB, rScenListID, resScenList, IToS (kCurrentDreamVersion), otherHandle); ReleaseResource (otherHandle); DoScenarioDesignerBiddings := TRUE;END;(******* Bank user interface ***********)VAR groupList: ListInfoRec;{$S DefProcs}PROCEDURE DrawBankList (where: DialogPtr; item: INTEGER);BEGIN {$UNUSED where, item} Ridisegna (groupList);END;{$S LowLevel}PROCEDURE DoWait (mustEat: Boolean);VAR numDead, amountToWaitFor: Integer;BEGIN amountToWaitFor := kMezzanotte - ora + 1; IF placeData[5] { wandering monsters } THEN AddToTranscript ('', ktTheGroup, '', ktWontWait) ELSE BEGIN AddToTranscript ('', ktTheGroup, '', ktWaits); numDead := TimingSystem (amountToWaitFor, mustEat) { Fino a domani } ENDEND;{$S LowLevel}PROCEDURE DragChar (c1, c2: EntityRef);{ Il personaggio che era in posizione c1 passa in posizione c2 }VAR loop: EntityRef; swap: Tpersonaggio; aWindow, movedOnesWindow: WindowPtr;BEGIN { Nel codice seguente bisogna PRIMA trovare la finestra chiamando Personaggio2Window, POI spostare il personaggio nel mondo e INFINE chiamare AdjWindow per modificare il numero del personaggio dentro la window. Questo perchŽ Personaggio2Window va a cercare il numero dentro la window, e se il personaggio giˆ stato spostato dentro il Mondo quando viene chiamato, fa pasticci. } swap := Mondo[c1]; { Il personaggio da spostare } movedOnesWindow := Personaggio2Window (swap); IF c1 > c2 THEN FOR loop := c1 DOWNTO c2+1 DO BEGIN aWindow := Personaggio2Window (Mondo[loop-1]); Mondo[loop] := Mondo[loop-1]; TMSetWRefCon (aWindow, kRefConForID, loop) END ELSE FOR loop := c1 TO c2-1 DO BEGIN aWindow := Personaggio2Window (Mondo[loop+1]); Mondo[loop] := Mondo[loop+1]; TMSetWRefCon (aWindow, kRefConForID, loop) END; Mondo[c2] := swap; TMSetWRefCon (movedOnesWindow, kRefConForID, c2); SetPort (mainWindow); FillRect (groupRect, qd.white); { Questo necessarioÊ} TMInvalRect (groupRect); gCharSelected := c2;END;{$S LowLevel}PROCEDURE SortByS;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].forza < Mondo[j].forza) | ((Mondo[i].forza = Mondo[j].forza) & (Mondo[i].superforza < Mondo[j].superforza)) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByI;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].intelligenza < Mondo[j].intelligenza) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByW;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].saggezza < Mondo[j].saggezza) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByD;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].destrezza < Mondo[j].destrezza) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByCo;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].costituzione < Mondo[j].costituzione) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByCh;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].carisma < Mondo[j].carisma) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE SortByHP;VAR i, j: Entityref;BEGIN FOR i := 0 TO numPC-2 DO FOR j := numPC-1 DOWNTO i DO IF (Mondo[i].HP < Mondo[j].HP) THEN SwapChars (i, j);END;{$S LowLevel}PROCEDURE DoBanking;CONST resBankDialog = 143; rbEverybody = 3; rbCurrentChar = 4; {-----------} rbDeposit = 6; rbWithDraw = 7; {-----------} rbAllGold = 8; rbAllBut100 = 9; rbNoOverload = 10; rbExactly = 11; {-----------} lCharList = 5; teGEamount = 12; statStats = 13; {-----------} kNumItemsInBank = rbExactly; (** IMP **) kNoCharSelected = '-';VAR myDlog: DialogPtr; empty, radio1, { all/one } radio2, { deposit/withdraw } radio3, { some/all/amount } exactAmount, { amount in GE } others: Family; { ok-cancel-list } operationAmount, groupCarries: Longint; { total GE carried } statTextRect, groupListRect: Rect; groupListCell: Cell; buffer, geInBankStr, { Per il paramtext } selectedCharStr, selectedCarriesStr, groupCarriesStr: Str255; charSelected, { Char che compie l'operazione, o -1 per "nessuno" } item, { Item cliccato } i: Integer; { Per il ciclo } oldPort: GrafPtr; { Per salvare } e: EventRecord; { Per DialogLord } { Lettura dei dati } groupAction, { Se true, tutto il gruppo } withdrawAction: Boolean; { Se true, deposita } PROCEDURE BasicBanking; { Sottrae operationAmount GE a selectedChar. Aggiorna le variabili } BEGIN IF charSelected = -1 THEN BEGIN DoSoundAsync (sndImpossible); Exit (BasicBanking) END; { Impedisci i prestiti } IF (operationAmount < 0) AND (abs(operationAmount) > geInBank) THEN operationAmount := -geInBank; { Se il risultato non fa s“ che il PC abbia un num negativo di GP, OK } IF Mondo[charSelected].GP - operationAmount >= 0 THEN BEGIN Mondo[charSelected].GP := Mondo[charSelected].GP - operationAmount; Mondo[charSelected].weightLoad := Mondo[charSelected].weightLoad - operationAmount; geInBank := geInBank + operationAmount; groupCarries := groupCarries - operationAmount; { Adj display } groupCarriesStr := IntegerToLocalString (groupCarries); geInBankStr := IntegerToLocalString (geInBank); selectedCarriesStr := IntegerToLocalString (Mondo[charSelected].GP); ParamText (geInBankStr, selectedCharStr, selectedCarriesStr, groupCarriesStr); InvalRect (statTextRect); END END; PROCEDURE DoDeposit; { Chiama BasicBanking solo se la somma ha un senso ( positiva) } BEGIN IF operationAmount > 0 THEN BasicBanking END; PROCEDURE DoWithdraw; { Chiama BasicBanking solo se la somma ha un senso ( negativa) } BEGIN IF operationAmount < 0 THEN BasicBanking END; PROCEDURE AvoidOverload; VAR maxLoad: Longint; BEGIN { Inizia depositando tuttoÉ Questo mi permette di sapere qual il peso che sto trasportando e di cui non mi posso liberare } operationAmount := Mondo[charSelected].GP; BasicBanking; { Calcola il massimo peso trasportabile senza incorrere in overload } maxLoad := Mondo[charSelected].WA * 9 DIV 10 - 1; { Calcola quanti GP posso portare pur rispettando tale limite } maxLoad := - (maxLoad - Mondo[charSelected].weightLoad); { Se il risultato che il giocatore mi ha detto di depositare e io finisco per incassare, o viceversa, annulla l'operazione; altrimenti continua } IF withdrawAction AND (maxLoad < operationAmount) THEN operationAmount := - operationAmount ELSE IF NOT withdrawAction AND (maxLoad > operationAmount) THEN operationAmount := - operationAmount ELSE operationAmount := maxLoad; { Incassa in modo da averne proprio quel numero } BasicBanking END;BEGIN { Load dialog window } myDlog := GetNewDialog(resBankDialog,NIL,WindowPtr(-1)); IF myDlog = NIL THEN DeathAlert (errMissingApplRes, -192); GetPort (oldPort); SetPort (myDlog); { Init vars } ClearFamily (empty); exactAmount := empty; exactAmount[teGEamount] := TRUE; radio1 := empty; radio1[rbEverybody] := TRUE; radio1[rbCurrentChar] := TRUE; radio2 := empty; radio2[rbDeposit] := TRUE; radio2[rbWithDraw] := TRUE; radio3 := empty; radio3[rbAllGold] := TRUE; radio3[rbAllBut100] := TRUE; radio3[rbNoOverload] := TRUE; radio3[rbExactly] := TRUE; others := radio1; { DialogLord4 permette solo 2 gruppi di radios } others[kStdOkItemIndex] := TRUE; others[kStdCancelItemIndex] := TRUE; others[lCharList] := TRUE; GetItemRect (myDlog, statStats, statTextRect); { Scorri il gruppo, e nello scorrerlo trova quanto posseggono tra tutti e prepara la lista dei personaggi } GetItemRect (myDlog, lCharList, groupListRect); groupList := NuovaLista (myDlog, groupListRect, 1, WantVScroll+WantAutoScroll, LOnlyOne + LNoNilHilite); groupCarries := 0; groupListCell.h := 0; FOR i := 0 TO numPC-1 DO BEGIN groupCarries := groupCarries + mondo[i].GP; buffer := mondo[i].nome; groupListCell.v := i; NuovaCella (groupList, groupListCell, @buffer); END; { Init strings } charSelected := -1; groupCarriesStr := IntegerToLocalString (groupCarries); selectedCharStr := kNoCharSelected; selectedCarriesStr := kNoCharSelected; geInBankStr := IntegerToLocalString (geInBank); ParamText (geInBankStr, selectedCharStr, selectedCarriesStr, groupCarriesStr); { Init radiobuttons banks } SetRadio (myDlog, rbEverybody, kNumItemsInBank, radio1); SetRadio (myDlog, rbDeposit, kNumItemsInBank, radio2); SetRadio (myDlog, rbAllGold, kNumItemsInBank, radio3); { Set the draw procedure for my list. } SetItemProcedure (myDlog,lCharList, @DrawBankList); { Go with DialogLord } DefaultButton (myDlog, TRUE); REPEAT item := DialogLord (myDlog, kNumItemsInBank, radio2, radio3, others, empty, exactAmount, empty, exactAmount, 5, e); SetPort (myDlog); { Bug fix 1.6 } IF radio1[item] THEN SetRadio (myDlog, item, kNumItemsInBank, radio1); { DialogLord4 ne gestisce da solo solo 2 gruppi } CASE item OF lCharList: CASE FindList (@groupList, e, groupListCell, NIL) OF Click, DoubleClick: BEGIN SetRadio (myDlog, rbCurrentChar, kNumItemsInBank, radio1); charSelected := groupListCell.v; IF (charSelected < 0) OR (charSelected > numPC-1) THEN BEGIN charSelected := -1; selectedCharStr := kNoCharSelected; selectedCarriesStr := kNoCharSelected; END ELSE BEGIN selectedCharStr := Mondo[charSelected].nome; selectedCarriesStr := IntegerToLocalString (Mondo[charSelected].GP); END; ParamText (geInBankStr, selectedCharStr, selectedCarriesStr, groupCarriesStr); InvalRect (statTextRect); END { click } END; { case find list of} OK: BEGIN { Tieni presente che stata mutata la situazione } dirty := TRUE; groupAction := (GetRadio (myDlog, kNumItemsInBank, radio1) = rbEverybody); withdrawAction := (GetRadio (myDlog, kNumItemsInBank, radio2) = rbWithdraw); i := GetRadio (myDlog, kNumItemsInBank, radio3); CASE i OF rbAllGold: IF groupAction THEN IF withdrawAction THEN BEGIN operationAmount := - geInBank DIV numPC; FOR charSelected := numPC-1 DOWNTO 0 DO BasicBanking END { group withdraws all } ELSE FOR charSelected := numPC-1 DOWNTO 0 DO BEGIN operationAmount := Mondo[charSelected].GP; BasicBanking END { group deposits all } ELSE IF withdrawAction THEN BEGIN operationAmount := - geInBank; BasicBanking END { Char withdraws all } ELSE IF charSelected >= 0 THEN BEGIN operationAmount := Mondo[charSelected].GP; BasicBanking END; { Char deposits all } rbAllBut100: IF groupAction THEN IF withdrawAction THEN BEGIN operationAmount := - (geInBank-100) DIV numPC; FOR charSelected := numPC-1 DOWNTO 0 DO DoWithdraw END { group withdraws all but 100 } ELSE FOR charSelected := numPC-1 DOWNTO 0 DO BEGIN operationAmount := Mondo[charSelected].GP-100; DoDeposit END { group deposits all but 100 } ELSE IF withdrawAction THEN BEGIN operationAmount := - (geInBank-100); DoWithdraw END { Char withdraws all } ELSE IF charSelected >= 0 THEN BEGIN { Char deposits all } operationAmount := Mondo[charSelected].GP-100; DoDeposit END; { Char deposits all } rbExactly: BEGIN GetItemText (myDlog, teGEamount, buffer); operationAmount := SToI (buffer); IF groupAction THEN IF withdrawAction THEN BEGIN operationAmount := - operationAmount; FOR charSelected := numPC-1 DOWNTO 0 DO DoWithdraw END { group withdraws amount } ELSE FOR charSelected := numPC-1 DOWNTO 0 DO DoDeposit ELSE IF charSelected >= 0 THEN IF withdrawAction THEN BEGIN operationAmount := - operationAmount; DoWithdraw { Char withdraws amount } END ELSE DoDeposit { Char deposits amount } END; { rbExactly } rbNoOverload: IF groupAction THEN FOR charSelected := numPC-1 DOWNTO 0 DO AvoidOverload ELSE IF charSelected >= 0 THEN AvoidOverload END; { case } { Aggiusta i display } IF groupAction THEN FOR i := numPC-1 DOWNTO 0 DO CharacterHasChanged (Mondo[i]) ELSE IF charSelected >= 0 THEN CharacterHasChanged (Mondo[charSelected]) END; { case OK } END; { case item of } UNTIL item = Cancel; { Dealloca la memoria usata } ListaShutdown (groupList); ResetItemProcedure (myDlog,lCharList); { Grazie arrivederci } SetPort (oldPort); DisposeDialog (myDlog);END;{$S Main}PROCEDURE LoadPreferences;VAR foundDirID: longint; prefsFile: MyFile; err, foundVRefNum, prefsVersion, x, y, readBuffer: integer; prefsName: StringHandle; theDesktop: RgnHandle; PROCEDURE ReadWindowPos (w: WindowPtr); VAR winPos: Point; BEGIN ReadLongint (prefsFile, Longint (winPos)); MyMoveWindow (w, winPos, TRUE); END; BEGIN { Load preferences file } { 1. Find prefs folder } IF FindPrefsFolder (foundVRefNum, foundDirID) = noErr THEN BEGIN { 2. Find preferences file } prefsName := StringHandle (MyGetResource ('STR ', rDreamPrefsFile, TRUE, FALSE)); err := FSMakeFSSpec (foundVRefNum, foundDirID, prefsName^^, prefsFile.FSS); ReleaseResource (Handle (prefsName)); CASE err OF fnfErr:; { no prefs file, leave windows in std pos } noErr: BEGIN { 3. Read it } ResetByFSS (prefsFile, prefsFile.FSS); IF prefsFile.errore = noErr then BEGIN { 3.1 Version number } ReadInt (prefsFile, prefsVersion); IF (prefsFile.errore <> noErr) | (prefsVersion < kMinimumFileFormatUnderstood) | (prefsVersion > kGreatestFileFormatUnderstood) THEN BEGIN NewErrorAlert (kAlertNoteAlert, errNoPreferences, prefsFile.errore); Close (prefsFile); Exit (LoadPreferences) END; { 3.2 Preferences } ReadInt (prefsFile, readBuffer); gSoundIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kSound, gSoundIsOn); ReadInt (prefsFile, readBuffer); gSpeechIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kSpeech, gSpeechIsOn); ReadInt (prefsFile, readBuffer); gTranscriptIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kTranscript, gTranscriptIsOn); IF NOT gTranscriptIsOn THEN HideWindow (transcriptWindow); ReadInt (prefsFile, readBuffer); gNotificationIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kNotification, gNotificationIsOn); IF prefsVersion > 150 THEN BEGIN { From 1.5.1, music can be on } ReadInt (prefsFile, readBuffer); gMusicIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kMusic, gMusicIsOn); IF prefsVersion >= 210 THEN BEGIN { From 2.1, music can be on } ReadInt (prefsFile, readBuffer); gQuickTimeIsOn := (readBuffer <> 0); DoCheckItem (kGameMenu, kQuickTime, gQuickTimeIsOn); END; END; { 3.3 Posizione delle window } theDesktop := GetGrayRgn; { For sanity checks } ReadWindowPos(mainWindow); ReadWindowPos(arenaWindow); ReadWindowPos(transcriptWindow); ReadWindowPos(PICTWindow); ReadWindowPos(TEXTWindow); { From version 1.1 onward: size of main window } IF prefsVersion >= 110 THEN BEGIN ReadInt (prefsFile, x); ReadInt (prefsFile, y); TMSizeWindow (mainWindow, x, y, FALSE); ResizeMainWindow (FALSE) END; { From v2.0 onward: verification system } IF prefsVersion >= 200 THEN BEGIN ReadLn (prefsFile, userName); ReadLn (prefsFile, password) END; { From v2.1 onward: 3D preferences } IF prefsVersion >= 210 THEN Engine3D_LoadPrefs (prefsFile); Close (prefsFile); END { IF could open prefs file } ELSE NewErrorAlert (kAlertCautionAlert, errNoPreferences, prefsFile.errore) END; { noErr in finding file } OTHERWISE NewErrorAlert (kAlertCautionAlert, errNoPreferences, err) END { case } END { if prefs file found }END;{$S Main}PROCEDURE SavePreferences;VAR foundDirID: longint; prefsFile: MyFile; err, foundVRefNum: integer; prefsName: StringHandle; winPos: Point; PROCEDURE SaveWinPos (w: WindowPtr); VAR winPos: Point; BEGIN winPos := GiveBackWindowPositionOnScreen (w); WriteLongint (prefsFile, Longint (winPos)) END;begin { Save preferences file } { 1. Find prefs folder } IF FindPrefsFolder (foundVRefNum, foundDirID) = noErr THEN BEGIN { 2. Find preferences file } prefsName := StringHandle (MyGetResource ('STR ', rDreamPrefsFile, TRUE, FALSE)); err := FSMakeFSSpec (foundVRefNum, foundDirID, prefsName^^, prefsFile.FSS); ReleaseResource (Handle (prefsName)); if (err = noErr) | (err = fnfErr) then BEGIN { 3. Write it } RewriteByFSS (prefsFile, prefsFile.FSS, fileTypePreferences, fileTypeAppl); if prefsFile.errore = noErr then begin { 3.1 Version number } WriteInt (prefsFile, kCurrentFileFormatVersion); { 3.2 Preferences } WriteInt (prefsFile, ord(gSoundIsOn)); WriteInt (prefsFile, ord(gSpeechIsOn)); WriteInt (prefsFile, ord(gTranscriptIsOn)); WriteInt (prefsFile, ord(gNotificationIsOn)); WriteInt (prefsFile, ord(gMusicIsOn)); WriteInt (prefsFile, ord(gQuickTimeIsOn)); { 3.3 Window position } SaveWinPos (mainWindow); SaveWinPos (arenaWindow); SaveWinPos (transcriptWindow); SaveWinPos (PICTWindow); SaveWinPos (TEXTWindow); { 3.4 NEW FOR v1.1 - main window size } SetPort (mainWindow); winPos := mainWindow^.portRect.botRight; WriteInt (prefsFile, winPos.h); WriteInt (prefsFile, winPos.v); { 3.5 NEW FOR 2.0 - verification system } WriteLn (prefsFile, userName); WriteLn (prefsFile, password); { 3.6 NEW FOR V2.1: 3D engine preferences } Engine3D_SavePrefs (prefsFile); Close (prefsFile) end; { if could rewrite } END { if FSS made sense } END; { if no error from code before } { Delete temp save file - new for v2.0 } Close (currentSavegameFile); err := FSpDelete (currentSavegameFile.FSS);END;(*********************** User interface, macish things *******************){$S Main}PROCEDURE ClickOnDisclosureTriangle (p:TPersonaggio; w: WindowPtr);BEGIN p.windowIsWide := NOT p.windowIsWide; IF p.windowIsWide THEN TMSizeWindow (w, kCWWidth, kCWHeight, TRUE) ELSE TMSizeWindow (w, kCWWidth, 0, TRUE); { 0 in realtˆ significa "solo la info bar" }END;{$S Main}FUNCTION HandleInfoClick (which: WindowPtr; globalWhere: Point; kind: integer): char;{ Perfettamente analogo a HandleClick, ma chiamato quando il click nella info bar }VAR r: Rect; p: TPersonaggio; localWhere: Point;BEGIN {$UNUSED kind} SetPort (which); localWhere := globalWhere; GlobalToLocal (localWhere); IF Window2Personaggio (which, p) THEN BEGIN { New for v2.0 - handle triangle gadget } SetRect (r, kCWWidth-16, kCWInfoBarHeight-16, kCWWidth, kCWInfoBarHeight); { Spazio ove mettere la icona } IF NOT gHasThemes AND PtInRect (localWhere, r) THEN BEGIN ClickOnDisclosureTriangle (p, which); TMInvalRect (r); { Ridisegna il triangolo, che va invertito } HandleInfoClick := ' '; Exit (HandleInfoClick) END; END ELSE { Deve essere la finestra principale, perchŽ le finestre degli item e quelle dei negozi non hanno info bar } { Se fa clic sulla barra di status, mostro il nome del posto } StatusLine (placeName); HandleInfoClick := ' ' { No user action }END;{$S Main}FUNCTION ClickPoint2Storage (click: Point; w: WindowPtr; classe: TClasse; VAR where:Storage): BOOLEAN;{ dato un punto in coordinate locali in una finestra di personaggio, controllase stato cliccato in un punto in cui viene conservato l'equipaggiamento.Se si, restituisce TRUE e Ñ in where Ñ l'identificativo del punto.Altrimenti restituisce FALSE }VAR loop: Storage; uscita: BOOLEAN;BEGIN IF TMGetWRefCon (w, kRefConForPage) <> 2 THEN BEGIN { Se non sta mostrando la pagina dell'equipaggiamento, esci subito } ClickPoint2Storage := FALSE; Exit (ClickPoint2Storage); END; loop := Testa; { primo punto dello storage } uscita := FALSE; REPEAT IF PtInRect (click, GetIndNrect (rFirstSilhouette+ord (classe), ord(loop))) THEN BEGIN where := loop; ClickPoint2Storage := TRUE; uscita := TRUE END ELSE IF loop = Sacco6 THEN BEGIN { ultimo punto dello storage } uscita := TRUE; ClickPoint2Storage := FALSE END ELSE loop := succ (loop) UNTIL uscitaEND;{$S Main}FUNCTION CheckTransaction (prezzo: integer): BOOLEAN;{ They ask for a monetary transaction. Check that there is a character selected,and that he/she owns enough coins. If he does exitreturning true, else return false. }VAR buyer, companion: TPersonaggio; i, amount: INTEGER;BEGIN { Chi compra ? } IF gCharSelected = -1 THEN BEGIN SellAlert (kNoCharSelected); CheckTransaction := false END ELSE BEGIN buyer := Mondo[gCharSelected]; IF buyer.GP < prezzo THEN BEGIN { L'acquirente non ha soldi sufficienti. Colletta! } i := 0; REPEAT { Non ha senso togliere soldi a noi stessi } IF i = gCharSelected THEN BEGIN i := succ (i); IF i = numPC THEN leave; { Evitiamo un out of bounds } END; companion := Mondo [i]; { Quanti soldi togliamo a costui? } amount := companion.GP; IF amount > prezzo THEN amount := prezzo; companion.GP := companion.GP - amount; companion.weightLoad := companion.weightLoad - amount; buyer.GP := buyer.GP + amount; buyer.weightLoad := buyer.weightLoad + amount; { Passiamo al prossimo membro del gruppo } i := succ (i) UNTIL (i >= numPC) OR (buyer.GP = prezzo); END; { colletta } IF buyer.GP < prezzo THEN BEGIN { Se nonostante la colletta mancano soldi, allora rinuncia } SellAlert (kNoMoney); CheckTransaction := false END ELSE CheckTransaction := true END { if there is a buyer }END;{$S Characters}FUNCTION GetHisMoney (howMuch: Integer): Boolean;{ Controlla che il personaggio possa spendere una certa cifra, usandoChecktransaction. Se pu˜, gliela toglie, restituisce TRUE, ed esce.Altrimenti restituisce FALSE }VAR buyer: TPersonaggio;BEGIN IF CheckTransaction (howMuch) THEN BEGIN IF howMuch > 0 THEN DoSoundAsync (sndCoin); buyer := Mondo[gCharSelected]; buyer.GP := buyer.GP - howMuch; gMoneySpentHere := gMoneySpentHere + howMuch; buyer.weightLoad := buyer.weightLoad - howMuch; CharacterHasChanged (buyer); GetHisMoney := TRUE END ELSE GetHisMoney := FALSEEND;{$S Characters}PROCEDURE RationalizeWindows;{ revised for v2.0 }VAR who: EntityRef; w: WindowPtr; pos: Point; screenRect: Rect; menuBarPlusWindowTitleHeight: Integer;BEGIN { Ricava menuBarPlusWindowTitleHeight } SetPort (mainWindow); SetPt (pos, 0, 0); LocalToGlobal (pos); menuBarPlusWindowTitleHeight := GetMBarHeight + pos.v; { Tolgo la posizione dell'angolo alto della barra del titolo } pos := WindowPeek(mainWindow)^.strucRgn^^.rgnBBox.topLeft; menuBarPlusWindowTitleHeight := menuBarPlusWindowTitleHeight - pos.v; screenRect := qd.screenBits.bounds; { Finestre dei personaggi } FOR who := 0 TO numPC-1 DO BEGIN w := Personaggio2Window (Mondo[who]); IF w <> NIL THEN TMSelectWindow (w) ELSE BEGIN w := CreateCharWindow (who); Mondo[who].charWindow := w END; SetPt (pos, 1, menuBarPlusWindowTitleHeight+(kCWInfoBarHeight+16)*who); MyMoveWindow (w, pos, FALSE); END; { Main window } SetPt (pos, kCWWidth+3, menuBarPlusWindowTitleHeight); MyMoveWindow (mainWindow, pos, FALSE); { Combat window } pos.v := menuBarPlusWindowTitleHeight * 2 - GetMBarHeight + kMWTop; { Lascia intravedere l'orologio } pos.h := screenRect.right - arenaWindow^.portRect.right; { Allineata al bordo dx dello schermo } MyMoveWindow (arenaWindow, pos, FALSE); { Which width & height for main window? } WITH pos DO BEGIN h := screenRect.right - kCWWidth - 7; IF (placeKind = threeD) THEN IF (screenRect.bottom > 600) THEN v := kThreeDDisplayHeight+kThreeDMapHeight ELSE v := kThreeDDisplayHeight ELSE v := h DIV 16 * 9 { proporzione dorata } END; TMSizeWindow (mainWindow, pos.h, pos.v, TRUE); ResizeMainWindow (FALSE); { Transcript, sotto la main window } SetPt (pos, kCWWidth+3, 60+kMWHeight); MyMoveWindow (transcriptWindow, pos, FALSE); TMSizeWindow (transcriptWindow, kStandardWindoidWidth, screenRect.bottom-60-kMWHeight, FALSE); { Text windoid } IF screenRect.right >= kCWWidth+606 THEN { Di fianco al transcript } pos.h := kCWWidth+306 ELSE { Sul bordo destro } pos.h := screenRect.Right - kStandardWindoidWidth; MyMoveWindow (TEXTwindow, pos, FALSE)END;{$S Characters}FUNCTION SellItem (ID, prezzo: INTEGER): BOOLEAN;{ Ha espresso il desiderio di comprare un oggetto. Se la transazione va a buon fine,restituisce TRUE, altrimenti FALSE. Modifica la globale gMoneySpentHere perriflettere quanto il personaggio abbia speso in un posto.QUESTA procedura viene chiamata anche per gestire il grabbing degli oggettiche segue a un combattimento in cui i mostri hanno tesoro }VAR result: BOOLEAN;BEGIN result := false; { Cominciamo col supporre che non ce la faccia } { Chi compra ? E, pu˜ pagare? } IF CheckTransaction (prezzo) THEN BEGIN { CheckTransaction checks that there is a char selected and that he/she owns money enough } result := GiveItemToChar (Mondo[gCharSelected], Sacco1, ID, prezzo > 0, prezzo = 0) & GetHisMoney (prezzo); END; { if CheckTransaction says it's OK to proceed } SellItem := resultEND;{$S Main}FUNCTION ClickOnCharWindow (localWhere, globalWhere: Point; charWindow: WindowPtr; who: TPersonaggio; kind: INTEGER): Char;VAR risultato: char; { Risultato della funzione } placeOfFirstClick, { Dove il personaggio che cede sta tenendo l'oggetto? } placeOfMouseUp: Storage; { Dove il personaggio che riceve vuole l'oggetto? } w: WindowPtr; { Finestra relativa al personaggio che riceve } altroPersonaggio: TPersonaggio; { Personaggio che riceve } oggetto: TItem; { Oggetto cliccato } c: Cell; { Per il caso in cui clicchi su un incantesimo } iconID, page: Integer; {ÊNumero della "pagina" della scheda scelta dall'utente } itemOriginalPos: Rect;BEGIN risultato := ' '; { Salvo eccezioni, va a finire in vaccaÉ } IF NOT gHasThemes AND (localWhere.v < kCWTopOfFreeSpace) THEN BEGIN { Click on button - icons } { A che pagina vuole andare? } page := (localWhere.h - kCWOffsetToIcons) DIV 32 + 1; { Se non uno spellcaster e vuole andare a pag. 3, nisba } IF (NOT (IsSpellCaster (who.classe, who.livello) | (who.Classe = paladino)) & (page = 3)) { Se un mostro e vuole andare a pag. 2, nisba - NEW for v1.3 } | ((who.classe = Mostro) & (page = 2)) { Se ha cliccato oltre pagina tre, ignora } | (page > 4) { Se giˆ a quella pagina, ignora } | (page = TMGetWRefCon (charWindow, kRefConForPage)) THEN BEGIN ClickOnCharWindow := ' '; Exit (ClickOnCharWindow) END; { Passa alla pagina indicata } FlipPage (charWindow, who, page); ClickOnCharWindow := ' '; Exit (ClickOnCharWindow) END; { se siamo a pagina tre, da alla lista modo di reagire } IF TMGetWRefCon (charWindow, kRefConForPage) = 3 THEN BEGIN Hlock (Handle (who)); {$PUSH} {$H-} IF FindList (@who.listData, evento.EventRecord, c, NIL) = DoubleClick THEN risultato := CastSpell (who, c.v); {$POP} HUnlock (Handle (who)) END; { Window2Page (charWindow) = 3 } { Ha cliccato in una finestra relativa a un personaggio, che sta mostrando l'equipaggiamento, e per di pi nel quadratino dove pu˜ apparire un oggetto ?}{1} IF ClickPoint2Storage (localWhere, charWindow, who.classe, placeOfFirstClick) THEN { Si. Ma in quello spazio c' un oggetto? }{1.5} IF (who.equipaggiamento[placeOfFirstClick] <> NIL) THEN CASE kind OF { Si, c'. Lasciamogli manipolare l'oggetto altrove. } 1: BEGIN { Lasciamo che lo trascini dove vuole } itemOriginalPos := GetIndNrect (rFirstSilhouette+ord(who.classe), ord(placeOfFirstClick)); iconID := who.equipaggiamento[placeOfFirstClick].icon; IF iconID < 1000 THEN { Garantisce che sia una Cicn } globalWhere := DragCicn (localWhere, iconID, itemOriginalPos) ELSE globalWhere := DragRect (localWhere, itemOriginalPos); TMPhysicalToLogical (globalWhere); { DragRect restituisce un risultato in coordinate locali. Allora trasformiamoÉ } LocalToGlobal (globalWhere); { In uscita, globalWhere vale il punto dove ha rilasciato il mouse. Ora, se ha rilasciato su una finestra...}{2} IF (FindWindow (globalWhere, w) = inContent) & (w <> NIL) & { E se anche questa la finestra di un personaggio... } Window2Personaggio (w, altroPersonaggio) THEN BEGIN { Éallora focalizzati su questa seconda finestra } SetPort (w); GlobalToLocal (globalWhere); { Bene. Per caso anche questa finestra di un personaggio, e sta mostrando l'equipaggiamento di costui, e il mouse stato trascinato proprio su uno degli spazi riservati all'equi- paggiamento? }{3} IF ClickPoint2Storage (globalWhere, w, altroPersonaggio.classe, placeOfMouseUp) & { Se si, l'oggetto stato lasciato su uno spazio libero? } (altroPersonaggio.equipaggiamento[placeOfMouseUp] = NIL) & { Pu˜ venire spostato nel punto dove lui lo vuole spostare? } who.equipaggiamento[placeOfFirstClick].wearingPlace [Storage2Place (placeOfMouseUp)] & { Stiamo spostando nel sacco? Se si, possiamo procedere. Se stiamo spostando altrove dobbiamo anche testare che questo personaggio possa usare questo oggetto } ((placeOfMouseUp >= Sacco1) | (who.equipaggiamento[placeOfFirstClick].permittedUse [ord(altroPersonaggio.classe)])) & { Infine, l'oggetto ci consente di spostarlo? (Potrebbe essere maledetto, e in questo caso essere bloccato al suo posto) } who.equipaggiamento[placeOfFirstClick].Drop (FALSE, placeOfFirstClick) THEN BEGIN { Tutto OK. Procediamo allo spostamento } altroPersonaggio.equipaggiamento[placeOfMouseUp] := who.equipaggiamento[placeOfFirstClick]; who.equipaggiamento[placeOfFirstClick] := NIL; altroPersonaggio.equipaggiamento[placeOfMouseUp].Grab (altroPersonaggio, placeOfMouseUp >= Sacco1); { Fa s“ che l'oggetto spostato appaia } TMInvalRect ( GetIndNrect (rFirstSilhouette+ord(altroPersonaggio.classe), ord(placeOfMouseUp))); { Costringi anche al ridisegno del rettangolo sorgente. } SetPort (charWindow); TMInvalRect ( GetIndNrect (rFirstSilhouette+ord(who.classe), ord(placeOfFirstClick))); { Bene. Ma se l'oggetto spostato un arma bisogna aggiornare la scheda del proprietario vecchio e nuovoÊ} { 4 } IF altroPersonaggio.equipaggiamento[placeOfMouseUp].data[6] { weapon } OR altroPersonaggio.equipaggiamento[placeOfMouseUp].data[5] {thr. weap. } OR altroPersonaggio.equipaggiamento[placeOfMouseUp].data[3] { ammo. } THEN BEGIN { Forse ho tolto l'arma al primo personaggio. In tal caso, Drop ha giˆ settato la sua wieldedWeapon a Fodero. Ma devo provvedere all'aggiornamento della finestra } WeaponHasChanged (who); { Ho messo un arma al secondo personaggio? } IF ((placeOfMouseUp = manoDx) OR (placeOfMouseUp = ManoSx)) AND NOT altroPersonaggio.equipaggiamento[placeOfMouseUp].data[3] THEN BEGIN altroPersonaggio.wieldedWeapon := placeOfMouseUp; WeaponHasChanged (altroPersonaggio); END; { In pi, se l'arma arma da lancio, allora avvertiamo che servono anche le munizioni } IF altroPersonaggio.equipaggiamento[placeOfMouseUp].data[5] {thr. weap. } & (altroPersonaggio.equipaggiamento[placeOfMouseUp].numCariche = 0) { needs ammo } & NOT CheckAmmunition (altroPersonaggio.equipaggiamento[placeOfMouseUp].ID, altroPersonaggio, FALSE, c.v, c.v, c.v) THEN GenericDreamAlert (kAmmoMissing); END; { if 4 } risultato := '*'; { Ha mossoÉ } END { IF numero 3 }(* ELSE DoSoundAsync (sndImpossible) *) END; { IF numero 2 } END; { CASE 1, singolo click } 2: { Doppio click. Se c' giˆ una finestra con questo oggetto, portala in primo piano. Altrimenti, creala. } BEGIN oggetto := who.equipaggiamento[placeOfFirstClick]; w := oggetto.itemWindow; IF w <> NIL THEN TMSelectWindow (w) ELSE w := CreateItemWindow (oggetto) END; { Doppio clic su un oggetto } END; { Case kind of click } ClickOnCharWindow := risultato;END; { Function }{$S Magic}PROCEDURE TryToIdentifyItem (whatItem: TItem; whoseItem: TPersonaggio; buttonHandle: ControlHandle);VAR itsPosition: Storage; i: EntityRef; j: Integer; found: Boolean;BEGIN { Trova se un caster ha memorizzato kSpelIdentifyAll } i := 0; found := FALSE; REPEAT IF IsSpellCaster (Mondo[i].classe, Mondo[i].livello) THEN BEGIN j := 0; WHILE NOT found AND (j < kMaxSpellInMemory) DO BEGIN j := succ (j); found := (Mondo[i].spellsInMemory[j] = kSpelIdentifyAll); END; { while che cerca l'incantesimo } END; { if spellcaster } IF NOT found THEN i := succ (i) UNTIL found OR (i = numPC); IF found { Si, quindi eseguilo } THEN BEGIN IF CastSpell (Mondo[i], j-1) = 'K' THEN BEGIN { CastSpell vuole un cursore zero-based } { Scopri in che posizione si trova l'oggetto } itsPosition := Testa; WHILE StripAddress(whoseItem.equipaggiamento[itsPosition]) <> StripAddress (whatItem) DO itsPosition := succ (itsPosition); { Esegui l'identificazione } DoIdentify (itsPosition, whoseItem); { Togli il pulsante "Identify" dalla finestra } DisposeControl (buttonHandle); END { se lanciato } END { se trovato } ELSE SpellAlert (kCantIdentify, 0);END;{$S Main}FUNCTION ClickOnControl: Char;CONST rDropAlert = 129; rSellOfferAlert = 130;VAR where: ControlHandle; thePoint: Point; how: Longint; whose: WindowPtr; controlID, amountOffered: Longint; item, itemClone: TItem; proprietario: TPersonaggio; finProprietario: WindowPtr; suoNome, nomeOggetto: String; aStorage: Storage; result: Char; c: Cell; dummy: Boolean; allButtons: Family; page: Integer; BEGIN result := ' '; { salvo contrordine, nessun effetto } { 0, Estrapola i dati dall'event record. Chiama TrackControl per reagire al click } where := ControlHandle (evento.wmTaskData2); whose := WindowPtr (evento.wmTaskData); SetPort (whose); { Bug fix 2.2 } thePoint := evento.wmWhere; GlobalToLocal(thePoint); how := TrackControl(where, thePoint, pointer( - 1)); { Ha moused out of control? } IF how = 0 THEN Exit (ClickOnControl); { 1. Fatti dire dal Control Manager quale pulsante ho premuto. La control handle non mi dice nulla, ma il codice che ha creato il pulsante (dentro ClickOnCharWindow) ha messo un ID del pulsante dentro il refCon } controlID := GetControlReference (where); { Liberiamoci innanzitutto dei casi semplici } CASE controlID OF rButtonBuy: BEGIN IF DammiCella (shopSellingList, c) THEN { amtTilesInMap.right settato da DoSetupShop } WITH itemsForSell [c.v*amtTilesInMap.right+c.h] DO IF (itemPrice > -1) & SellItem (itemID, itemPrice) THEN { bug fix 2.1 } ClickOnControl := '*' ELSE ClickOnControl := ' '; Exit (ClickOnControl) END; rButtonExit, rButtonEnough: BEGIN ClickOnControl := '5'; Exit (ClickOnControl) END; rButtonTake: BEGIN { Se ha indicato sia l'oggetto che il futuro proprietarioÉ } IF DammiCella (shopSellingList, c) THEN WITH itemsForSell [c.v*amtTilesInMap.right+c.h] DO IF (itemPrice > -1) & SellItem (itemID, itemPrice) THEN BEGIN { bug fix 2.1 } itemPrice := -1; SetPort (mainWindow); AggiornaCCella (shopSellingList, c, 0, NIL); END; ClickOnControl := '*'; Exit (ClickOnControl) END; rButtonGetInfo: BEGIN IF DammiCella (shopSellingList, c) THEN WITH itemsForSell [c.v*amtTilesInMap.right+c.h] DO IF itemID > 0 THEN BEGIN New (item); FailNIL (item); item.Init (itemID); item.data[8] := (itemPrice > 0); { Known? } item.itemWindow := CreateItemWindow (item) END; ClickOnControl := '*'; Exit (ClickOnControl) END; { Button get info } rTriangle: BEGIN ClickOnControl := ' '; IF Window2Personaggio (whose, proprietario) THEN { New for v2.2: disclusure triangle in character window } ClickOnDisclosureTriangle (proprietario, whose); Exit (ClickOnControl) END END; { case } IF Window2Personaggio (whose, proprietario) & (proprietario <> NIL) THEN CASE controlID OF rButtonCast: BEGIN { Finestra personaggio, pulsante "cast" } HLock (Handle (proprietario)); {$PUSH} {$H-} IF DammiCella (proprietario.listData, c) THEN {$POP} result := CastSpell (proprietario, c.v); HUnLock (Handle (proprietario)); END; { if button is "cast" } rTabs: BEGIN page := GetControlValue (where); IF (page = 3) AND CharacterGetsNoSpellPage (proprietario) THEN page := 4; FlipPage (whose, proprietario, page); END; END { case, la finestra di un personaggio } ELSE BEGIN { Scopri a che oggetto sto facendo riferimento. Questo facile, perchŽ basta chiedere a TaskMaster } item := TItem (TMGetWRefCon (whose, kRefConForHandle)); nomeOggetto := item.nome; { E chi il proprietario di questa cosina? } proprietario := TPersonaggio(item.owner); suoNome := proprietario.nome; { Bene, adesso procediamo. } CASE controlID OF rButtonUse: BEGIN result := '*'; { Dunque. TItem.Use in alcuni casi ha bisogno di riferire il proprietario, perchŽ (per esempio) stiamo trattando uno scroll e devo far apparire l'icona del caster nella window di LearnSpell. } thisCharacter := proprietario; { Caso speciale: cerca di "usare" del cibo } IF item.data[14] { food } AND NOT item.data[15] { can be used } THEN GenericDreamAlert (kDontUseFood) ELSE BEGIN { Try to use item } IF item.Use THEN ItemIsThrown (item, proprietario, FALSE, FALSE) ELSE ItemHasChanged (item); { Now, a few magic items can cast spells. In that case, on exit from TItem.Use we find the newly cast spell in attackAdditionalInfo.attackingSpell, soÉÊ} IF attackAdditionalInfo.attackingSpell <> NIL THEN result := 'K'; END { do use item } END; rButtonSplit: BEGIN { Cerca un posto dove far apparire lo splitted item } aStorage := Sacco6; dummy := TRUE; WHILE dummy AND NOT ItemCanBeThere (item, proprietario, aStorage) DO IF aStorage > Testa THEN aStorage := Pred (aStorage) ELSE dummy := FALSE; { A questo punto, se dummy = TRUE ho trovato il posto. } IF dummy AND (item.numCariche > 1) THEN BEGIN itemClone := TItem (item.Clone); FailNIL (itemClone); { Aggiorna le caratteristiche delle due metˆ } itemClone.prezzo := itemClone.prezzo DIV 2; itemClone.weight := itemClone.weight DIV 2; itemClone.numCariche := itemClone.numCariche DIV 2; itemClone.itemWindow := NIL; item.prezzo := item.prezzo - itemClone.prezzo; item.weight := item.weight - itemClone.weight; item.numCariche := item.numCariche - itemClone.numCariche; { Aggiorna il personaggio } itemClone.Grab (proprietario, aStorage >= Sacco1); proprietario.weightLoad := proprietario.weightLoad - itemClone.weight; proprietario.equipaggiamento[aStorage] := itemClone; ItemHasChanged (item); finProprietario := Personaggio2Window (proprietario); IF finProprietario <> NIL THEN BEGIN { Se la finestra del proprietario aperta, fa apparire l'oggetto } SetPort (finProprietario); TMInvalRect ( GetIndNrect (rFirstSilhouette+ord (proprietario.classe), ord(aStorage))); END; { Suono } DoSoundAsync (sndSplit); result := '*' END ELSE BEGIN DoSoundAsync (sndImpossible); result := ' ' END; END; rButtonSell: { Siamo in un negozio? Se no, non pu˜ vendere! } IF placeData[3] { is shop } & (placeID <> rEncounterTreasure) THEN BEGIN { Bug fix 1.6. Prima gli diceva che maledetto anche se non l'aveva indossato. Adesso invece controllo. Dove lo tiene? } aStorage := Testa; WHILE StripAddress(proprietario.equipaggiamento[aStorage]) <> StripAddress (item) DO aStorage := succ (aStorage); { Se l'oggetto maledetto E indossato, se lo tiene } IF item.data[0] AND (aStorage < Sacco1) THEN BEGIN SpellAlert (kItemCursed, 0); DoSoundAsync (sndRebound) END ELSE BEGIN DoSoundAsync (sndAttention); { Bug fix 1.6.1 - Workaround per un numeric overflow } amountOffered := item.prezzo; amountOffered := amountOffered * 9 DIV 10; ParamText (IntegerToLocalString (amountOffered), '', '', ''); ClearFamily (allButtons); allbuttons[kStdOkItemIndex] := TRUE; allbuttons[kStdCancelItemIndex] := TRUE; IF AlertLord (rSellOfferAlert, 2, allButtons) = OK THEN BEGIN DoSoundAsync (sndCoin); AddToTranscript (suoNome, ktSells, nomeOggetto, 0); { Venduto! Dagli i soldi } proprietario.GP := proprietario.GP + amountOffered; proprietario.weightLoad := proprietario.weightLoad + amountOffered; { Se era un'arma, aggiorna la scheda equipaggiamento } IF item.data[6]{ weapon } OR item.data[5] { throwing weap. } THEN WeaponHasChanged (proprietario); { Togli l'oggetto di torno } ItemIsThrown (item, proprietario, FALSE, FALSE); CharacterHasChanged (proprietario); END { IF alert - conferma la vendita } END { else di IF item.data[0] - lo vende } END { if in un negozio } ELSE { Non in un negozio } GenericDreamAlert (kCantSell); rButtonDrop: BEGIN DoSoundAsync (sndAttention); ClearFamily (allButtons); allButtons[1] := TRUE; allButtons[2] := TRUE; IF (AlertLord (rDropAlert, 2, allButtons) = 2 {drop}) THEN BEGIN AddToTranscript (suoNome, ktDrops, nomeOggetto, 0); ItemIsThrown (item, proprietario, FALSE, FALSE); END; END; { drop } rButtonIdentify: BEGIN TryToIdentifyItem (item, proprietario, where); IF attackAdditionalInfo.attackingSpell <> NIL THEN result := 'K'; END { identify } END; { Case } END; { Ramo ELSE di IF controlIDÉ - si riferisce a un oggetto } ClickOnControl := resultEND;{$S Main}FUNCTION ClickOnMemberIcon (localPointOfClick: Point; kindOfClick: INTEGER): Char;{ é stata cliccata l'icona di un membro del gruppo nella finestra principale. }VAR iconID: Integer; r: Rect; { Icona da trascinare } who, { Numero caratteristico del personaggio cliccato } newPos: INTEGER; { Nuova posizione in formazione in cui si vuole mettere questo membro del gruppo } w: WindowPtr; { Finestra del personaggio cliccato } mh: MenuHandle; localPt: point;BEGIN who := (localPointOfClick.v - kMWTop) DIV kIconHeight; { Evita di trattare il click su spazio bianco come click valido } IF Mondo[who] = NIL THEN BEGIN { Se click su bianco, selezione nulla } IF gCharSelected > -1 THEN BEGIN { Ha cliccato sullo sfondo. Deseleziona tutto } gCharSelected := -1; TMInvalRect (groupRect); { Impedisci la selezione delle voci del menu personaggio } mh := GetMenuHandle(kCharMenu); DisableItem(mh,0); DrawMenuBar END END { Click valido, su icona di personaggio. } ELSE CASE kindOfClick OF 1: BEGIN { Seleziona - Lo IF evita sfarfallamento } IF who <> gCharSelected THEN BEGIN { Seleziona l'icona } gCharSelected := who; TMInvalRect (groupRect); { Permetti la selezione delle voci del menu personaggio } mh := GetMenuHandle(kCharMenu); EnableItem(mh,0); DrawMenuBar END; { Calcola il boundsRect dell'icona } SetRect(r,0,who*kIconWidth+kMWTop,kIconWidth,(who+1)*kIconHeight+kMWTop); { Permetti di trascinare l'outline } iconID := Mondo[who].icon; IF iconID < 1000 THEN { Garantisce che mia, dunque che CICN } localPt := DragCicn (localPointOfClick, iconID, r) ELSE localPt := DragRect (localPointOfClick, r); TMPhysicalToLogical (localPt); { Ha rilasciato sempre nel gruppo ? } IF NOT PtInRect (localPt, groupRect) THEN BEGIN { No, esci di qui! } ClickOnMemberIcon := ' '; Exit (ClickOnMemberIcon) END; { Sposta la posizione del personaggio nel gruppo se del caso } newPos := (localPt.v - kMWTop) DIV kIconHeight; IF (newPos <> who) & (newPos < numPC) THEN DragChar (who, newPos); END; 2: BEGIN { Doppio click. Se una finestra per questo personaggio esiste giˆ, portala in primo piano. Altrimenti, creala } w := Personaggio2Window (Mondo[who]); IF w <> NIL THEN TMSelectWindow (w) ELSE Mondo[who].charWindow := CreateCharWindow (who); END; { case double click on icon } END; { CASE: singolo o doppio clic? } ClickOnMemberIcon := ' 'END;{$S Main}FUNCTION ClickInMap (startPoint, endPoint: Point; kindOfClick: Integer): Char;{ é stato cliccato sulla mappa delle finestra principale o fight (arena).Vediamo dove }VAR result: Char; dx, dy: INTEGER; { Direzione del movimento. (negativo, positivo o nullo) }BEGIN dy := endPoint.v - startPoint.v; dx := endPoint.h - startPoint.h; CASE dx OF -maxInt..-1: { A sinistra } CASE dy OF -maxint..-1: { In alto } result := '7'; { In alto a sinistra } 0: { Verticalmente immobile } result := '4'; { Direttamente a sinistra } OTHERWISE { In basso } result := '1' { in basso a sinistra } END; { Case dy } 0: { Orizzontalmente immobile } CASE dy OF -maxint..-1: { In alto } result := '8'; { Direttamente in alto } 0: { Immobile } if kindOfClick = 2 THEN result := 'L' ELSE result := ' '; { Sta fermo... } OTHERWISE { In basso } result := '2' { Direttamente in basso } END; { Case dy } OTHERWISE { A destra } CASE dy OF -maxint..-1: { In alto } result := '9'; { In alto a destra } 0: { Verticalmente immobile } result := '6'; { Direttamente a destra } OTHERWISE { In basso } result := '3' { in basso a destra } END; { Case dy } END; { case dx } ClickInMap := resultEND;{$S Main}PROCEDURE HandleClickOn3DMap;{ Da Dream II 2.0d22 }VAR currentScaleValue, valorediScalaIniziale, distanzaIniziale, distanzaPrecedente, newScaleValue, distanza: Longint; centroFinestra, pt: Point;BEGIN currentScaleValue := Environment.MapSize; { Mi faccio dare da Rakku il valore precedente } {Engine3D_ScanMap;} { Permetti di trascinare per ridimensionare la mappa } { Trova dove stia il centro della finestra } WITH centroFinestra DO BEGIN h := kMWLeft + kThreeDMapWidth DIV 2; v := kThreeDDisplayHeight + kThreeDMapHeight DIV 2; END; { Init } SetPort (mainWindow); GetMouse (pt); valorediScalaIniziale := currentScaleValue; newScaleValue := currentScaleValue; { Per il caso in cui non trascini } SubPt (centroFinestra, pt); distanzaIniziale := Abs (pt.v) + Abs (pt.h); distanzaPrecedente := distanzaIniziale; TMBeginDirectDraw (mainWindow); WHILE StillDown DO BEGIN { Distanza tra il punto dove mi trovo e il centro della finestra? } GetMouse (pt); SubPt (centroFinestra, pt); distanza := Abs (pt.v) + Abs (pt.h); { Se ha trascinato, ridisegna } IF distanza <> distanzaPrecedente THEN BEGIN { distanza iniziale : scala iniziale = distanza attuale : scala attuale } newScaleValue := (distanza * valorediScalaIniziale) DIV distanzaIniziale; { refresh video } Engine3D_SetMapSize (LoWrd(newScaleValue)); Engine3D_ScanMap; { Ricorda } distanzaPrecedente := distanza END; END; { while } TMEndDirectDraw (mainWindow); currentScaleValue := newScaleValue;END;{$S Main}FUNCTION HandleClick (which: WindowPtr; globalWhere, startPoint: Point; kind: integer): char;{ Kind vale 1 per click semplice, 2 per doppio click, 3 per triplo clickÉ globalWhere il punto del click in coordinate globali.startPoint la posizione di chi deve muovere, espressa a mo' di battaglia navale, e cio con unitˆ di misura l'icona, e comunque relativa alla finestra (nel caso della finestra principale sempre x = 5 y = 4).Il valore restituito perfettamente analogo a quello di MainEventLoop, e cioil codice dell'azione compiuta dal personaggio, se applicabile, oppure blank. }VAR localWhere, { Punto del click in coordinate locali } endPoint: Point; { Posizione nella finestra in cui ha cliccato, espressa in unitˆ di misura omogenee a quelle di startPoint } questoPersonaggio: TPersonaggio; r: rect; cella: Cell; i: Integer; FUNCTION HandleClickOn3DSpace: Char; VAR whichThirdWasClicked: Integer; BEGIN whichThirdWasClicked := localWhere.h DIV (kThreeDDisplayWidth DIV 3); (* HandleClickOn3DSpace := Chr (whichThirdWasClicked + ord ('7')) *) HandleClickOn3DSpace := '8' END; BEGIN { Mettiamo come port la finestra in cui ha cliccato, cos“ posso disegnarci e fare i conti in coordinate locali nelle routine chiamate da qui in g } SetPort (which); localWhere := globalWhere; GlobalToLocal (localWhere); { Liberiamoci del caso della finestra di un personaggio } IF Window2Personaggio (which, questoPersonaggio) THEN BEGIN IF gGameRunning THEN HandleClick := ClickOnCharWindow (localWhere, globalWhere, which, questoPersonaggio, kind) ELSE HandleClick := ' '; exit (HandleClick); END; { E su che finestra abbiamo cliccato, di bello? } CASE TMGetWRefCon (which, 1) OF kArenaWindowRefCon: BEGIN WITH localWhere DO BEGIN endPoint.v := v DIV 32; { Qs. sistema di coord. one-based } endPoint.h := h DIV 32 { Vedi FightCoord2ArenaRect } END; HandleClick := ClickInMap (startPoint, endPoint, kind); END; kMainWindowRefCon: BEGIN { Ha cliccato nella finestra principale } IF placeData [3] & (PtInRect (localWhere, paneRect)) THEN BEGIN { We are displaying a shop. This is a special case! } CASE FindList (@shopSellingList, evento.EventRecord, cella, NIL) OF Click: BEGIN { We must change the offer in the window } r := paneRect; r.top := r.bottom - kBottomSpaceForButtons; r.right := mainWindow^.portRect.right - 387 + 219; { Get Info btn comincia a 220 secondo le rez } (* EraseRect (r); { Funziona solo perchŽ in vigore TMBeginDirectDraw } *) TMInvalRect (r); HandleClick := ' ' END; DoubleClick: BEGIN { Quale oggetto ha cliccato? } i := cella.v*amtTilesInMap.right+cella.h; WITH itemsForSell [i] DO IF (itemPrice > -1) THEN BEGIN { Controlla che non l'abbia giˆ preso } IF SellItem (itemID, itemPrice) & { Prendilo } (itemPrice = 0) THEN BEGIN { e se gratis... } { Lascio uno spazio bianco } iconID := 0; itemPrice := -1; AggiornaCCella (shopSellingList, cella, 0, NIL); END; { Preso, fa passare un minuto } HandleClick := '*'; END ELSE { No joy, non c' niente nella cella } HandleClick := ' '; END; OTHERWISE HandleClick := ' ' END; { case } TMEndDirectDraw (mainWindow); Exit (HandleClick) END; { IF } { Gestisci il caso di un click sull'icona di un personaggio nella finestra principale } IF PtInRect (localWhere, groupRect) THEN HandleClick := ClickOnMemberIcon (localWhere, kind) ELSE BEGIN { main pane } IF placeKind = threeD THEN BEGIN IF localWhere.v > kThreeDDisplayHeight THEN BEGIN HandleClickOn3DMap; HandleClick := ' ' END { map subpane } ELSE { 3D subpane } HandleClick := HandleClickOn3DSpace; END { if 3D } ELSE WITH localWhere DO BEGIN { Click sulla mappa 2D. Trova cosa contiene il punto che stato cliccato } { In qs. sistema di coord, il gruppo a x = 5 y = 4, e cio startPoint vale h = 5 v = 4. } endPoint.v := (v - kMWTop) DIV 32; endPoint.h := (h - kMWLeft) DIV 32; HandleClick := ClickInMap (startPoint, endPoint, kind); END; { 2D } END { click in main window pane } END; { Finestra principale } OTHERWISE { Ne finestra main, n fight, n personaggiÉ oggetto! } HandleClick := ' '; { Il click su una finestra di oggetto non ha effetto } END { case }END; { proc }{$S Magic}PROCEDURE BuySpell (casterName: String; what, price: INTEGER);VAR buyer: TPersonaggio;BEGIN IF CheckTransaction (price) THEN BEGIN { CheckTransaction checks that there is a char selected and that he/she owns money enough } buyer := Mondo[gCharSelected]; { Dato che non incantesimo fatto da personaggio, qui ci sono diverse scorciatoie rispetto all'analogo HandleSpell. Innanzitutto non chiamiamo CastSpell ma suo il suo equivalente di basso livello, DoCastSpellÉÊ} IF GetHisMoney (price) & { Qui sotto la pos. del buyer ininfluente } (DoCastSpell (what, kCasterLevelForItems, casterName, buyer.formazione) <> NIL) THEN BEGIN { Époi, invece di passare dal TargetSystem, forziamo il bersaglio all'acquirente dell'incantesimoÉ } attackAdditionalInfo.target := buyer; attackAdditionalInfo.targetRef := gCharSelected; attackAdditionalInfo.groundZero := Individual; { É a questo punto possiamo rivolgerci allo SpellSystem per la risoluzione dell'incantesimo. } SpellSystem2; END; END; { If there's a buyer selected }END;{$S Magic}PROCEDURE DoLeaveCharAtInn;BEGIN { Aggiungi in lista } Mondo[gCharSelected].nextChar := listaPersInLocanda; listaPersInLocanda := Mondo[gCharSelected]; { Ricordati dove l'abbiamo lasciato } listaPersInLocanda.whereAmI.h := placeID; { Ricordati anche quando l'abbiamo lasciato } listaPersInLocanda.whereAmI.v := giorno; { Togli dalle scatole } DoKillChar (gCharSelected, FALSE)END;{$S Magic}PROCEDURE DoRetakeCharAtInn;CONST resInnDialog = 148; pbDoIt = 1; pbCancel = 2; lCharList = 3; statGEPrice = 5; kNumItemsInInn = 3; kMaxClientsForInn = 8;TYPE CharInfo = RECORD id, lodgedFromDay: Integer END;VAR myDlog: DialogPtr; buttons, nothing: Family; listRect, billRect: Rect; c: Cell; oldPort: GrafPtr; e: EventRecord; { Per dialoglord } theBill, numCharsHere, item: Integer; hisName, temp1, temp2: Str255; host, loop: TPersonaggio; nameSelected: Boolean;BEGIN { Load dialog window } myDlog := GetNewDialog(resInnDialog,NIL,WindowPtr(-1)); IF myDlog = NIL THEN DeathAlert (errMissingApplRes, -192); GetPort (oldPort); SetPort (myDlog); { Toolbox, get ready } ParamText ('', '', '', ''); DisableDialogItem (myDlog, pbDoIt); DefaultButton (myDlog, FALSE); { Init vars } ClearFamily (nothing); buttons := nothing; buttons[pbDoIt] := TRUE; buttons[pbCancel] := TRUE; buttons[lCharList] := TRUE; GetItemRect (myDlog, statGEPrice, billRect); GetItemRect (myDlog, lCharList, listRect); groupList := NuovaLista (myDlog, listRect, 1, WantVScroll+WantAutoScroll, LOnlyOne + LNoNilHilite); c.h := 0; c.v := 0; { Trova quali personaggi risiedono a questa locanda } host := listaPersInLocanda; WHILE host <> NIL DO BEGIN IF host.whereAmI.h = placeID THEN BEGIN hisName := host.nome; NuovaCella (groupList, c, @hisName); c.v := c.v + 1 END; host := host.nextChar END; { while } numCharsHere := c.v - 1; { Memorizza il numero di celle effettive (zero-based)} { Dialog lord! } nameSelected := FALSE; REPEAT item := DialogLord (myDlog, kNumItemsInInn, nothing, nothing, buttons, nothing, nothing, nothing, nothing, 0, e); IF item = lCharList THEN BEGIN CASE FindList (@groupList, e, c, NIL) OF Click, DoubleClick: IF c.v <= numCharsHere THEN BEGIN { Senza l'if bomba quando si clicca sullo spazio bianco } nameSelected := TRUE; (* Trova da quanto qui, e chiedi il conto *) { Scorri la lista sino a trovarlo } host := listaPersInLocanda; WHILE (c.v >= 0) AND (host.whereAmI.h <> placeID) DO BEGIN IF host.whereAmI.h = placeID THEN c.v := c.v - 1; { Ne abbiamo scorso uno } host := host.nextChar END; { Trova quando arrivato e desumi da quanto qui } theBill := giorno - host.whereAmI.v + 1; GetIndString(temp1,rBarmanStrings,rThatllBe); GetIndString(temp2,rBarmanStrings,rGEOnly); ParamText (temp1, Itos (theBill), temp2, ''); EnableDialogItem (myDlog, pbDoIt); END ELSE nameSelected := FALSE; OTHERWISE nameSelected := FALSE END; { case } IF NOT nameSelected THEN BEGIN ParamText ('', '', '', ''); DisableDialogItem (myDlog, pbDoIt) END; InvalRect (billRect) END; { if clicked the list of guests } UNTIL item <= pbCancel; { Shutdown dialog - il personaggio selezionato sempre in host } SetPort (oldPort); ListaShutdown (groupList); DisposeDialog (myDlog); { Se ha scelto di farlo unire al gruppoÉ } IF item = OK THEN BEGIN { 1. Verifica che tutto funzioni } IF BeforeJoin (host.classe) THEN IF GetHisMoney (theBill) THEN BEGIN gActiveChars := gActiveChars OR (host.HP > 0); Mondo[numPC] := host; numPC := numPC+1; IF NOT gActiveChars THEN CharsHere; { Bug fix 2.1 } AfterJoin (host.allineamento); { 3. Rimuovi host dalla lista di coloro che stanno in locanda } IF host = listaPersInLocanda THEN listaPersInLocanda := host.nextChar ELSE BEGIN { trova il precedente } loop := listaPersInLocanda; WHILE loop.nextChar <> host DO loop := loop.nextChar; { Rimuovilo dalla lista } loop.nextChar := host.nextChar; END END ELSE BarmanTalks (rGoAwayNoMoney) ENDEND;{$S Magic}PROCEDURE DoLodge;CONST goldPerPersonPerNight = 1;VAR stays, dummy: Boolean; i: Integer; BEGIN REPEAT stays := GetHisMoney (goldPerPersonPerNight * numPC); IF stays THEN BEGIN { Un giorno passa e recuperano un punto vita } DoSoundAsync (sndSleeps); DoWait (FALSE); HandleRest (FALSE); { Non mangiano razioni, gli dˆ da mangiare l'oste } { Dato che riposano in alloggio, do altri 3 punti a testa. Se a questo punto sono tutti sani, escono. Altrimenti, restano per un'altra notte } stays := FALSE; FOR i := numPC-1 DOWNTO 0 DO IF NOT Mondo[i].status[IsDead] THEN BEGIN dummy := HPChange (Mondo[i], 3); stays := stays OR (Mondo[i].HP < Mondo[i].maxHP) END; IF NOT stays THEN { Tutti sani, vanno via } BarmanTalks (rGoodByeCustomers) END { If he can pay and stays } ELSE { Non hanno soldi, il barman li caccia } BarmanTalks (rGoAwayNoMoney) UNTIL NOT stays;END;{$S Magic}PROCEDURE DoBarman;{ Hanno chiesto di parlare col barista! }VAR okButton: Family; chejjedico, i: Integer; frase: Str255;{ Convenzione: per ogni bar esiste nello scenario una risorsa con lo stesso IDdel place che contiene tre rivelazioni in ordine di importanza. }BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; CASE gMoneySpentHere OF 0: chejjedico := 1; 1..11: chejjedico := 2; OTHERWISE chejjedico := 3; END; GetIndString (frase, placeID, chejjedico); ParamText (frase, '', '', ''); i := AlertLord (rBarmanAlert, 1, okButton);END;{$S Magic}PROCEDURE LetUserCastSpell;{ L'utente pu˜ premere 'K' sulla tastiera, o scegliere Cast da menu, per indicareche il personaggio corrente vuole fare un incantesimo. In questo caso bisognalasciargli scegliere l'incantesimo tra quelli disponibili, usando la finestradel personaggio.Questa procedura crea la finestra se ancora non esiste, la porta in primopiano, e seleziona la pagina degli incantesimi, di modo che l'utente possaselezionare l'incantesimo con la massima comoditˆ DË PER SCONTATO CHE gCharSelected SIA INDICE DI PERSONAGGIO ESISTENTE }VAR laFinestra: WindowPtr;BEGIN IF IsSpellCaster (Mondo[gCharSelected].classe, Mondo[gCharSelected]. livello) THEN BEGIN laFinestra := Personaggio2Window (Mondo[gCharSelected]); IF laFinestra = NIL THEN laFinestra := CreateCharWindow (gCharSelected) ELSE TMSelectWindow (laFinestra); IF TMGetWRefCon (laFinestra, kRefConForPage) <> 3 THEN FlipPage (laFinestra, Mondo[gCharSelected], 3); END { if is spellcaster } ELSE DoSoundAsync (sndImpossible)END;{$S Main}FUNCTION ScegliMenu (menu, voce: Integer): char;{ Se l'utente sceglie di uscire mette gQuit a TRUE }CONST rDreamAbout = 128; { resID del PICT di about di dream } rScenarioAbout = 1128;VAR dummy, isReset: boolean; action: char; s: Str255; allButtons: Family; numDead: Integer; PROCEDURE HandleSell (itemToSell, qty, price: Integer); BEGIN action := '*'; WHILE (qty > 0) & SellItem (itemToSell, price) DO qty := pred (qty) END; BEGIN action := ' '; { Tranne eccezioni, nessuna azione } CASE menu OF kAppleMenu: BEGIN CASE voce OF kAboutDream: CustomAbout (rDreamAbout); kAboutScenario: CustomAbout (rScenarioAbout); END; { case } { Free memory. This won't be called often... } {$IFC MAC68K} UnloadSeg (@CustomAbout) {$ENDC} END; { kAppleMenu } kFileMenu: case voce of kOpenGame: DoLoadGame; kClose: MyDisposeWindow (TMFrontWindow); kSave: DoSaveGame (FALSE); kSaveAs: DoSaveGame (TRUE); kPageSetup: ; kPrint: ; kQuit: gQuit := true end; { case File menu } kGameMenu: CASE voce OF kPauseGame: BEGIN { Metti o togli il segno di spunta } DoCheckItem (menu, voce, gGameRunning); { Setta la globale correttamente } gGameRunning := NOT gGameRunning; { Se esce dalla pausa, resetta il display } IF gGameRunning THEN StatusLine ('') END; kSound: BEGIN gSoundIsOn := NOT gSoundIsOn; DoCheckItem (menu, voce, gSoundIsOn) END; kSpeech: BEGIN gSpeechIsOn := NOT gSpeechIsOn; { Bug fix 1.6: se partiva disattivo, non inizializzavaÉ } IF gSpeechIsOn THEN GalateaStartup ELSE GalateaShutdown; DoCheckItem (menu, voce, gSpeechIsOn) END; kTranscript: BEGIN gTranscriptIsOn := NOT gTranscriptIsOn; ShowHide (transcriptWindow, gTranscriptIsOn); DoCheckItem (menu, voce, gTranscriptIsOn) END; kNotification: BEGIN gNotificationIsOn := NOT gNotificationIsOn; DoCheckItem (menu, voce, gNotificationIsOn) END; kMusic: BEGIN gMusicIsOn := NOT gMusicIsOn; DoCheckItem (menu, voce, gMusicIsOn); IF gMusicIsOn THEN ChooseAndStartTheMusic ELSE QTMusicStop END; kQuickTime: BEGIN gQuickTimeIsOn := NOT gQuickTimeIsOn; DoCheckItem (menu, voce, gQuickTimeIsOn) END; kShuffleWindows: RationalizeWindows { New for 1.6 } END; { menu game } kGroupMenu: CASE voce OF kSearch: action := 'L'; kRest: action := 'R'; kFormation: BEGIN GetItemMark (GetMenuHandle (menu), voce, action); { uso action come temp buffer } isReset := (action = chr(noMark)); { Metti o togli il segno di spunta } DoCheckItem (menu, voce, isReset); IF isReset THEN FightingSystem (resFormation, TRUE, 0) ELSE AddToTranscript ('', ktTheGroup, '', ktChangesFormation); action := 'F'; end; kWait: action := 'W'; kByS: SortByS; kByI: SortByI; kByW: SortByW; kByD: SortByD; kByCo: SortByCo; kByCh: SortByCh; kByHP: SortByHp; END; { case group menu } kCharMenu: CASE voce OF kCast: BEGIN LetUserCastSpell; action := ' '; END; kDismiss: IF gCharSelected = 8 THEN GenericDreamAlert (kWontDismiss) ELSE BEGIN DoSoundAsync (sndAttention); { Metti il suo nome nell'alert } s := Mondo[gCharSelected].nome; ParamText (s, '', '', ''); { Chiedi conferma } ClearFamily (allButtons); allbuttons[kStdOkItemIndex] := TRUE; allbuttons[kStdCancelItemIndex] := TRUE; IF AlertLord (rDismissAlert, 2, allButtons) = OK THEN BEGIN action := '*'; { Liberati di lui } DoKillChar (gCharSelected, TRUE); END ELSE action := ' '; END; kShoot: action := 'T' END; { char menu } kBarMenu: BEGIN action := '*'; CASE voce OF kIronRations: IF NOT SellItem (kItmIronRations, 5) THEN action := ' '; kStdRations: IF NOT SellItem (kItmRations, 1) THEN action := ' '; kLightDrink: IF GetHisMoney (2) THEN; kStrongDrink: IF GetHisMoney (5) THEN; kTalk: DoBarman; kLodge: IF gActiveChars THEN DoLodge; kLeaveChar: IF gCharSelected > -1 THEN DoLeaveCharAtInn; kTakeChar: IF numPC <= kMaxCharInUI THEN BEGIN { Go ahead only if there's a selected char } IF CheckTransaction (1) THEN DoRetakeCharAtInn END ELSE GenericDreamAlert (kRosterFull); kAskRanger: Join (Ranger); { Case ask a ranger to join } END; { case voce of } END; { kBarMenu } kTempleMenu: BEGIN action := '*'; GetIndString (s, rMiscStrings, rHighPriest); CASE voce OF kCLW: BuySpell (s, kSpelCureGraze, 50); kCSW: BuySpell (s, kSpelCureWound, 100); kCCW: BuySpell (s, kSpelCureGash, 200); kCAW: BuySpell (s, kSpelCureAll, 400); kCI: BuySpell (s, kSpelCureIllness, 10); kCP: BuySpell (s, kSpelCurePoison, 50); kRestore: BuySpell (s, kSpelRestore, 300); kRaiseDead: BuySpell (s, kSpelRaise, 500); kRemoveCurse: BuySpell (s, kSpelRemoveCurse, 1000); kAskCleric: Join (Chierico); END; { case voce ofÉ } END; { kTempleMenu } kThreeDMenu: BEGIN CASE voce OF kHighDetail: BEGIN Engine3D_SetDetail (E3D_DetailHigh); DoCheckItem (kThreeDMenu, kHighDetail, TRUE); DoCheckItem (kThreeDMenu, kLowDetail, FALSE); DoCheckItem (kThreeDMenu, kVarDetail, FALSE); END; kLowDetail: BEGIN Engine3D_SetDetail (E3D_DetailMedium); DoCheckItem (kThreeDMenu, kHighDetail, FALSE); DoCheckItem (kThreeDMenu, kLowDetail, TRUE); DoCheckItem (kThreeDMenu, kVarDetail, FALSE); END; kVarDetail: BEGIN Engine3D_SetDetail (E3D_DetailVariable); DoCheckItem (kThreeDMenu, kHighDetail, FALSE); DoCheckItem (kThreeDMenu, kLowDetail, FALSE); DoCheckItem (kThreeDMenu, kVarDetail, TRUE); END; kClouds: BEGIN GetItemMark (GetMenuHandle (menu), voce, action); { uso action come temp buffer } isReset := (action = chr(noMark)); { Inverti il segno di spunta } DoCheckItem (menu, voce, isReset); { Engine 3D fa il lavoro } Engine3D_ActivateClouds (isReset); end; { mip map } k3DMipMap: BEGIN GetItemMark (GetMenuHandle (menu), voce, action); { uso action come temp buffer } isReset := (action = chr(noMark)); { Inverti il segno di spunta } DoCheckItem (menu, voce, isReset); { Engine 3D fa il lavoro } Engine3D_ActivateMipMap (isReset); end; { mip map } kLensFlare: BEGIN GetItemMark (GetMenuHandle (menu), voce, action); { uso action come temp buffer } isReset := (action = chr(noMark)); { Inverti il segno di spunta } DoCheckItem (menu, voce, isReset); { Engine 3D fa il lavoro } Engine3D_ActivateLensFlare (isReset); end; { mip map } END; { case voce of } action := ' '; END; { kThreeDMenu } kComp1Menu: CASE voce OF kBuyOne: HandleSell (kItmAmber, 1, 50); OTHERWISE HandleSell (kItmAmber, (voce-1)*5, 50); END; kComp2Menu: CASE voce OF kBuyOne: HandleSell (kItmBatGuano, 1, 1); OTHERWISE HandleSell (kItmBatGuano, (voce-1)*5, 1); END; kComp3Menu: CASE voce OF kBuyOne: HandleSell (kItmCurePotion, 1, 25); OTHERWISE HandleSell (kItmCurePotion, (voce-1)*5, 25); END; kComp4Menu: CASE voce OF kBuyOne: HandleSell (kItmGlassLens, 1, 5); OTHERWISE HandleSell (kItmGlassLens, (voce-1)*5, 5); END; kComp5Menu: CASE voce OF kBuyOne: HandleSell (kItmMagnet, 1, 2); OTHERWISE HandleSell (kItmMagnet, (voce-1)*5, 2); END; kComp6Menu: CASE voce OF kBuyOne: HandleSell (kItmMushRoom, 1, 1); OTHERWISE HandleSell (kItmMushRoom, (voce-1)*5, 1); END; kComp7Menu: CASE voce OF kBuyOne: HandleSell (kItmQuickSilver, 1, 25); OTHERWISE HandleSell (kItmQuickSilver, (voce-1)*5, 25); END; kComp8Menu: CASE voce OF kBuyOne: HandleSell (kItmMirror, 1, 100); OTHERWISE HandleSell (kItmMirror, (voce-1)*5, 100); END; kComp9Menu: CASE voce OF kBuyOne: HandleSell (kItmHolyWater, 1, 5); OTHERWISE HandleSell (kItmHolyWater, (voce-1)*5, 5); END; kBrothelMenu: CASE voce OF kSpendNight: action := 'R'; kRations: IF SellItem (kItmRations, 1) THEN action := '*'; kAskThief: Join (Ladro); END; { case voce of brothel } kBankMenu: CASE voce OF kDeposit : IF gActiveChars THEN DoBanking; kAskFighter: Join (Combattente) END; { case voce of bank } kMageTower: BEGIN action := '*'; CASE voce OF kComp1: dummy := SellItem (kItmAmber, 100); kComp2: dummy := SellItem (kItmBatGuano, 1); kComp3: dummy := SellItem (kItmCurePotion, 100); kComp4: dummy := SellItem (kItmGlassLens, 5); kComp5: dummy := SellItem (kItmMagnet, 10); kComp6: dummy := SellItem (kItmMushRoom, 2); kComp7: dummy := SellItem (kItmQuickSilver, 25); kComp8: dummy := SellItem (kItmMirror, 200); kAskMage: Join (Mago) END; { case voce of tower } END; { tower } kShrine: CASE voce OF kPray: BEGIN gMoneySpentHere := succ (gMoneySpentHere); { Per ricordarm che ha pregato } AddToTranscript ('', ktGroupPrays, '', 0); numDead := TimingSystem (60, TRUE); IF (gMoneySpentHere DIV 100 > 0) THEN { Se ha anche fatto un offerta } AddToTranscript ('', ktPaladinIsNear, '', 0) ELSE AddToTranscript ('', ktPaladinEyesYou, '', 0); END; kOffer: IF GetHisMoney (100) THEN BEGIN AddToTranscript ('', ktOffersGold, '', 0); IF (gMoneySpentHere MOD 100 > 0) THEN AddToTranscript ('', ktPaladinIsNear, '', 0) ELSE AddToTranscript ('', ktPaladinEyesYou, '', 0); END; kAskPaladin: IF (gMoneySpentHere MOD 100 > 0) & (gMoneySpentHere DIV 100 > 0) THEN Join (Paladino) ELSE BEGIN DoSoundAsync (sndImpossible); AddToTranscript ('', ktNoPaladinNearby, '', 0); END; END; { case voce of shrine } kSpecialMenu: CASE voce OF kTalkStranger: BEGIN KillText; TextOut (placeID, TRUE) END; kJoinStranger: IF Mondo[kNPCReference] = NIL THEN Join (Mostro) ELSE GenericDreamAlert (kRosterFull) END; { case voce of special menu } end; { case barra dei menu } HiliteMenu (0); ScegliMenu := action end;{$S Main}FUNCTION GameAction (timeForAction: integer): boolean;{ Chiamata quando l'utente vuole compiere una azione.Se pu˜ farlo (il gioco attivo) la funzione restituisce true, aggiornal'orologio e prende nota di quando ha agito.Altrimenti restituisce false.Se ci troviamo in un place che contiene wandering monsters, GameAction nepermette la comparsa.GameAction, inoltre, ha il compito essenziale di porre la variabile booleanaglobale "dirty" a true ogni volta che restituisce true. Questo informa il modulodi I/O (in DreamIo.p) che il gioco stato modificato e va salvato. }CONST kMovesBetweenWMCheck = 6;VAR s: Str255; numDead: Integer;BEGIN lastMove := TickCount; IF gGameRunning THEN BEGIN numDead := TimingSystem (timeForAction, TRUE); { Check for wandering monsters } gRoamingCount := succ (gRoamingCount); IF gRoamingCount >= kMovesBetweenWMCheck THEN BEGIN gRoamingCount := 0; IF placeData[5]{ place has wandering monsters} & gActiveChars & (Dado (1, 100) <= gPercentWM) THEN DoWanderingMonsters (placeID); END; GameAction := TRUE END ELSE BEGIN GetIndString(s, rUserIntfStrings, kGameIsStopped); StatusLine (s); GameAction := FALSE END;END;{$S Main}PROCEDURE HandleActivation (whose: WindowPtr; on: INTEGER);VAR p: TPersonaggio; fileMenuHandle: MenuHandle;BEGIN { Mantieni aggiornato lo status del menu "close window" - new for v2 } fileMenuHandle := GetMenuhandle (kFileMenu); CASE TMGetWRefCon (TMFrontWindow, 1) OF { Can be closed? } kTranscriptWindoidRefCon, kHandicappedHelper, kCharacterRefCon, kItemRefCon: EnableItem (fileMenuHandle, kClose) OTHERWISE DisableItem (fileMenuHandle, kClose); END; { case } IF (StripAddress (whose) = StripAddress (mainWindow)) & placeData [3] THEN { La lista degli item dello shop che ha bisogno di qs. evento } LActivate (on=1, shopSellingList.theList); IF Window2Personaggio (whose, p) & (TMGetWRefCon (whose, kRefConForPage) = 3) THEN BEGIN Hlock (handle (p)); {$PUSH} {$H-} LActivate (on=1, p.listData.theList); {$POP} HUnlock (Handle (p)) END;END;{$S Main}FUNCTION MainEventLoop (whereAmI: point): Char;{ Main event loop chiamato da main game loop (durante gli spostamenti delgruppo) e dalle istanze di TPersonaggio.Move (durante i combattimenti),e deve restituire un codice di azione se applicabile.I codici restituiti sono:¥ ' ': non ha compiuto nessuna azione.¥ cifre 1..9: vuole muoversi nella direzione indicata (MainEventLoop mappa a questi caratteri anche le mosse fatte con il mouse e quelle con le frecce). é per la gestione di questa funzione che si rende necessario passare la posizione attuale del chiamante.¥ '0': Ha perso tempo per un minuto¥ 'R': Il giocatore vuole che i personaggi riposino.¥ 'L': Il giocatore vuole cercare (search).¥ 'W': Il giocatore vuole attendere l'indomani (wait).¥ 'T': Il giocatore vuole lanciare un'arma da lancio (throw)Le azioni come la visione dell'equipaggiamento di un personaggiosono viste dal sistema come "non azioni", eMEL le effettua in tutta trasparenza per l'ambiente di gioco restituendo ' '.Se il gioco fermo (in pausa), MEL non lascia effettuare nessuna azionee restituisce sempre " ".Se il giocatore sceglie "esci" dal menu "archivio", MEL pone gQuit a true.Questo codice prende una mossa, se disponibile, da moveBuffer. L“ finiscono,infatti, le mosse che mi arrivano tramite evento Apple.}CONST rFirstWatchSmallIcon = 300; rGameIsStoppedIcon = 308;var err: OSErr; result: Char; s: str255; i: Longint; threeDnpcMet: Integer; p: TPersonaggio;begin SetPort (mainWindow); IF gGameRunning THEN BEGIN i := (TickCount - lastMove) DIV 75; IF i > 7 THEN i := 7; { Bug fix 1.6 per impedire che appaiano icone insensate } { Fai apparire l'orologino che dˆ il tempo della mossa } err := PlotIconID (watchRect, atNone, ttNone, rFirstWatchSmallIcon + i); END { if game running } ELSE err := PlotIconID (watchRect, atNone, ttNone, rGameIsStoppedIcon); CASE TaskMaster(everyEvent, NIL, evento) OF nullEvt: BEGIN InitCursor; { One-stop end to wait cursor - bug fix 1.6 } QTMusicIdle; WITH evento DO BEGIN { Ha ridimensionato la main window? } IF (WindowPtr (evento.wmTaskData2) = mainWindow) & ((wmTaskData = wInGrowRgn) | (wmTaskData = wInZoom)) THEN ResizeMainWindow (FALSE) END; { with } { Alla partenza, se ci arriva un evento Apple con un savegame o almeno uno scenario da aprire siamo a cavallo. Altrimenti dobbiamo interagire con l'utente. Questo il codice che se ne occupa, Per i commenti del caso, le convenzioni e gli algoritmi vedere DoLoadGame dentro DreamIO. } IF (currentScenarioFile.resFork = 0) & (moveBuffer[1] <> '°') THEN BEGIN DoLoadGame; { Fallito? Allora esci! } IF (currentScenarioFile.resFork = 0) THEN gQuit := TRUE; lastMove := TickCount; END; IF length (moveBuffer) = 0 THEN result := ' ' { No user action } ELSE BEGIN { C' una mossa per noi nel move buffer. Oh, God, I'm so excited } result := moveBuffer[1]; Delete (moveBuffer, 1, 1) END; { Gestione del display nella status line } IF TickCount - 300 { 5 sec } > tickleTime THEN StatusLine (''); { Animazione 3D } IF placeKind = threeD THEN BEGIN TMBeginDirectDraw (mainWindow); IF gGameRunning AND (movingGuy = NIL) THEN { No combat, we may meet NPCs } threeDnpcMet := Engine3D_Scan (RotateAll) ELSE { Combat or game stoppedÉ } threeDnpcMet := Engine3D_Scan (DoNothing); {$IFC POWERPC} IF (placeKind = threeD) AND (TickCount - gLast3DMapRefresh > 30) THEN BEGIN Engine3D_ScanMap; { Rinfresca la mappa del luogo 3D a video } gLast3DMapRefresh := TickCount; { Last refresh of map is now } END; {$ENDC} TMEndDirectDraw (mainWindow); IF threeDnpcMet <> 0 THEN { Talking monster met. Invoke talk and then encounter, if any } HandleTalk (threeDnpcMet); { In uscita potremmo non essere in luogo 3D, ma in treasure dispatching place } END; { if place is 3D } END; { Idle } keyDown: BEGIN result := chr(BAnd(evento.wmTaskData, charCodeMask)); CASE result OF { Map arrow keys to number keys } chUp: result := '8'; chDown: result := '2'; chLeft: if placeKind = threeD then result := '7' else result := '4'; chRight: if placeKind = threeD then result := '9' else result := '6'; chEscape: { Mappa ESC sull'evento "pausa" } result := ScegliMenu (kGameMenu, kPauseGame); 'a'..'z': result := chr (BAnd (ord(result), $FFDF)) { Make it uppercase } END; IF result = 'K' THEN BEGIN result := ' '; { 'K' riservato per quando l'incantesimo viene lanciato } IF (gCharSelected > -1) THEN LetUserCastSpell; END; { if 'K' } { Avoid control keys, which are used internally } IF (result < ' ') OR (result > 'Z') THEN result := ' '; {$IFC POWERPC} IF (placeKind = threeD) AND (TickCount - gLast3DMapRefresh > 30) THEN BEGIN TMBeginDirectDraw (mainWindow); Engine3D_ScanMap; { Rinfresca la mappa del luogo 3D a video } gLast3DMapRefresh := TickCount; { Last refresh of map is now } TMEndDirectDraw (mainWindow); END {$ENDC} END; activateEvt: BEGIN { In wmTaskData la window attivata o deattivata. In wmTaskdata2 il flag di attivazione } HandleActivation (WindowPtr (evento.wmTaskData), LoWrd (evento.wmTaskdata2)); { In OS8 la main pane delle char win un control e si ridisegna quando si attiva/disativa. Dunque devo ridisegnare quel che ci stava sopra } IF gHasThemes AND Window2Personaggio (WindowPtr (evento.wmTaskData), p) THEN BEGIN SetPort (WindowPtr (evento.wmTaskData)); TMInvalRect (WindowPtr (evento.wmTaskData)^.portRect); END; result := ' ' END; wInContentRgn: { wmTaskdata is the window where click happened wmWhere is the point in global coord } WITH evento DO result := HandleClick (WindowPtr (wmTaskData), wmWhere, whereAmI, wmClickCount); wSuspend: BEGIN InitCursor; KillSoundChannel; { Leave the sound hardware to other appls } gInBackground := true; {HandleActivation (FrontWindow, 0);} CallMeAtSuspend; result := ' ' { No user action } end; wResume: BEGIN gInBackground := false; {HandleActivation (FrontWindow, 1);} SetupSoundChannel; CallMeAtResume; result := ' ' { No user action } END; wInInfo: WITH evento DO result := HandleInfoClick (WindowPtr (wmTaskData), wmWhere, wmClickCount); wInSpecial, wInMenuBar: result := ScegliMenu (HiWrd(evento.wmTaskData), LoWrd(evento.wmTaskData)); wInGoAwayRgn: BEGIN { Chiude una finestra. OK, no problem } MyDisposeWindow (WindowPtr (evento.wmTaskData)); result := ' ' END; wInControl: WITH evento DO BEGIN { E' un mio pulsante o una scrollbar del list manager? } GetControlTitle (ControlHandle(wmTaskData2), s); IF Length (s) > 0 THEN { Pulsante. Gestiscilo } result := ClickOnControl ELSE { Scrollbar. Passalo come click semplice, ci pensa HandleClick } result := HandleClick (WindowPtr (wmTaskData), wmWhere, whereAmI, wmClickCount); END; kHighLevelEvent: begin result := ' '; err := AEProcessAppleEvent (evento.eventRecord); if err <> noErr then NewErrorAlert (kAlertStopAlert, 0, err) end; OTHERWISE { other events, like activate } result := ' ' END; { Se passa un minuto tempo di gioco (10 sec. reali) dall'ultima mossa, perde il turno} IF (result = ' ') & (TickCount - lastMove >= 600) THEN BEGIN { memory check, new for v2.1 } IF FreeMem < $00010000 THEN NewErrorAlert (kAlertStopAlert, errOutOfmemory, -108); { Advance game time by one minute } i := longint (GameAction (1)); END; { restituisci l'esito del main event loop } MainEventLoop := resultend;(*********************** THE CORE OF THE DREAM *******************){$S UtilInit}PROCEDURE GameInit;VAR maxHeight, maxWidth: Integer;BEGIN { Buffer delle mosse } moveBuffer := ''; { Per TaskMaster } evento.wmClickCount := 0; evento.wmTaskMask := tmMenuKey+ { Handle menu keys } tmUpdate+ { Update windows } tmFindW+ { Call FindWindow } tmMenuSel+ { May call menuselect } tmOpenNDA+ { May access apple menu } tmSysClick+ { May switch } tmDragW+ { May drag windows } tmContent+ { May activate windows } tmClose+ { May close windows } tmZoom+ { May zoom windows } tmGrow+ { May grow windows} tmScroll+ { May scroll windows } tmSpecial+ { May handle edit menu } tmCRedraw+ { May redraw controls } { tmInactive May allow selection of inactive menu items } tmInfo+ { Don't activate window on click on info bar } { tmContentControls+ May call FindControl } { tmControlKey May pass keys to controls } { tmControlMenu May pass keys to menus } tmMultiClick+ { Handles multiple clicks } tmIdleEvents+ { Passes idle events to controls } tmDoDiskMount+ { May format bad floppies } tmMultiFinder; { May pass back suspend & resume } ; { Get the main window. } mainWindow := GetNewCWindow(rMainWindow+ord(gHasThemes)*1000, nil, nil); if mainWindow = nil then DeathAlert (errMissingApplRes, resNotFound); maxHeight := 10000; { Verrˆ cambiato al primo DoLoadPlace } maxWidth := 10000; TMNewWindow (mainWindow, fInfoBar+fGrow, kMainWindowRefCon, maxHeight, maxWidth, maxHeight, maxWidth, kMainWindowMinHeight, kMainWindowMinWidth, 0, 0, 0, 0, kStatusLineHeight, { Info bar height } DrawStatusLine, DrawMainWindow, NIL); SetPort(mainWindow); { set window to current graf port } { Init Script Manager-related utilities inside BinIO } InitializeDefaultNumberSeparators; LoadPreferences; ShowWindow (mainWindow); IF NOT Verifica THEN PresentaRichiestaRegistrazione; { Go go go } gGameRunning := TRUEEND;{$ Main}PROCEDURE NPCMoveAftermath;{ Da chiamare dopo che una mossa stata eseguita e ha provocatoun cambiamento di place, per consentire agli NPC di reagire }BEGIN { New for v1.3: Handle NPCs } WITH npcData DO IF (Mondo [kNPCReference] <> NIL) & (placeID = placeForExit) THEN BEGIN TextOut (talkOnExit, TRUE); IF nctrForExit <> 0 THEN FightingSystem (nctrForExit, TRUE, 0); DoKillChar (kNPCReference, TRUE); END;END;{$S Main}FUNCTION PeopleFlying: Boolean;VAR i: Integer; result: Boolean;BEGIN result := NOT morePlaceData [15] AND { Don't fly indoors } gActiveChars; { At least one char to be flying } FOR i := numPC-1 DOWNTO 0 DO result := result AND Mondo[i].status[IsFlying]; IF result THEN BEGIN DoSoundAsync (sndFlying); AddToTranscript ('', ktTheGroup, '', ktFlies) END; PeopleFlying := resultEND; {$S Main}PROCEDURE TrapManagement (trapID: Integer);{ New for version 1.1 }TYPE TrapHandle = ^TrapPtr; TrapPtr = ^Trap; Trap = PACKED RECORD baseDmg, numDice, diceSize: Integer; { damage } spellID: Integer; textIfSprung, textIfFound: Integer; options: BitsInByte; { [7] Ruffian can disarm [6] Won't spring if group flying [5] Won't spring if group invisible [4] Springs only once [3] Don't tell it's a trap (new for v2) [0] Was already sprung } soundId: Integer END;VAR theTrapHandle: TrapHandle; ruffianName: String; damage: Integer; group: EntityRef; onePoint: Point; allChars: EntityRef; someOneDied: Boolean; FUNCTION FindRuffian: Integer; { Looks inside the group for a ruffian, and give back his/her experience level. Or zero if none } VAR allChars: EntityRef; result: Integer; BEGIN result := 0; FOR allChars := numPC-1 DOWNTO 0 DO IF (Mondo[allChars].classe = Ladro) & (Mondo[allChars].Livello > result) THEN BEGIN result := Mondo[allChars].Livello; ruffianName := Mondo[allChars].nome END; FindRuffian := result END;BEGIN { Load the trap description } theTrapHandle := TrapHandle (MyGetResource (resTrap, trapID, TRUE, TRUE)); WITH theTrapHandle^^ DO BEGIN { Maybe the trap springs only once and it already did? } IF (options[4] AND options[0]) OR { Or maybe they fly above the trap? } (options [6] AND PeopleFlying) THEN Exit (TrapManagement); { Can the player disarm the trap? } IF options[7] & { Did he disarm the trap? } (Dado (1, 20) <= FindRuffian) THEN BEGIN { Yes, simply say so } AddToTranscript (ruffianName, ktDisarmsTrap, '', 0); TextOut (textIfFound, TRUE) END ELSE BEGIN { The trap was sprung. Act accordingly } IF NOT options [3] THEN AddToTranscript ('', ktTheGroup, '', ktSpringsTrap); TextOut (textIfSprung, TRUE); { New for v1.6 - handle sounds } IF soundId <> 0 THEN DoSoundAsync (soundId); { Assess damage } damage := baseDmg + Dado (numDice, diceSize); { Inflict damage } someOneDied := FALSE; IF damage <> 0 THEN FOR group := numPC-1 DOWNTO 0 DO BEGIN IF HPChange (Mondo[group], -damage) THEN BEGIN someOneDied := TRUE; KillAllSpells (Mondo[group]) END; CharacterHasChanged (Mondo[group]); END; { if damage was given } IF someOneDied THEN SomeoneDiedCheckForActiveChars; { Invoke spell if needed } Longint(onePoint) := 0; IF (spellID <> 0) & (DoCastSpell (spellID, kCasterLevelForItems, '', onePoint) <> NIL) THEN WITH attackAdditionalInfo DO BEGIN target := NIL; targetRef := -1; { new for 1.3.5 } IF groundZero <> Caster THEN groundZero := AllGroup; { Fa in modo che siano tutti nell'area } FOR allChars := numPC-1 DOWNTO 0 DO Mondo[allChars].whereAmI := onePoint; { Esegui } SpellSystem2 END; { if spell was cast and deployed } END; { if trap sprung } IF options[4] { Springs only once } THEN BEGIN options[0] := TRUE; DetachResource (Handle (theTrapHandle)); WriteRes (currentSavegameFile, trapID, resTrap, '', Handle (theTrapHandle)); END; { Mark as done } END; { with } HUnlock (Handle (theTrapHandle));END;{$S Main}FUNCTION MayIExploreThere (x, y: Integer; VAR direzione: Char): TExplorationResult;{ Restituisce TRUE se il giocatore pu˜ muoversi nel luogo indicato }VAR destinationLoc: MapLocation; cantMove: Boolean; dummy1: TPersonaggio; dummy2: Storage; j: Integer;BEGIN { Calcola una volta per tutte l'indice del posto dove dovr˜ andare } destinationLoc := placeMap^[x+(y-1)*placeW]; { Hide the previous pict, if any } IF direzione <> '0' THEN BEGIN KillPict; { Se in un negozio, qualsiasi movimento mi fa uscire } IF placeData [3] { negozio } THEN BEGIN DoLoadPlace (-1, '5'); MayIExploreThere := noYouCant; Exit (MayIExploreThere) END; { Can he move in the place he wishes to reach? } { 1. If exiting the bounds, he pops (new for v1.3) } IF (x < 1) | (y < 1) | (x > placeW) | (y > placeH) THEN BEGIN DoLoadPlace (-1, direzione); MayIExploreThere := noYouCant; Exit (MayIExploreThere) END; { If in bounds then they can move if the location is passable or if they are flying } cantMove := destinationLoc.characteristics[7] & NOT PeopleFlying; { If he could go there, but there's a riddle, check the riddle (new for v1.3) } IF NOT cantMove & destinationLoc.characteristics[4] THEN cantMove := NOT RiddleResolutor (destinationLoc.encounter); IF cantMove THEN BEGIN { No, he can't. Sound to signal the error } IF GameAction (1) THEN DoSoundAsync (sndBump); MayIExploreThere := noYouCant; Exit (MayIExploreThere) END; END { if direzione <> '0' } ELSE { Mappa il segnale di perdita di tempo nella mossa nulla } direzione := '5'; { Eccetto che nel caso del negozio, evitiamo di processare un '5'. In caso contrario restare fermi su un negozio produrrebbe un continuo dentro e fuori } IF direzione = '5' THEN BEGIN j := Integer (GameAction (1)); AddToTranscript ('', ktTheGroup, '', 5); MayIExploreThere := noYouCant; Exit (MayIExploreThere); END; IF GameAction (placeTime) THEN BEGIN { Caso speciale: serve un oggetto per entrare qui. Per i commenti vedere le note all'inizio di LowLevel.p } dummy1 := NIL; { = Cerca in tutto il gruppo } IF (destinationLoc.itemNeeded <> 0) THEN BEGIN cantMove := NOT CheckItemNeeded (destinationLoc.itemNeeded, dummy1, dummy2); IF cantMove THEN TextOut (destinationLoc.textToShow, TRUE); END { If there is an item needed } ELSE BEGIN { No item is needed } TextOut (destinationLoc.textToShow, TRUE); cantMove := FALSE END; END { if GameAction } ELSE cantMove := TRUE; { We are pausing, don't move } IF cantMove THEN MayIExploreThere := noYouCant ELSE IF (destinationLoc.encounter <> 0) OR (destinationLoc.bringsTo <> 0) THEN MayIExploreThere := yesButStopInThere ELSE MayIExploreThere := yesYouMayExploreEND;PROCEDURE DoExploreMove (dx, dy: Integer; direzione: Char);{ Da chiamare nei luoghi non-3D quando l'utente vi si reca }VAR myUpdateRgn: RgnHandle; i, j, nearPlaceIndex: Integer; dummy: Boolean; physicalPaneRect: rect;BEGIN AddToTranscript ('', ktTheGroup, '', ord (direzione) - ord ('0')); SetPort (mainWindow); IF placeKind <> threeD THEN BEGIN { AlllloraÉ se io scrollo la finestra ho buona parte del lavoro giˆ fatto. Per˜ devo invalidare lo spazio dov'ero (per far sparire il simbolo del gruppo), il punto dove vado (per farlo apparire) e forse anche altri punti. Per esempio, nella mappa sotto, muovendomi dalla locazione "1" alla locazione "2", le locazioni indicate con "¡" vanno disegnate se prima non le vedevo. ¡ ¡ ¡ ¡ 2 . . ¡ . 1 . . . . In LowLevelInit ho definito vicinityRect come i rettangoli con il gruppo e le otto locazioni circostanti. A questo punto ne faccio uso. } { Du du dunque. Riempiamo la vecchia update rgn } dummy := TMDoUpdateStuff (mainWindow, 0); { Ora passiamo allo scrolling } physicalPaneRect := paneRect; TMLogicalToPhysical (physicalPaneRect.topLeft); TMLogicalToPhysical (physicalPaneRect.botRight); myUpdateRgn := NewRgn; CloseRgn(myUpdateRgn); ScrollRect(physicalPaneRect,-dx*32,-dy*32,myUpdateRgn); InvalRgn (myUpdateRgn); { E non TM..., perchŽ stiamo giˆ lavorando su coord fisiche } { Redraw always the old and new position, so that the old symbol disappears and the new symbol appears } TMInvalRect (vicinityRect [0, 0]); TMInvalRect (vicinityrect [-dx, -dy]); { - perchŽ giˆ scrollato } { Redraw the smallish square where the grow box used to be } CopyRgn (tmAuxRecordHandle(WindowPeek(mainWindow)^.refCon)^^.wScrollBarsSpace, myUpdateRgn); {$PUSH} {$R-} {$OV-} OffsetRgn(myUpdateRgn,-BSL(dx,5),-BSL(dy,5)); {$POP} InvalRgn (myUpdateRgn); DisposeRgn (myUpdateRgn); { Change group pos in global coord } groupX := groupX + dx; groupY := groupY + dy; { Mark the locations nearby as being known } FOR i := - 1 TO 1 DO IF (groupY + i > 0) & (groupY + i <= placeH) THEN FOR j := - 1 TO 1 DO IF (groupX + j > 0) & (groupX + j <= placeW) THEN BEGIN nearPlaceIndex := groupX+j+(groupY+i-1)*placeW; IF NOT placeMap^[nearPlaceIndex].characteristics[0] THEN BEGIN placeDirty := true; placeMap^[nearPlaceIndex].characteristics[0] := TRUE; TMInvalRect (vicinityRect [j,i]); AddToCache (groupX+j, groupY+i, nearPlaceIndex) END END; { Tell the graphic engine that the position has changed } ScrollWorld; ENDEND;PROCEDURE ExploreMoveAftermath (dx, dy: Integer; direzione: Char; forceExitFromPlace:Boolean);{ Esplora le conseguenze di un movimento, come una trappola }VAR placeIndex, i: Integer; allButtons: Family; threeDCoords: DoublePoint; exitWasForcedByDesigner: Boolean;BEGIN { Calcola una volta per tutte l'indice del posto dove mi trovo ora } placeIndex := groupX+(groupY-1)*placeW; { Check for special happenings } IF gActiveChars AND (placeMap^[placeIndex].encounter <> 0) THEN BEGIN { Trap } IF placeMap^[placeIndex].characteristics[5] THEN BEGIN TrapManagement (placeMap^[placeIndex].encounter); { esco, perchŽ questo potrebbe avermi portato in un altro posto, come il posto, via teleport o chissacchŽ. } Exit (ExploreMoveAftermath) END; { Talk, new for v 2.0 } IF placeMap^[placeIndex].characteristics[3] THEN BEGIN HandleTalk (placeMap^[placeIndex].encounter); Exit (ExploreMoveAftermath) END; { 3D Event, new for v2 } IF placeMap^[placeIndex].characteristics[2] AND (placeKind = threeD) THEN BEGIN Engine3D_LoadEvent (placeMap^[placeIndex].encounter); Engine3D_StartEvent; Exit (ExploreMoveAftermath) END; { Encounter } IF NOT placeMap^[placeIndex].characteristics[6] AND NOT placeMap^[placeIndex].characteristics[4] THEN BEGIN FightingSystem (placeMap^[placeIndex].encounter, TRUE, 0); { Se fuggito fallo tornare indietro di un passo } IF numChars = 0 THEN BEGIN groupX := groupX - dx; groupY := groupY - dy; IF placeKind <> threeD THEN BEGIN { Move back group symbol on screen} SetPort (mainWindow); TMInvalRect (paneRect); END ELSE BEGIN { Alex interface as of 30/8/96 } threeDCoords.h := groupX; threeDCoords.v := groupY; i := Engine3D_SetViewPoint (threeDCoords, FALSE) END; END; { Se c' stato un combattimento esco, perchŽ questo potrebbe avermi portato in un altro posto, come il posto per la spartizione del tesoro. } Exit (ExploreMoveAftermath) END; END; { Check if "goto other place" space } i := placeMap^[placeIndex].bringsTo; IF forceExitFromPlace THEN i := -1; { Engine3D richiede un POP - forzane l'esecuzione } exitWasForcedByDesigner := (placeMap^[placeIndex].bringsTo = -1); { Quattro casi: -1: un normale pop. zero: non porta da nessuna parte. 1000..1999: porta da un'altra parte pienamente accessibile 2000+ : porta in un posto accessibile solo con congruo numero di personaggi.} IF (i <> 0) THEN BEGIN IF (i >= 2000) THEN BEGIN { In questi posti entra solo se ha personaggi vivi. DreamMonsters fa ritornare in superficie il gruppo di morti, quindi questo non limitativo, nel senso che non blocca sottoterra un gruppo di morti } { Too few characters? } IF NOT gActiveChars THEN BEGIN GenericDreamAlert (kNoCharacters); Exit (ExploreMoveAftermath) END; IF numPC < minCharNumber THEN BEGIN DoSoundAsync (sndAttention); ParamText (ItoS (numPC), IToS (maxCharNumber), '', ''); ClearFamily (allButtons); allbuttons[kStdOkItemIndex] := TRUE; allbuttons[kStdCancelItemIndex] := TRUE; IF AlertLord (rTooFewPCsAlert, 2, allButtons) = Cancel THEN Exit (ExploreMoveAftermath); END { If too few chars } END; { If place ID is >= 2000 } IF exitWasForcedByDesigner THEN DoLoadPlace (i, '5') ELSE DoLoadPlace (i, direzione) END; { If place brings somewhere }END;{$S Main}Procedure MainGameLoop;VAR Direzione: Char; PROCEDURE HandleMove (dx, dy: INTEGER); VAR where: Point; dummy: Integer; actualDx, actualDy, actualDirection: Integer; forceExitfromPlace: Boolean; BEGIN IF placeKind <> threeD THEN BEGIN IF MayIExploreThere (groupX+dx, groupY+dy, direzione) <> noYouCant THEN BEGIN DoExploreMove (dx, dy, direzione); ExploreMoveAftermath (dx, dy, direzione, FALSE) END END ELSE BEGIN { 3D place } forceExitfromPlace := FALSE; { Pass 3D code the keypress } where.h := ord (direzione) - ord ('0'); { side effect: where is new group coords } actualDirection := Engine3D_SetViewDirection (where); IF actualDirection <> 0 THEN BEGIN { Stiamo uscendo da un place. Il motore 3D mi restituisce il punto cardinale verso cui ci muoviamo } direzione := chr (ord ('0') + actualDirection); forceExitfromPlace := TRUE END ELSE BEGIN SetPort (mainWindow); TMBeginDirectDraw (mainWindow); { Do graphics. See if they can do a step that way } dummy := Engine3D_Scan (DonTCare); TMEndDirectDraw (mainWindow); END; { OK. Test if it really amounted to a location change } actualDx := where.h - groupX; actualDy := where.v - groupY; { Move group counter, if so } groupX := where.h; groupY := where.v; { Aftermath. If it's a fighting, allow the game to step the group back } IF (actualDx <> 0) or (actualDy <> 0) or (forceexitfromplace) THEN ExploreMoveAftermath (actualDx, actualDy, direzione, forceExitfromPlace) END; END; { proc HandleMove } PROCEDURE HandleSpell; BEGIN If SpellCheck (FALSE) THEN IF TargetSystem (FALSE, maxint) THEN SpellSystem2; END; BEGIN repeat REPEAT { Sanity check. Non dovrebbe servire, maÉÊ} Direzione := MainEventLoop (amtTilesInMap.topLeft) UNTIL (placeMap <> NIL) | gQuit | (direzione = '°'); CASE direzione OF ' ': ; { Non vuole muovere } '8': HandleMove ( 0, -1); '1': HandleMove (-1, 1); '2': HandleMove ( 0, 1); '3': HandleMove ( 1, 1); '4': HandleMove (-1, 0); '0', '5': HandleMove (0, 0); { Tipicamente significa "voglio uscire di " } '6': HandleMove ( 1, 0); '7': HandleMove (-1, -1); '9': HandleMove ( 1, -1); '*': IF GameAction (1) THEN ; { Mossa generica che impegna un minuto } 'L': IF (placeMap^[groupX+(groupY-1)*placeW].encounter > 0) & gActiveChars & placeMap^[groupX+(groupY-1)*placeW].characteristics[6] THEN BEGIN AddToTranscript ('', ktTheGroup, '', ktFindsSomething); FightingSystem (placeMap^[groupX+(groupY-1)*placeW].encounter, TRUE, 0); END ELSE AddToTranscript ('', ktTheGroup, '', ktFindsNothing); 'R': HandleRest (TRUE); 'K': BEGIN HandleSpell; { Bug fix 1.7 per far passare il minuto } IF GameAction (1) THEN ; END; 'F': ; { Ha cambiato formazione. Bug fix 1.6 per evitare buzz } 'W': DoWait (TRUE); chr(7)..chr(14): { Il codice inferiore mi chiede di uccidere un personaggio } DoKillChar (ord(direzione)-7, TRUE); { Bug fix 2.2: ogni tanto un chr(0) si fa strada nel moveBuffer } '°': DoSwitchScenario; OTHERWISE DoSoundAsync (sndImpossible) END; { case } until gQuitEND;{$S Main}Procedure All;BEGIN MaxApplZone; { expand the heap so code segments load at the top } LowLevelInit; { Init Macintosh, then TaskMaster } EngineInit; { Init graphic engine } HiLevelInit; { Init game "world" } CharactersInit; { Init group & other creatures } MonstersInit; { Init monsters and fighting system } IOInit; { File I/O unit init } GameInit; { My own initialization }{$IFC MAC68K} UnloadSeg(@LowLevelInit);{$ENDC} { Programma } TMSelectWindow (mainWindow); { Questo garantisce che la finestra sia correttamente evidenziata } REPEAT MainGameLoop UNTIL IOShutDown; { True if user confirms shutdown } { Grazie e arrivederci } SavePreferences; MonstersShutdown; CharactersShutDown; HiLevelShutdown; EngineShutdown; LowLevelShutdownEND; { proc } end.