-
Notifications
You must be signed in to change notification settings - Fork 1
/
HiLevel.p
1412 lines (1302 loc) · 41.7 KB
/
HiLevel.p
1
Unit HiLevel;InterfaceUses Types, QuickDraw, { List 2 } Controls, Events, Memory, OSUtils, SegLoad, { List 3 - needs List 1/2 types } Files, { needs OSUtils, SegLoad } Windows, { needs Events, Controls } { List 4 - needs List 1/2/3 types } Lists, { needs Controls, Memory } ObjIntf, BinIO, Lista3, DreamTypes, LowLevel; { Gerarchia degli oggetti: Qualsiasi entitˆ dell'applicazione definita come TFantasy. In quanto tale caratterizzato da cinque metodi, che sono Init (leggi le tue caratteristiche dalle risorse di scenario o applicazione); Time ( passato del tempo: agisci, se devi); e Kill (stai per venire deallocato. Hai qualcosa da fare?) Ci sono anche Save e Load, che non sono pienamente implementati per tutti gli oggetti. (I mostri non vengono salvati, p.es.) TFantasy ha tutti i metodi vuoti. Devono venire overriden, e non c' bisogno che vengano inherited. Su TFantasy si basano TCreatura, TItem e TIncantesimo. TCreatura la base per ogni entitˆ animata. Definisce il metodo Move e i campi Allineamento, AC, HP... TIncantesimo piuttosto autoesplicativo (vedi). TMostro (in DreamMonsters.p) e TPersonaggio (in Characters.p) sono figli di TCreatura, e finalmente i loro metodi (override di Init, Kill, Move e Time) sono istanziati. IL SALVATAGGIO: non posso fare un salvataggio a basso livello, perchŽ salverei anche puntatori e handle (pessima idea). NŽ posso salvare tutto e poi durante il load reinizializzare questi ultimi, perchŽ distruggerei i puntatori ai miei metodi. Quindi gni oggetto salva uno per uno tutti i campi significativi, anche se pochissimo efficiente. TUTTI I FIGLI-FOGLIA (=che non hanno altri figli) SONO TENUTI A FARE HLOCK SU SE STESSI ALL'INIZIO DI UN SAVE o LOAD, HUNLOCK alla fine. } TYPE TFantasy = OBJECT (TObject) icon: integer; { made current by Init } nome: String; PROCEDURE Init (ref: integer); { Shall be called when Object is created. Meant to be overridden } PROCEDURE Draw (where: rect; how: INTEGER); { Disegna l'icona caratteristica nel punto specificato del grafPort corrente } FUNCTION Time (amountInMin: integer): boolean; { Shall be called every game turn. Returns true if the entity asks to be killed. Meant to be overridden } FUNCTION Kill: boolean; { Shall be called when Object is removed. Returns true if the object asks to be kept along, although inactive (useful for dead characters). Can be overridden } PROCEDURE Save (dest: MyFile); { Saves all necessary data on the file. Should be overridden and called as inherited. } PROCEDURE Load (source: MyFile; version: Integer); { Reloads all necessary data from file. Should be overridden and called as inherited. } END; { TFantasy } { In questo istante i ST funzionano solo con incantesimi che producono danno fisico, e non hanno senso negli altri casi. Vedere lo OTHERWISE all'interno di IndividualAttack } AttackSpecifier = RECORD damage: Integer; { Quanto danno fa, se arriva a segno? } areaWidth, { Per gli spell } attackerTHACO: Integer; { Per Weapon..WeaponPlusFive e Missile } targetSolved, { Messaggio a TargetSystem, don't bother NEW for v1.3 } STForNone, STForHalf: Boolean; { Ammette Saving Throw? } kind: KindOfAttack; { Melee, WeaponÉ spell kind } groundZero: AttackArea; { Single o multiple target? } target: TCreatura; { Oppure NIL se di area } targetRef: EntityRef; { EntityRef se personaggio, oppure -1 } origin, { For spells, caster position } pivot: Point; { Center, or starting point, if area } attackingSpell: TIncantesimo; { Per gli incantesimi non-istantanei } identifyWhat: Storage; { Solo per Identify - NEW v1.1 } END; DatiDellIncantesimoDaSalvare = RECORD livello: integer; tipo: KindOfAttack; reserved: Integer; specifiche: BitsInByte; { [7] Wizard(0) or Cleric(1) [6] STForNoEffect [5] STForHalfEffect [4] DamageIsPerLevel [3] ... (was: area is num creatures) [2] Can be cast during fight [1] Can be cast during peace time } area: Integer; range: Integer; targetType: AttackArea; duratBase, durPerLevel: integer; { Durata totale CALC BY CALLER & PUT IN DURATBASE FIELD} dieSize, dieQty: integer; { Danno totale CALC BY CALLER } matComp: Integer; { Componenti materiali necessari; ID oggetto } codeSelector: Integer; END; TIncantesimo = OBJECT (TFantasy) target: TCreatura; { MUST BE SET BY CALLER } livello: integer; tipo: KindOfAttack; reserved: Integer; specifiche: BitsInByte; { [7] Wizard(0) or Cleric(1) [6] STForNoEffect [5] STForHalfEffect [4] DamageIsPerLevel [3] ... (was: area is num creatures) [2] Can be cast during fight [1] Can be cast during peace time [0] Can't be learned - is reserved for a scroll (Æ2.2) } area: Integer; range: Integer; targetType: AttackArea; duratBase, durPerLevel: integer; { Durata totale CALC BY CALLER & PUT IN DURATBASE FIELD} dieSize, dieQty: integer; { Danno totale CALC BY CALLER } matComp: Integer; { Componenti materiali necessari; ID oggetto } codeSelector: Integer; nextSpell: TIncantesimo; { Per la lista linkata - MUST BE SET BY CALLER } PROCEDURE Init (ref: integer); OVERRIDE; FUNCTION Time (amountInMin: integer): boolean; OVERRIDE; FUNCTION Kill: boolean; OVERRIDE; { Called when something like "Dispel magic" is cast. Always returns true. Shouldn't be called when Time returns TRUE } FUNCTION Act (VAR attack: AttackSpecifier): Boolean; PROCEDURE Save (dest: MyFile); OVERRIDE; PROCEDURE Load (source: MyFile; version: Integer); OVERRIDE; END; DatiDellaCreaturaDaSalvare = RECORD {*} dannoInDadi: Integer; { Per esempio 3-18 dannoInDadi = 3 dimDadi = 6 } {*} dimDadi: Integer; { 2-5 dannoInDadi = 1 dimDadi = 4 baseDamage = 1 } {*} baseDamage: Integer; {*} allineamento: TAllineamento; {*} whereAmI: Point; { Posizione sulla mappa. MUST BE SET AFTER CALLING CONSTRUCTOR BY CALLER } {*} HP, maxHP: integer; { HP correnti e HP massimi } status: CreatureStatus; { is ill, is dead, is poisoned... } isPoisoned: Integer; { Normalmente 0. Se pi di zero, quel numero di HP viene tolto per ogni round. } specialAttacks, specialDefenses, specialModifiers: PowerSet; hitDuringTheRound: Boolean; { Used by the fighting system } {*} wieldedWeapon: Storage; { Mano dx, mano sx o fodero per indicare una body weapon } {*} THACO: Integer; {*} attacchiPerDueRound: Integer; {*} AC: Integer; {*} Intelligenza, Destrezza: Integer; weightLoad: integer; { Questo serve solo ai TPersonaggio, ma qui di modo che TItem possa modificarlo quando fa Grab o Drop } END; TCreatura = OBJECT (TFantasy) { I campi marcati con * vanno inizializzati dal metodo INIT del figlio. I campi non marcati sono inizializzati da TCreatura.init } {*} dannoInDadi: Integer; { Per esempio 3-18 dannoInDadi = 3 dimDadi = 6 } {*} dimDadi: Integer; { 2-5 dannoInDadi = 1 dimDadi = 4 baseDamage = 1 } {*} baseDamage: Integer; {*} allineamento: TAllineamento; {*} whereAmI: Point; { Posizione sulla mappa. MUST BE SET AFTER CALLING CONSTRUCTOR BY CALLER } {*} HP, maxHP: integer; { HP correnti e HP massimi } status: CreatureStatus; { is ill, is dead, is poisoned... } isPoisoned: Integer; { Normalmente 0. Se pi di zero, quel numero di HP viene tolto per ogni round. } specialAttacks, specialDefenses, specialModifiers: PowerSet; hitDuringTheRound: Boolean; { Used by the fighting system } {*} wieldedWeapon: Storage; { Mano dx, mano sx o fodero per indicare una body weapon } {*} THACO: Integer; {*} attacchiPerDueRound: Integer; {*} AC: Integer; {*} Intelligenza, Destrezza: Integer; weightLoad: integer; { Questo serve solo ai TPersonaggio, ma qui di modo che TItem possa modificarlo quando fa Grab o Drop } activeSpells: TIncantesimo; PROCEDURE Draw (where: rect; how: INTEGER); OVERRIDE; Function Move: char; { Meant to be overridden.} Function Time (amountInMin: integer): BOOLEAN; OVERRIDE;{ Meant to be overridden AND called as inherited method when son's Init is finished } Procedure Init (ref:INTEGER); OVERRIDE; { Meant to be overridden AND called as inherited method when son's Init is BEGINNING } PROCEDURE Save (dest: MyFile); OVERRIDE; PROCEDURE Load (source: MyFile; version: Integer); OVERRIDE; END;(*** CONVENZIONE GENERALE PER IL RISULTATO DELLE FUNZIONI CHE RIPORTANO UNA MOSSA.Le funzioni Txxx.Move (TCreatura.Move e metodi figli seguono la seguente convenzione: ¥ Restituisce 1..9 per la direzione nella quale la creatura desidera muoversi o attaccare, ¥ Restituisce blank se la creatura resta ferma ¥ Restituisce * se la creatura ha impiegato altrimenti il suo tempo (per esempio lanciando un incantesimo). MainEventLoop segue questo stesso protocollo, poichŽ viene interrogato daTPersonaggio.Move per scopriore che cosa desidera fare il PC.***) (* Dati salvati da Dream 1.0 *) DatiDellOggettoDaSalvare = RECORD ID: integer; secretName: String; dannoInDadi, dimDadi, baseDamage: INTEGER; weight: integer; numCariche: integer; prezzo: integer; specialEffect: integer; reserved: integer; data: BitsInWord; wearingPlace: BitsInByte; permittedUse: BitsInByte; (* Solo in Dream 1.1: windowPos: Point *) END; TItem = Object (TFantasy) ID: integer; { Lo stesso resource ID. Serve per trattare gli ogget- ti definiti a livello applicazione, come il cibo o i componenti materiali per incantesimi } secretName: String; { Nome che si scopre solo facendo Identify } dannoInDadi, dimDadi, baseDamage: INTEGER; weight: integer; { peso in GP dell'oggetto } numCariche: integer; prezzo: integer; specialEffect: integer; { Internal routine selector } reserved: integer; { Per uso del codice dedicato a scopi speciali, come lo stato di un oggetto che fornisce luce (acceso o spento) etc. } data: BitsInWord; { [7] is magic [6] is weapon [5] is throwing weapon [4] is armour [3] is ammunition [2] fonte di luce [1] ricaricabile [0] maledetto [15] CAN BE USED AND IS SILENT - new for v1.6.1 [14] is food source [13] is scroll [12] is splittable [11] has text (* New for v1.3 *) [10] has pict [9] window is open (* NEW FOR V1.1 *) [8] known } wearingPlace: BitsInByte; { [7] finger [6] head [5] body [4] belt [3] hand [2] scabbard [1] sack } permittedUse: BitsInByte; {basta testare permittedUse [ord(Classe)] { [7] reserved [6] reserved [5] Wizard [4] Cleric [3] Thief [2] Ranger [1] Paladino [0] Combattente } windowPos: Point; (** New for v1.1: position that window will assume when opened **) itemWindow: WindowPtr; (** New for v1.1: window ptr **) owner: TCreatura; { Il proprietario. Mantenuto attraverso chiamate a Grab e Drop. } PROCEDURE Free; OVERRIDE; PROCEDURE Init (ref: integer); OVERRIDE; PROCEDURE Grab (who: TCreatura; inSack: BOOLEAN); { Chiamata quando qualcuno indossa l'oggetto, di modo che l'oggetto possa avere i suoi effetti sulla creatura. Se inSack TRUE, l'oggetto non ha tutt gli effetti (per esempio una armatura non migliora la AC) } FUNCTION Drop (forcedMove: BOOLEAN; fromWhere: Storage): BOOLEAN; { Chiamata quando qualcuno vuole lasciar andare l'oggetto (lo fa cadere, lo cede ad altra persona, lo vendeÉ) di modo che l'oggetto possa cessare i suoi effetti sulla creatura. Se forcedMove false, il trasferimento pu˜ essere negato (a discrezione dell'oggetto, che potrebbe essere maledetto). In questo caso il metodo restituisce TRUE se la mossa permessa, FALSE altrimenti. Se forcedMove TRUE, il trasferimento deve venire consentito dall'oggetto, e il risultato della funzione ignorato. Se l'oggetto stava nel sacco non vengono eliminate le conseguenze del possesso dell'oggetto (come il miglioramento di AC delle armature), perchŽ non erano state conteggiate in prima istanza } FUNCTION Time (amountInMin: integer): boolean; OVERRIDE; FUNCTION Use: Boolean; { Uso l'oggetto (p.es. l'utente ha cliccato sul pulsante Use). Fai ci˜ che devi, e dimmi se con ci˜ consumo l'oggetto } PROCEDURE Save (dest: MyFile); OVERRIDE; PROCEDURE Load (source: MyFile; version: Integer); OVERRIDE; END;TYPE ShopItem = RECORD { Used when we have a shop in the main window } item: TItem; { NEW for v1.1. Needed to have a get info button } itemName: String [30]; itemPrice: INTEGER; iconID, itemID: INTEGER END;(* Var globali *)VAR { For shops } gMoneySpentHere, { Usato per stabilire la loquacitˆ del barista } gNumItemsForSell: INTEGER; { -1..kMaxItemsInshop; - 1 serve per indicare "nessun oggetto " } itemsForSell: ARRAY [0..kMaxItemsInshop] OF ShopItem; shopSellingList: ListInfoRec; { List manager data } gActiveChars: Boolean; { Se true, ci sono personaggi attivi. Qs. var va testata dal codice che deve prendere decisioni del tipo "li lascio entrare o no", "faccio avvenire un combattimento o no", perchŽ possono esserci due personaggi nel gruppo (numPC = 2), ma entrambi morti, e in quel caso il combattimento non deve avvenire }{*** Inizializzazione e shutdown ***}PROCEDURE HiLevelInit;PROCEDURE HiLevelShutdown;{*** Codice di basso livello ad uso dei moduli superiori ***}FUNCTION OutputClass (c: TClasse): STR255;{ Gives back a string with the class name }FUNCTION OutputAlignment (a: TAllineamento): STR255;{ Gives back a string with the alignment name }PROCEDURE DrawMiscString (id: INTEGER);{ Leggi dal ramo risorse una STR#. Poi chiama DrawString su di essa.Le stringhe assortite sono tutte in MiscStrings }FUNCTION HPChange (me: TCreatura; howManyHP: INTEGER): BOOLEAN;{ Takes a creature, and adds/remove the said number of HP. If HP are added, theamount is clipped to maxHP. If HP are removed (i.e. howManyHP is negative),HPChange checks to see if creature is dead. In that case, it returns TRUE.Otherwise, it returns FALSE }PROCEDURE NoChars;{ Da chiamare quando anche l'ultimo dei personaggi se ne va o muove. Impedisceche venga selezionato alcunchŽ dal menu Group e aggiorna gActiveChars }PROCEDURE CharsHere;{ Da chiamare quando anche appare un personaggio attivo. Permetteche venga selezionato qualcosa dal menu Group e aggiorna gActiveChars }PROCEDURE SpellAlert (message, class: INTEGER);{ Finestra di dialogo che avvisa di un problema con gli incantesimi.Se message kMatSpellCompMiss, in "class" deve trovarsi lo ID del componente ma-teriale mancante. }PROCEDURE ItemID2ItemData (posInInventory, ID: INTEGER; usePrivateName: BOOLEAN);{ Creata per l'uso DoLoadShop, la procedura creata per caricare i dati relativi aglioggetti in vendita nei negozi. Ora qui, perchŽ lo stesso meccanismo viene usato perspartire il tesoro guadagnato negli incontri con i mostri.Il suo effetto di mettere nella struttura dati interna (quella usata pergovernare la lista di oggetti disponibili a video) l'oggetto di ID indicato.ItemID2ItemData si preoccupa di trasformare lo ID nel nome dell'oggetto, e ditrovarne il prezzo in GP.posInInventory l'indice del ciclo, e va aumentato per ogni successiva chiamata. }PROCEDURE DrawItemWindow(myWin: WindowPtr);{ Disegna la finestra dell'oggetto. Usata anche dal codice di stampa }implementationUSES Errors, Icons, Menus, QuickdrawText, { New for Universal Interfaces 2.0a3 } Resources, TextEdit, TextUtils, { List 4 - needs List 1/2/3 types } Dialogs, { needs TextEdit, Windows } Cilindro, DialogLord4, TaskMaster3;{$S LowLevel}PROCEDURE SpellAlert (message, class: INTEGER);{ Finestra di dialogo che avvisa di un problema con gli incantesimi.Se message kMatSpellCompMiss, in "class" deve trovarsi lo ID del componente ma-teriale mancante. }VAR msg, mat: Str255; i: INTEGER; okButton: Family;BEGIN ClearFamily (okButton); okButton[kStdOkItemIndex] := TRUE; DoSoundAsync (sndAttention); GetIndString (msg, rAlertMessages, message); { Get the main message } IF message = kMatSpellCompMiss THEN BEGIN ItemID2ItemData (kMaxItemsInshop, class, TRUE); mat := itemsForSell[kMaxItemsInshop].itemName END ELSE mat := ''; ParamText (msg, mat, '', ''); i := AlertLord (rSpellAlert, 1, okButton)END;{$S UtilInit}PROCEDURE HiLevelInit;BEGIN { Init variables } ora := 8 * 60; { 8 di mattina } giorno := 0; { primo gennaio, 1200 D.C. } gActiveChars := FALSEEND;{$S LowLevel}PROCEDURE NoChars;VAR mh: MenuHandle;BEGIN mh := GetMenuHandle(kGroupMenu); DisableItem (mh, 0); mh := GetMenuHandle(kCharMenu); DisableItem (mh, 0); DrawMenuBar; gActiveChars := FALSEEND;{$S LowLevel}PROCEDURE CharsHere;VAR mh: MenuHandle;BEGIN mh := GetMenuHandle(kGroupMenu); IF mh = NIL THEN DeathAlert(rUtilStrings, kNoMenuBar); EnableItem (mh, 0); DrawMenuBar; gActiveChars := TRUEEND;{$S LowLevel}FUNCTION HPChange (me: TCreatura; howManyHP: INTEGER): BOOLEAN;BEGIN HLock (Handle (me)); {$PUSH} {$H-} WITH me DO BEGIN if status[IsDead] THEN { Se morto non si pu˜ n dargli n togliergli HP } HPChange := FALSE ELSE BEGIN { OK, modifichiamo i punti vita } IF howManyHP < 0 THEN hitDuringTheRound := TRUE; HP := HP + howManyHP; { Se era un danno, facciamo sentire un lamento appropriato } IF howManyHP < -10 THEN BEGIN DoSoundAsync (sndSoSoHit); AddToTranscript (nome, ktIsClobbered, IToS (-howManyHP), ktHP); eND ELSE IF howManyHP < 0 THEN BEGIN DoSoundAsync (sndHit); AddToTranscript (nome, ktIsHit, IToS (-howManyHP), ktHP); END; { Ora vediamo se sopravvissuto, e clippiamo gli HP risultanti tra maxHP e zero } IF HP <= 0 THEN BEGIN HPChange := TRUE; AddToTranscript (nome, ktIsDead, '', 0); HP := 0; status[IsDead] := TRUE; END ELSE BEGIN HPChange := FALSE; IF HP > maxHP THEN HP := maxHP END END; END; { if isn't dead } {$POP} HUnlock (Handle (me));END;{$S LowLevel}PROCEDURE HiLevelShutdown;BEGIN NoChars { Grazie a questo se riparto tutto OK }END;{$S LowLevel}Procedure TFantasy.Init (ref: integer);{ Must be overridden }begin{$UNUSED ref}end;{$S LowLevel}PROCEDURE TFantasy.Draw (where: rect; how: INTEGER);{ New for v2.0: uses cicn when available }CONST iconForNoIcon = 203;VAR err: OSErr; grandeHandle: CIconHandle;BEGIN how := BAnd (how, $7FFF); grandeHandle := GetCIcon (icon); IF grandeHandle <> NIL THEN BEGIN err := PlotCiconHandle (where, kAlignAbsoluteCenter, how, grandeHandle); DisposeCicon (grandeHandle) END ELSE { Lo And mi esclude la costante $8000 che indica l'uso di figure intere anzichŽ il solo viso ( trattato in TPersonaggio.Draw) } IF PlotIconID (where, kAlignAbsoluteCenter, how, icon) <> noErr THEN { Question mark no-icon } err := PlotIconID (where, kAlignAbsoluteCenter, how, iconForNoIcon)END;{$S LowLevel}FUNCTION TFantasy.Time (amountInMin: integer): boolean; { Must be overridden }begin{$UNUSED amountInMin} Time := FALSEend;{$S LowLevel}FUNCTION TFantasy.Kill: boolean;begin Kill := trueend;{$S LowLevel}PROCEDURE TFantasy.Save (dest: MyFile);BEGIN {$PUSH} {$H-} WriteInt (dest, icon); WriteLn (dest, nome); CursorAnimate; {$POP}END;{$S LowLevel}PROCEDURE TFantasy.Load (source: MyFile; version: Integer);BEGIN {$UNUSED version} {$PUSH} {$H-} ReadInt (source, icon); ReadLn (source, nome); CursorAnimate; {$POP}END;{$S LowLevel}PROCEDURE DrawMiscString (id: INTEGER);VAR s: str255;BEGIN GetIndString (s, rMiscStrings, id); DrawString (s)END;{$S Characters}FUNCTION OutputClass (c: TClasse): STR255;CONST rClasses = 130;BEGIN GetIndString (OutputClass, rClasses, ORD(c)+1)END;{$S DefProcs}PROCEDURE DrawItemWindow(myWin: WindowPtr);VAR i: Integer; r: Rect; s: str255; obj: TItem; somethingSpecial: Boolean; PROCEDURE ACapo; BEGIN r.top := r.top + smallFontSize + 1; MoveTo (7, r.top); END; PROCEDURE OutputDamage; BEGIN somethingSpecial := TRUE; DrawString (ItoS (obj.baseDamage + obj.dannoInDadi)); DrawMiscString (rItmDivider); DrawString (ItoS (obj.baseDamage + obj.dannoInDadi * obj.dimDadi)); ACapo END; PROCEDURE OutputMark (available: Boolean); VAR penPos: Point; BEGIN REPEAT DrawChar ('.'); GetPen (penPos) UNTIL penPos.h >= kIWWidth-20; IF available THEN DrawMiscString (rItmMark) ELSE BEGIN ForeColor (redColor); DrawMiscString (rItmUnavailable); ForeColor (blackColor) END END;BEGIN TextFont (smallFontID); TextSize (smallFontSize); { Lo sfondo deve essere grigio, e NON basta che BackPat sia grigia. Di per sŽ, questo si limita a far s“ che gli effetti di EraseRect siano grigi e non bianchi, e fa anche s“ che le aree che appaiono per effetto di ScrollRect siano grige } IF NOT gHasThemes THEN BEGIN SetRect (r, 0, 0, kIWWidth, kIWHeight); EraseRect (r); END; obj := TItem (TMGetWRefCon (myWin, kRefConForHandle)); { 1. Disegna l'icona } SetRect (r, 10, 10, 74, 74); { Spazio ove mettere la icona } obj.Draw (r, ttNone); { 2. Scrivi il nome dell'oggetto } MoveTo (80, 20); IF obj.data[8] { obj is known } THEN s := obj.secretName ELSE s := obj.nome; { Obj Pascal won't accept a field as proc parameter } TextFace([Bold]); DrawString (s); TextFace ([]); { 3. E adesso il peso } MoveTo (80, 53); DrawMiscString (rStrWeight); DrawString (IToS(obj.weight)); { 4. Se un oggetto stackable, aggiungi il numero di cariche } IF obj.data[8] { known } THEN BEGIN MoveTo (80, 65); CASE obj.numcariche OF -1: DrawMiscString (rBurnedOut); 0: ; OTHERWISE BEGIN DrawMiscString (rStrDosesLeft); DrawString (IToS(obj.numCariche)); END; END; { case } END; { if } { 5 Se acceso, diglielo } IF obj.data[2] {lightbringing} THEN BEGIN MoveTo (80, 77); IF obj.reserved=1 THEN BEGIN ForeColor (redColor); DrawMiscString (rOn); ForeColor (blackColor) END ELSE IF obj.data[7] { magico } THEN BEGIN ForeColor (redColor); DrawMiscString (rLightbringer); ForeColor (blackColor) END ELSE DrawMiscString (rOff) END; { Inizia la scheda vera e propria (new for v1.1) } r.top := 77; ACapo; ACapo; TextFace ([Bold]); DrawMiscString (rItmIs); TextFace ([]); ACapo; IF NOT obj.data[8] {known} THEN BEGIN ForeColor (redColor); DrawMiscString (rItmUnknown); ForeColor (blackColor) END ELSE BEGIN somethingSpecial := FALSE; IF obj.data[7] THEN BEGIN somethingSpecial := TRUE; DrawMiscString (rItmMagic); ACapo END; IF obj.data[6] THEN BEGIN DrawMiscString (rItmWeapon); OutputDamage END; IF obj.data[5] THEN BEGIN somethingSpecial := TRUE; DrawMiscString (rItmThrown); IF obj.numCariche > 0 THEN OutputDamage ELSE ACapo END; IF obj.data[3] THEN BEGIN DrawMiscString (rItmAmmo); OutputDamage END; IF obj.data[4] THEN BEGIN somethingSpecial := TRUE; DrawMiscString (rItmArmor); DrawString (Itos (10 - obj.specialEffect)); ACapo END; FOR i := 2 DOWNTO 0 DO IF obj.data[i] THEN BEGIN somethingSpecial := TRUE; DrawMiscString (rItmCursed - i); ACapo END; FOR i := 14 DOWNTO 10 DO { Bug fix 1.6.1 } IF obj.data[i] THEN BEGIN somethingSpecial := TRUE; DrawMiscString (rItmLastAttribute + 10 - i); ACapo END; IF NOT somethingSpecial THEN BEGIN DrawMiscString (rItmNothingSpecial); ACapo END; ACapo; DrawMiscString (rStrValue); DrawString (IntegerToLocalString (obj.prezzo)); ACapo; ACapo; TextFace ([Bold]); DrawMiscString (rItmKept); TextFace ([]); ACapo; FOR i := 6 DOWNTO 1 DO IF obj.wearingPlace[i] THEN BEGIN DrawMiscString (rItmLastPos + 1 - i); ACapo; END; ACapo; TextFace ([Bold]); DrawMiscString (rItmUsable); TextFace ([]); ACapo; FOR i := 5 DOWNTO 0 DO BEGIN DrawString (OutputClass (TClasse (i))); OutputMark (obj.permittedUse[i]); ACapo; END; END { item known }END;{$LowLevel}PROCEDURE ItemID2ItemData (posInInventory, ID: INTEGER; usePrivateName: BOOLEAN); { Legge la risorsa Item di ID indicato, e mette i dati per l'utente in itemsForSell[posInInventory] } CONST kOffsetToPrice = 10; kOffsetToName = 14; { Uso il "nome se sconosciuto". Si suppone che nel caso di oggetti venduti in negozio sia = al nome conosciuto, ma visto che questa routine viene usata anche per i tesoriÉ } VAR theItem: Handle; itemScanner: Ptr; BEGIN theItem := MyGetResource ('Obj ', ID, TRUE, TRUE); WITH itemsForSell[posInInventory] DO BEGIN item := NIL; itemID := ID; { La prima cosa lo ID dell'icona } itemScanner := theItem^; iconID := GetIntegerFromRes (itemScanner); { A "kOffsetFromPrice" bytes di distanza sta il prezzo } itemScanner := Ptr(Ord4(StripAddress(theItem^))+kOffsetToPrice); itemPrice := GetIntegerFromRes (itemScanner); { A "kOffsetToName" bytes di distanza sta il nome pubblico } itemScanner := Ptr(Ord4(StripAddress(theItem^))+kOffsetToName); itemName := GetStringFromRes (itemScanner); { Se sono autorizzato posso mostrare il nome "privato" } IF usePrivateName THEN itemName := GetStringFromRes (itemScanner) END; { with } HUnlock (theItem) END;FUNCTION CreateItemWindow (oggetto: TItem): WindowPtr; EXTERNAL; { Trasferita in characters.p }{$S LowLevel}Procedure TItem.Init (ref: integer);VAR definition: Handle; scanner: Ptr;begin definition := MyGetResource (resTypeItem, ref, TRUE, TRUE); ID := ref; { Vedi commento nella definizione di TItem } scanner := StripAddress(definition^); icon := GetIntegerFromRes (scanner); data := GetWBFromRes (scanner); specialEffect := GetIntegerFromRes (scanner); numCariche := GetIntegerFromRes (scanner); weight := GetIntegerFromRes (scanner); prezzo := GetIntegerFromRes (scanner); wearingPlace := GetBBFromRes (scanner); permittedUse := GetBBFromRes (scanner); nome := GetStringFromRes (scanner); secretName := GetStringFromRes (scanner); baseDamage := GetByteFromRes (scanner); dannoInDadi := GetByteFromRes (scanner); dimDadi := GetByteFromRes (scanner); { owner sarˆ settato tramite Grab } owner := NIL; { reserved utilizzato nei casi speciali e reinizializzato, se serve, da Use } reserved := 0; { Non c' finestra per ora } itemWindow := NIL; Longint (windowPos) := 0; HUnlock (definition);end;FUNCTION ShellForLearningSpells (learner: TCreatura; spellID: Integer): Boolean;EXTERNAL; { Vedi discussione dentro Characters.p }PROCEDURE ShellForCastingSpells (itemName: String; owner: TCreatura; spellID: Integer);EXTERNAL;{$S LowLevel}FUNCTION TItem.Use: Boolean;VAR result, reallyUsed: Boolean; diffWeight: Integer; mioNome, padronNome: String;BEGIN mioNome := secretName; padronNome := owner.nome; { Let's keep track of real use. This way, if the item doesn't react to being "used", we can inform the player. } reallyUsed := FALSE; IF numCariche > 0 THEN BEGIN reallyUsed := TRUE; { Se un oggetto normale - p.es. razioni, diminuiscine il peso } IF data [7] { magico } OR data[15] THEN BEGIN IF specialEffect > 0 THEN ShellForCastingSpells (mioNome, owner, specialEffect) END ELSE BEGIN { Non magico. Di quanto cala il peso? } diffWeight := weight DIV numCariche; { Togli al possessore il peso consumato } owner.weightLoad := owner.weightLoad - diffWeight; { Assegna all'oggetto il nuovo peso } weight := weight - diffWeight; END; { Ora aggiorna il prezzo } prezzo := prezzo - prezzo DIV numCariche; { Finalmente, aggiorna il numero di cariche rimaste } numCariche := pred (numCariche); result := (numCariche = 0) END ELSE result := FALSE; { Salvo eccezioni, "Use" non consuma gli oggetti } IF { light source } data[2] & (numCariche > 0) & (reserved = 0) THEN BEGIN reserved := 1; { acceso } artificialLight := artificialLight + 1; IF (artificialLight > 0) & placeData[6] { need light } THEN BEGIN { Lasciagli vedere i dintorni } SetPort (mainWindow); TMInvalRect (paneRect) END; reallyUsed := TRUE; END; IF data[13] { scroll } THEN BEGIN numCariche := 0; { Questo serve come safety measure. Se il creatore dello scenario settasse il num cariche rischieremmo di ricadere anche nel codice successivo } reallyUsed := TRUE; result := ShellForLearningSpells (owner, specialEffect); END; { New for v1.3 } IF data[11] { book } THEN BEGIN reallyUsed := TRUE; KillText; TextOut (specialEffect, TRUE) END; IF data[10] { map } THEN BEGIN reallyUsed := TRUE; KillPict; PictOut (specialEffect) END; { IF object didn't react the button pressing, alert the user. } IF reallyUsed THEN AddToTranscript (padronNome, ktUses, mioNome, 0) ELSE GenericDreamAlert (kCantUse); Use := result;END;{$S LowLevel}PROCEDURE TItem.Free;BEGIN IF itemWindow <> NIL THEN { Questo dovrebbe accedere solo per le finestre degli item in vendita nei negozi } TMDisposeWindow (itemWindow); INHERITED Free;END;{$S LowLevel}FUNCTION TItem.Time (amountInMin: integer): boolean;VAR result: BOOLEAN; myName, ownerName: String;begin result := FALSE; { Salvo contrordini non voglio morire } IF { Maledetto } data[0] & (numCariche > 0) THEN BEGIN { Questo codice serve soprattutto agli oggetti creati tramite incantesimo e che hanno durata limitata nel tempo (come le Wizard Sword). Quegli oggetti sono dichiarati maledetti (di modo che il caster non possa cederli) e con numero cariche limitato (di modo che io li possa riconoscere qui. } numCariche := numCariche - amountInMin; IF numCariche < 1 THEN BEGIN AddToTranscript (myName, ktVanishes, '', 0); { Bug fix 1.7.1 } numCariche := -1; { esaurito } result := TRUE; END END; IF { light source } data[2] & (numCariche > 0) & (reserved = 1) THEN BEGIN { Spegni automaticamente l'oggetto se siamo usciti all'aperto } IF NOT placeData [6] { need light } THEN BEGIN { We are in a place which needs no light } reserved := 0; myName := nome; { Per i transcript } ownerName := owner.nome; AddToTranscript (ownerName, ktTurnsOff, myName, 0); amountInMin := 1; { Devo togliere almeno una carica, oppure TimingSystem non si accorgerˆ che ho modificato l'oggetto e non farˆ aggiornare la finestra relativa! } END; { se devi spegnere l'oggetto } numCariche := numCariche - amountInMin; IF numCariche < 1 THEN BEGIN numCariche := -1; { esaurito } IF artificialLight > 0 THEN artificialLight := artificialLight - 1; result := TRUE; IF artificialLight = 0 THEN BEGIN AddToTranscript ('', ktLightIsOff, '', 0); GenericDreamAlert (kPitchBlack) END; { if no more light } END { if end of charges } END; { if light equipment } Time := resultend;{$S LowLevel}PROCEDURE TItem.Grab (who: TCreatura; inSack: BOOLEAN);VAR mioNome, padronNome: String; attackLoop: KindOfAttack;begin IF NOT inSack THEN BEGIN mioNome := nome; padronNome := who.nome; AddToTranscript (padronNome, ktDons, mioNome, 0); IF { armour } data[4] THEN who.AC := who.AC - specialEffect; IF { weapon } data[6] THEN BEGIN { Quanto danno fa con me in mano? } who.baseDamage := baseDamage; who.dannoInDadi := dannoInDadi; who.dimDadi := dimDadi; IF { is magic weapon } data[7] THEN FOR attackLoop := Weapon DOWNTO KindOfAttack (Ord (Weapon) - baseDamage) DO who.specialAttacks[attackLoop] := TRUE; END; IF { gives light } data[2] & { is magic } data [7] THEN artificialLight := artificialLight + 1 END; who.weightLoad:= who.weightLoad + weight; owner := who; { segnati il riferimento per il futuro }end;{$S LowLevel}FUNCTION TItem.Drop (forcedMove: BOOLEAN; fromWhere: Storage): boolean;VAR result: Boolean; mioNome, padronNome: String; attackLoop: KindOfAttack;begin result := forcedMove OR (NOT data[0]) OR (fromWhere >= Sacco1); { se non maledetto si pu˜ lasciare andare } if result THEN BEGIN IF fromWhere < Sacco1 THEN BEGIN mioNome := nome; padronNome := owner.nome; AddToTranscript (padronNome, ktTakesOff, mioNome, 0); IF { armour } data[4] THEN owner.AC := owner.AC + specialEffect; IF result & { gives light } data[2] THEN artificialLight := artificialLight - 1 END; owner.weightLoad:= owner.weightLoad - weight; END; { Disarmato? } IF ((fromWhere = ManoDx) OR (fromWhere = ManoSx)) AND { weapon } data[6] THEN BEGIN { Ora disarmato } owner.baseDamage := 0; owner.dannoInDadi := 1; owner.dimDadi := 2; owner.wieldedWeapon := Fodero; { Non ha pi arma impugnata } IF { is magic weapon } data[7] THEN BEGIN FOR attackLoop := kFirstWeapon TO kLastWeapon DO owner.specialAttacks[attackLoop] := FALSE; owner.specialAttacks[Weapon] := TRUE { uno spreco, ma almeno funziona anche se inverto di nuovo W+5..W con W..W+5 } END; END; drop := resultend;{$S LowLevel}PROCEDURE TItem.Save (dest: MyFile);VAR size: longint;BEGIN { If the window is open, then save its position for next time } IF data[9] THEN windowPos := GiveBackWindowPositionOnScreen (itemWindow); HLock (Handle (self)); {$PUSH} {$H-} INHERITED Save (dest); size := SizeOf (DatiDellOggettoDaSalvare) { Dati comuni con Dream 1.0 } + SizeOf (Point); { Dati salvati solo da Dream 1.1 } PtrWrite (dest, size, @ID); CursorAnimate; {$POP} HUnLock (Handle (self));END;{$S LowLevel}PROCEDURE TItem.Load (source: MyFile; version: Integer);VAR size: longint;BEGIN HLock (Handle (self)); {$PUSH} {$H-} INHERITED Load (source, version); size := SizeOf (DatiDellOggettoDaSalvare); PtrRead (source, size, @ID); { Se stiamo leggendo dati salvati da Dream 1.1... } IF version >= 110 THEN BEGIN size := SizeOf (Point); PtrRead (source, size, @windowPos); END ELSE BEGIN { Inizializza i nuovi campi a un valore conosciuto } windowPos.v := 0; windowPos.h := 0; END; { inizializzazione degli altri campi } CursorAnimate; {$POP} HUnLock (Handle (self)); { Fa riapparire la finestra se in precedenza esisteva. } IF data[9] THEN itemWindow := CreateItemWindow (self) ELSE itemWindow := NIL;End;{$S LowLevel}PROCEDURE TCreatura.Draw (where: rect; how: INTEGER);BEGIN IF status[IsDead] THEN how := how + ttOffline; INHERITED Draw (where, how);END;{$S LowLevel}Function TCreatura.Move: char;{ Must be overridden }begin Move := '*' end;{$S LowLevel}FUNCTION TCreatura.Time (amountInMin: integer): boolean;BEGIN IF isPoisoned > 0 THEN { isPoisoned mi dice quanti punti vita perde ogni turno, quindi ogni dieci minuti. Ora, per calcolare quanti HP perde in tutto faccio questo conto. Immaginiamo che "ora" valga 542 (cio sono passati 542 minuti dalla mezzanotte). Immaginiamo che "amountInMin" valga 15. Io calcolo: ora DIV 10 - (ora - amountInMin) DIV 10. Questo vale 54 - 52 = 2, ed esattamente quel che volevo: il numero di volte che la lancetta dei minuti ha toccato il ten-minute mark da quando questa procedura stata chiamata l'ultima volta. } Time := HPChange (self, -isPoisoned*(ora DIV 10 - (ora - amountInMin) DIV 10)) ELSE Time := FALSEEND;{$S LowLevel}PROCEDURE TCreatura.Init (ref: INTEGER);VAR allStatusFlags: OneStatus; powerSetLoop: KindOfAttack;BEGIN{$UNUSED ref} FOR allStatusFlags := rs15 TO IsIll DO status[allStatusFlags] := FALSE; isPoisoned := 0; whereAmI.h := 255; { No need to do this. It's here to make debugging easier } whereAmI.v := 255; weightLoad := 0; { Standard config for a creature. May be modified by son } FOR powerSetLoop := kFirstAttack TO kLastAttack DO specialModifiers[powerSetLoop] := FALSE; specialAttacks := specialModifiers; specialAttacks[Weapon] := TRUE; specialDefenses := specialModifiers; specialDefenses[Curse] := TRUE; activeSpells := NIL;END;{$S LowLevel}PROCEDURE TCreatura.Save (dest: MyFile); OVERRIDE;VAR aSpell: TIncantesimo; size: longint;BEGIN {$PUSH} {$H-} INHERITED Save (dest); size := SizeOf (DatiDellaCreaturaDaSalvare); PtrWrite (dest, size, @dannoInDadi); CursorAnimate; {$POP} { Now for the spells } aSpell := activeSpells; { testa della lista } WHILE aSpell <> NIL DO BEGIN WriteInt (dest, 1); { Segnala che segue un incantesimo } aSpell.Save (dest); aSpell := aSpell.nextSpell END; WriteInt (dest, 0);END;{$S LowLevel}PROCEDURE TCreatura.Load (source: MyFile; version: Integer); OVERRIDE;VAR aSpell: TIncantesimo; thereIsSpell: Integer; size: Longint;BEGIN {$PUSH} {$H-} INHERITED Load (source, version); size := SizeOf (DatiDellaCreaturaDaSalvare); PtrRead (source, size, @dannoInDadi); CursorAnimate; {$POP} { Gli incantesimi } activeSpells := NIL; REPEAT ReadInt (source, thereIsSpell); IF (thereIsSpell = 1) THEN BEGIN aSpell := NIL; { Istanzialo } New (aSpell); FailNIL (aSpell); aSpell.Load (source, version); { Caricalo } aSpell.Target := self; { Sistema il riferimento a me } aSpell.NextSpell := activeSpells; { Aggiusta la lista } activeSpells := aSpell END UNTIL thereIsSpell = 0;END;{$S LowLevel}PROCEDURE TIncantesimo.Init (ref: integer);VAR definizione: Handle; scanner: Ptr;begin { Read from application the spell descriptor data if necessary } definizione := MyGetResource (resSpell, ref, TRUE, FALSE); scanner := definizione^; { Start reading. } livello := GetByteFromRes (scanner); tipo := OldKindOfAttackToKindOfAttackv21 (GetIntegerFromRes (scanner)); reserved := GetIntegerFromRes (scanner); specifiche := GetBBFromRes (scanner); targetType := AttackArea(GetByteFromRes (scanner)); range := GetByteFromRes (scanner); area := GetByteFromRes (scanner); { Calcola il danno } dieSize := GetByteFromRes (scanner); dieQty := GetByteFromRes (scanner); { Calcola la durata in round } duratBase := GetByteFromRes (scanner); durPerLevel := GetByteFromRes (scanner); { Proseguo la lettura } matComp := GetIntegerFromRes (scanner); codeSelector := GetIntegerFromRes (scanner); nome := GetStringFromRes (scanner); icon := GetintegerFromRes (scanner); (*** bersaglio va fissato dal chiamante!!! ***) target := NIL; nextSpell := NIL; HUnlock (definizione)END;{$S LowLevel}FUNCTION TIncantesimo.Act (VAR attack: AttackSpecifier): Boolean;{ Questa funzione viene chiamata per gli incantesimi di protezione:ha la possibilitˆ di modificare l'attacco portato (tipicamente cambiandoil valore del campo "damage". Deve restituire TRUE se ha modificato, FALSEaltrimenti }BEGIN CASE codeSelector OF -1: { protection spell } BEGIN attack.damage := BSR (attack.damage, 1); Act := TRUE END; -2: { Circle of protection spell } BEGIN attack.damage := 0; duratBase := 0; Act := TRUE END; OTHERWISE act := FALSE END { case }END;{$S LowLevel}PROCEDURE TIncantesimo.Save (dest: MyFile);VAR size: longint;BEGIN INHERITED Save (dest); size := SizeOf (DatiDellIncantesimoDaSalvare); PtrWrite (dest, size, @livello); CursorAnimate;END;{$S LowLevel}PROCEDURE TIncantesimo.Load (source: MyFile; version: Integer);VAR size: longint;BEGIN HLock (Handle (self)); {$H-} INHERITED Load (source, version); size := SizeOf (DatiDellIncantesimoDaSalvare); PtrRead (source, size, @livello); IF version < 210 THEN { Nuova versione dell'enumerato "KindOfAttack" } tipo := OldKindOfAttackToKindOfAttackv21 (Ord(tipo)); target := NIL; nextSpell := NIL; CursorAnimate; {$H-} HUnLock (Handle (self));END;PROCEDURE ShellForKillingChars (target: TCreatura);EXTERNAL;{$S LowLevel}FUNCTION TIncantesimo.Time (amountInMin: integer): boolean;VAR finito: Boolean; mioNome: String; { Per il transcript }BEGIN duratBase := duratBase - amountInMin; { Finito? } finito := duratBase < 0; { Se finito, revoca i miei effetti, se necessario } (*** QUESTA PARTE DI CODICE DEVE ESSERE STRETTAMENTE SIMMETRICA E SINCRONA A SPELLSYSTEM IN DREAMMONSTERS.P ***) IF finito THEN BEGIN mioNome := nome; AddToTranscript (mioNome, ktExpires, '', 0); { Incantesimi di protezione (caratterizzati da codeSelector negativo) } IF codeSelector < 0 THEN target.specialModifiers[tipo] := FALSE; CASE tipo OF Poison: { Questo era Slow poison! } target.isPoisoned := succ (target.isPoisoned); Illness: { Slow illness? VabbehÉ } target.status[isIll] := TRUE; Special: CASE codeSelector OF 1: { light } BEGIN artificialLight := pred (artificialLight); IF artificialLight <= 0 THEN BEGIN AddToTranscript ('', ktLightIsOff, '', 0); IF placeData[6] { need light } THEN GenericDreamAlert (kPitchBlack) END; { pitch black } END; { light } 3: { slow } target.attacchiPerDueRound := BSL (target.attacchiPerDueRound, 1); 4: { haste } target.attacchiPerDueRound := BSR (target.attacchiPerDueRound, 1); 5: { Feign death } BEGIN target.status[IsDead] := FALSE; { CharacterHasChanged (TPersonaggio(target)); } SetPort (mainWindow); TMInvalRect (groupRect); END; 6: { hold monster } target.attacchiPerDueRound := reserved; 10: { fly. In qs. caso reserved = 1 vale "ground", 2 vale "fly" } target.status[IsFlying] := (reserved<>2); 12: { Faerie fire - barkskin and similar } { Ripristina la sua AC } IF reserved <> 0 THEN target.AC := target.AC - reserved ELSE target.AC := pred(target.AC); 13: { Simulacrum } { Problema: qui concettualmente dovrei chiamare DoKillChar per liberarmi del clone. Per˜ nel seguito lo spell system cercherˆ di deallocare me stesso, l'incantesimo. Per˜ DoKillChar mi ha giˆ deallocato. Inoltre il TimingSystem quando esco di qui cercherˆ di percorrere la carena degli incantesimi, anch'essa deallocata. Boom. Per risolvere l'impasse metto nella lista degli eventi da eseguire uno pseudo-evento "uccidi questo personaggio" } ShellForKillingChars (target); 17: { Invisibility } target.status[IsInvisible] := FALSE END { Caso speciale } END { Case sul tipo di incantesimo } END; { If incantesimo finito } { Restituisci risultato } Time := finitoEND;{$S LowLevel}FUNCTION OutputAlignment (a: TAllineamento): STR255;CONST rAlignment = 131;BEGIN GetIndString (OutputAlignment, rAlignment, ORD(a)+1)END;{$S LowLevel}FUNCTION TIncantesimo.Kill: Boolean;BEGIN Kill := SELF.Time (maxint)END;end. { unit }