-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDreamMagic.p
1020 lines (924 loc) · 32.4 KB
/
DreamMagic.p
1
Unit DreamMagic;(* SCOPO: Gestisce il cast di incantesimi, i loro effetti, il meccanismo di memorizzazione e l'interfaccia utente relativa *)INTERFACEUSES Types, QuickDraw, { List 2 } Controls, Events, Memory, OSUtils, SegLoad, { List 3 - needs List 1/2 types } Files, { needs OSUtils, SegLoad } TextEdit, { needs Events, Controls } Windows, { needs Events, Controls } { List 4 - needs List 1/2/3 types } Dialogs, { needs TextEdit, Windows } Lists, { needs Controls, Memory } ObjIntf, BinIO, Lista3, DreamTypes, LowLevel, HiLevel;VAR thisCharacter: TCreatura; { Parametro implicito a LearnNewSpells e DrawClassIcon } attackAdditionalInfo: AttackSpecifier; { Restituito da TMostro.Move } { Questa variabile importantissima. TMostro.Move la restituisce per specificare la forma d'attacco tentata, che poi verrˆ risolta dal FightingSystem. Quando un personaggio fa un incantesimo (direttamente o indirettamente usando un item), viceversa, viene compilata da CastSpell (relativamente al campo attackingSpell e damage se applicabile), di modo che qualcuno sopra (FightingSystem o Main Game Loop) possa risolvere l'accaduto. CastSpell mette come "target" il caster, di modo che se l'incantesimo ha effetto sul caster il TargetSystem lo localizza subito. Se invece l'incantesi- mo di tipo diverso, il TargetSystem farˆ in modo di localizzarlo. }{ Gestione della parte incantesimi del Dream database }Procedure SpellDBStartup;Procedure SpellDBInit;Procedure SpellDBShutdown;FUNCTION SpellCheck (fightTime: Boolean): Boolean;{ Ha per parametro implicito attackAdditionalInfo, che deve contenere inattackingSpell lo handle allo spell istanziato, e in target lo handle al caster.Restituisce FALSE, e dealloca lo spell, se l'incantesimo non ha effetto.(Per esempio, spell cast a tempo inadatto).Negli altri casi restituisce TRUE. }FUNCTION LearnNewSpells (nome: String; VAR alreadyKnown: KnownSpellsArray; numKnown, numToLearn, whatToLearn, maxLevel: Integer; isCleric: Boolean): Boolean;{ Un personaggio deve imparare uno o pi nuovi incantesimi.PRIMA DI CHIAMARE QUESTA FUNZIONE, assegnare thisCharacter := SELF;PARAMETRI:Il suo nome in Nome, e gli incantesimi che conosce giˆ sono in alreadyKnown.numKnown il numero di incantesimi che conosce giˆ, e quindi gli incantesimiconosciuti sono alreadyKnown[1]..alreadyKnown[numKnown].Se il personaggio pu˜ imparare incantesimi da chierico, isCleric TRUE. Sepu˜ imparare incantesimi da mago, isCleric false.numToLearn il numero di incantesimi che il Nostro pu˜ imparare. Quando passa dilivello, vale uno; quando il pers. viene creato, vale pi d'uno (in 1.0, tre);quando impara da uno scroll, vale uno.Se impara da scroll, whatToLearn lo ID dell'incantesimo che pu˜ imparare. Neglialtri casi, invece, pu˜ scegliere tra tutti gli incantesimi esistenti, e allorawhatToLearn deve valere zero e maxLevel dev'essere il livello massimo diincantesimo a lui accessibile.LA FUNZIONE restituisce TRUE se tutto andato bene, FALSE in caso contrario.Alcuni motivi che possono portare a un risultato FALSE: il personaggio chiericoe l'incantesimo da mago; oppure il giocatore clicca su Cancel; oppure ilgiocatore conosce giˆ l'incantesimo passato in whatToLearn. }FUNCTION IsSpellCaster (c: TClasse; livello: Integer): Boolean;{ Data una classe e un livello, dice se questo personaggio fa uso di incantesimi }PROCEDURE DrawClassIcon (where: DialogPtr; item: INTEGER);{ Defproc richiamata dal dialog manager per disegnare l'icona di un personaggiodella finestra di dialogo manipolata dentro TPersonaggio.Init o inLearnNewSpells. (é uno useritem).Prima di porre il puntatore a qs. procedura nel descrittore del dlogmgr, necessario assegnare alla globale thisCharacter la handle al personaggio. }FUNCTION SpellID2SpellName (id: INTEGER): String;{ A uso delle funzioni che creano una lista di incantesimi. Serve arestituire il nome che caratterizza un incantesimo. Se non esiste un incantesimocon quell'identificatore restituisce stringa nulla }PROCEDURE SpellID2SpellData (spellID: Integer; VAR spellLevel: Integer; VAR spellKind: Boolean);{ A uso del codice che deve decidere se un personaggio pu˜ usare un certo incantesimo.Dato lo ID di un incantesimo restituisce il suo tipo (mago o chierico) e il suolivello di difficoltˆ }FUNCTION DoCastSpell (spellNumber, casterLevel: INTEGER; casterName: String; casterPos: Point): TIncantesimo;{ Kernel spellcasting code. Istanzia l'incantesimo, senza check di alcun tipo.Da chiamare quando il personaggio fa cast, e le condizioni sono giuste(ha i comp. mat., ecceteta) oppure quando un oggetto magico fa unincantesimo per conto del personaggio.Nel seguito si pu˜ forzare la risoluzione dell'incantesimo chiamando direttamenteSpellSystem2 oppure restituire un codice K ed attendere che il main game loopo il FightingSystem invochino SpellSystem.DoCastSpell non risolve il target (target, targetref e hisindex dentro attackadditionalinfo)Se casterName '', DoCastSpell non fa apparire nel Diario alcuna scritta(utile per gli spell lanciati da items)DoCastSpell pone targetSolved a FALSE. Se il chiamante non desidera i servigi delTargetSystem setti quella variabile a TRUE }PROCEDURE DoIdentify (onWhat: Storage; ofWhom: TCreatura);{ Da chiamare al posto di TargetSystem quando l'incantesimo identify.Focalizza l'incantesimo sul bersaglio specificato.In seguito, lo spell system eseguirˆ }PROCEDURE SpellTimingSystem (VAR theSpell: TIncantesimo; timeAmt: Integer);{ "Invecchia" di timeAmt minuti tutti gli incantesimi della lista la cuitesta si trova in theSpell.Chiamato dal TimingSystem per tutti i personaggi e dal FightingSystem direttamenteper tutti i mostri. }PROCEDURE RefillSpells (VAR bookOfSpells: KnownSpellsArray; numKnown: Integer; VAR mindOfChar: MemorizedSpellsArray; maxSpells: NumSpellsArray);{ Chiamato quando un personaggio ha terminato di riposare e pu˜ recuperaregli incantesimiPRIMA DI CHIAMARE QUESTA FUNZIONE, assegnare thisCharacter := SELF;}PROCEDURE FindSpellsPerLevel (l: Livello {personaggio}; c: TClasse; VAR spellsPerLevel: NumSpellsArray);{ Basso livello - scopri quanti incantesimi di un certo livello ilpersonaggio pu˜ memorizzare }PROCEDURE KillAllSpells (onWhom: TCreatura);{ Uccide tutti gli incantesimi che gravano su una creatura. Ha un sensosia a seguito di azioni di gioco, come Dispel Magic, sia in seguitoa problematiche programmatorie (p.es. il personaggio se ne va e bisognadeallocare tutto ci˜ che lo riguarda) }PROCEDURE GiveSpellToCreature (theSpell: TIncantesimo; theCreat: TCreatura);{ Assegna correttamente alla creatura indicata l'incantesimo dato, chedeve avere durata non nulla }PROCEDURE GiveCloneToCreature (theSpell: TIncantesimo; theCreat: TCreatura);{ Analogo di GiveSpellToCreature, ma crea un clone dell'incantesimo dato edassegna questo alla creatura }IMPLEMENTATIONUSES { List 2 - only require List 1 types } Errors, Icons, Resources, Cilindro, DialogLord4;VAR { Memoria degli incantesimi esistenti. } allSpells: AllSpellsTable;{--------------DB handling ------------------}{$S Magic}Procedure SpellDBStartup;VAR numSpells, i: Integer; { Spell attributes, mostly from GetResInfo } aSpell: Handle; aSpellInfo, spellLoop: TSpellInfoHdl; theSpell: TIncantesimo; itsID, itsLevel: Integer; itsName: Str255; itsType: ResType; itsKind: Boolean;BEGIN { Se la struttura dati esiste giˆ (qs. procedura viene chiamata da DoLoadScenario) va scaricata e ricaricata da zero. Solo in qs. modo mi garantisco che gli spell specifici dello scenario siano trattati correttamente } { New for 1.6 } IF allSpells[1, FALSE] <> NIL THEN FOR itsLevel := 1 TO kMaxSpellLevel DO FOR itsKind := FALSE TO TRUE DO WHILE allSpells [itsLevel, itsKind] <> NIL DO BEGIN aSpellInfo := allSpells [itsLevel, itsKind]; allSpells [itsLevel, itsKind] := aSpellInfo^^.next; DisposeHandle (Handle(aSpellInfo)) END; { Load all spell descriptions in spell table } numSpells := CountResources(resSpell); FOR i := 1 TO numSpells DO BEGIN aSpell := GetIndResource(resSpell,i); { Find ID } GetResInfo(aSpell,itsID,itsType,itsName); { Find level and kind (mage-cleric) } New (theSpell); theSpell.Init (itsID); itsLevel := theSpell.livello; itsKind := theSpell.specifiche[7]; { Check that it's a user-usable spell } IF (itsLevel > 0) AND (itsLevel <= kMaxSpellLevel) AND NOT theSpell.specifiche[0] { New for v2.2 } THEN BEGIN { Create spell info record } aSpellInfo := TSpellInfoHdl (NewHandle (SizeOf(TSpellInfo))); IF aSpellInfo = NIL THEN DeathAlert (errOutOfMemory, 666); BlockMove (@theSpell.nome, @aSpellInfo^^.name, 31); { Nome } aSpellInfo^^.id := itsID; aSpellInfo^^.icon := theSpell.icon; { Add it to the appropriate list } { Is it the only one, or the first? } IF (allSpells [itsLevel, itsKind] = NIL) | (theSpell.nome < allSpells [itsLevel, itsKind]^^.name) THEN BEGIN { Yes, put it there } aSpellInfo^^.next := allSpells [itsLevel, itsKind]; allSpells [itsLevel, itsKind] := aSpellInfo; END ELSE BEGIN { No, add in queue } spellLoop := allSpells [itsLevel, itsKind]; WHILE (spellLoop^^.next <> NIL) & (spellLoop^^.next^^.name < theSpell.nome) DO spellLoop := spellLoop^^.next; aSpellInfo^^.next := spellLoop^^.next; spellLoop^^.next := aSpellInfo END { if must be queued } END; { If added } { Remove unneeded info } theSpell.Free; ReleaseResource (aSpell); END;END;{$S Magic}Procedure SpellDBInit;VAR allLevels: Integer; allKinds: Boolean;BEGIN FOR allLevels := 1 TO kMaxSpellLevel DO FOR allKinds := FALSE TO TRUE DO allSpells [allLevels, allKinds] := NILEND;{$S Magic}Procedure SpellDBShutdown;VAR allLevels: Integer; allKinds: Boolean; temp: TSpellInfoHdl;BEGIN { Togli tutte le informazioni nella base dati incantesimi } FOR allLevels := 1 TO kMaxSpellLevel DO FOR allKinds := FALSE TO TRUE DO WHILE allSpells [allLevels, allKinds] <> NIL DO BEGIN temp := allSpells [allLevels, allKinds]; allSpells [allLevels, allKinds] := allSpells [allLevels, allKinds]^^.next; DisposeHandle (Handle (temp)); END;END;{$S Magic}{ New for Dream 1.6 }PROCEDURE SpellID2SpellData (spellID: Integer; VAR spellLevel: Integer; VAR spellKind: Boolean);LABEL 0;VAR i: Integer; j: Boolean; aSpell: TSpellInfoHdl;BEGIN { Supponi che non si troviÉÊ} spellLevel := 0; { Cercalo nella base dati } FOR i := 1 TO kMaxSpellLevel DO FOR j := FALSE TO TRUE DO BEGIN aSpell := allSpells [i, j]; WHILE aSpell <> NIL DO BEGIN IF aSpell^^.id = spellID THEN BEGIN { Found! } spellLevel := i; spellKind := j; GOTO 0 { Quick exit } END; aSpell := aSpell^^.next END { while not found } END; { for }0:END; { Procedure }{--------------DB handling end ------------------}{$S Magic}FUNCTION IsSpellCaster (c: TClasse; livello: Integer): Boolean;BEGIN IsSpellCaster := (c > Ladro) | (((c = Ranger) | (c = Paladino)) & (livello > 3))END;{$S Magic}PROCEDURE KillAllSpells (onWhom: TCreatura);VAR aSpell, proxSpell: TIncantesimo; dummy: Boolean;BEGIN aSpell := onWhom.activeSpells; WHILE aSpell <> NIL DO BEGIN dummy := aSpell.Kill; proxSpell := aSpell.nextSpell; aSpell.Free; aSpell := proxSpell END; onWhom.activeSpells := NIL { Bug fix 2.1 }END;{$S Magic}PROCEDURE SpellTimingSystem (VAR theSpell: TIncantesimo; timeAmt: Integer);VAR previousSpell, aSpell: TIncantesimo;BEGIN previousSpell := NIL; aSpell := theSpell; WHILE aSpell <> NIL DO BEGIN IF aSpell.Time (timeAmt) THEN BEGIN { Remove it from the chain } IF previousSpell = NIL THEN BEGIN theSpell := aSpell.nextSpell; aSpell.Free; aSpell := theSpell END ELSE BEGIN previousSpell.nextSpell := aSpell.nextSpell; aSpell.Free; aSpell := previousSpell.nextSpell END END ELSE BEGIN { Scroll chain to next spell } aSpell := aSpell.nextSpell; IF previousSpell = NIL THEN previousSpell := theSpell ELSE previousSpell := previousSpell.nextSpell; END { if this spell lasts } END; { while }END;{$S Magic}FUNCTION DoCastSpell (spellNumber, casterLevel: INTEGER; casterName: String; casterPos: Point): TIncantesimo;{ Kernel spellcasting code }VAR theSpell: TIncantesimo; nomeIncantesimo: String;BEGIN theSpell := NIL; New (theSpell); FailNIL (theSpell); theSpell.Init (spellNumber); { Interfaccia utente } nomeIncantesimo := theSpell.nome; IF casterName <> '' THEN AddToTranscript (casterName, ktCasts, nomeIncantesimo, 0); WITH attackAdditionalInfo DO BEGIN (*** Compila il record ***) attackingSpell := theSpell; origin := casterPos; IF theSpell.dieSize > 0 THEN { Bisogna calcolare il danno. Per ora lo lascio positivo, perchŽ cura di IndividualAttack trasformarlo in negativo come pretende HPChange } IF theSpell.specifiche[4] { damage is per level } THEN damage := Dado (theSpell.dieQty*casterLevel, theSpell.dieSize) ELSE damage := Dado (theSpell.dieQty, theSpell.dieSize) ELSE damage := 0; IF theSpell.durPerLevel > 0 THEN BEGIN { Bisogna calcolare la durata } theSpell.duratBase := theSpell.duratBase + theSpell.durPerLevel*casterLevel; theSpell.durPerLevel := 0 { anche se probabilmente non serve } END; kind := theSpell.tipo; groundZero := theSpell.targetType; stForNone := theSpell.specifiche[6]; stForHalf := theSpell.specifiche[5]; { attackerTHACO: non applicabile } { target: a cura di TargetSystem } { targetRef: a cura di TargetSystem } { pivot: a cura di TargetSystem } targetSolved := FALSE END; DoCastSpell := theSpellEND;{$S Magic}PROCEDURE DoIdentify (onWhat: Storage; ofWhom: TCreatura);BEGIN { Sostituisciti al TargetSystem per trovare il bersaglio } WITH attackAdditionalInfo DO BEGIN target := ofWhom; identifyWhat := onWhat; targetSolved := TRUE END;END;{$S Magic}PROCEDURE FindSpellsPerLevel (l: Livello {personaggio}; c: TClasse; VAR spellsPerLevel: NumSpellsArray);CONST rFirstLevelForPaladins = 128; rFirstLevelForClerics = 131;VAR tableID: Integer; spellTableHdl: handle;BEGIN IF (c = Chierico) OR (c = Mago) THEN tableID := rFirstLevelForClerics - 1 + l ELSE tableID := rFirstLevelForPaladins - 1 + l; { Load the appropriate table } spellTableHdl := MyGetResource (resSpellsPerLevel, tableID, TRUE, FALSE); { Copy it inside the array. The handle is locked, courtesy of MyGetResource } BlockMove (spellTableHdl^, @spellsPerLevel, Sizeof (NumSpellsArray)); HUnlock (spellTableHdl);END;{$S Magic}FUNCTION SpellID2SpellName (id: INTEGER): String;CONST kOffsetOfSpellName = 17;VAR definizione: Handle; scanner: Ptr;BEGIN definizione := MyGetResource (resSpell, id, FALSE, FALSE); IF definizione = NIL THEN SpellID2SpellName := '' ELSE BEGIN scanner := Ptr (Ord4 (StripAddress(definizione^)) + kOffsetOfSpellName); SpellID2SpellName := GetStringFromRes (scanner); HUnlock (definizione); ENDEND;{$S DefProcs}PROCEDURE DrawClassIcon (where: DialogPtr; item: INTEGER);VAR r: Rect;BEGIN GetItemRect (where, item, r); { Porcheria di cui mi vergogno molto: voglio riutilizzare questo codice per disegnare anche l'icona a figura piena nel Join Dialog. Siccome l“ la icona in figura piena ha item ID 23É } IF item > 20 THEN thisCharacter.Draw (r, ttNone+$8000) ELSE thisCharacter.Draw (r, ttNone)END;VAR { Per l'uso di DrawList e di LearnNewSpells } bookList, mayLearnList: ListInfoRec;{$S DefProcs}PROCEDURE DrawList (where: DialogPtr; item: INTEGER);VAR myRect: Rect;BEGIN IF item = kBookList THEN Ridisegna (bookList) ELSE Ridisegna (mayLearnList);END;{$S Magic}FUNCTION LearnNewSpells (nome: String; VAR alreadyKnown: KnownSpellsArray; numKnown, numToLearn, whatToLearn, maxLevel: Integer; isCleric: Boolean): Boolean;LABEL 9;VAR spellLoop: TSpellInfoHdl; isUnknown: Boolean; iType, i, j, item, cursorToUnknown, numLearned: Integer; spellKind: Boolean; oldPort: GrafPtr; myDlog: DialogPtr; iBox: Rect; noItems, allItems: Family; e: EventRecord; c: Cell; nomeSpell: String; listOfSpellsThatMayBeLearned: KnownSpellsArray; PROCEDURE DoSelectNewSpell (which: Integer); VAR i: Integer; BEGIN { é stato selezionato l'incantesimo numero which tra quelli memorizzati in listOfSpellsThatMayBeLearned. c contiene la cella selezionata } { 1. Togli la cella selezionata } CancellaCella (mayLearnList, c); { 2. Mettine copia nella lista degli inc. conosciuti } c.v := bookList.numCelleV; nomeSpell := SpellID2SpellName (listOfSpellsThatMayBeLearned[which]); NuovaCella (bookList, c, @nomeSpell); { E, finalmente, inserisci il nuovo incantesimo anche nel libro } numLearned := succ (numLearned); alreadyKnown[numKnown+numLearned] := listOfSpellsThatMayBeLearned[which]; { Togli l'incantesimo imparato dal vettore degli imparabili } FOR i := which TO kMaxKnownSpells-1 DO listOfSpellsThatMayBeLearned[i] := listOfSpellsThatMayBeLearned[i+1]; { 3. Consenti di annullare } IF numLearned = 1 THEN EnableDialogItem (myDlog, kUndoLearn); { 4. Tieni nota di quali e quanti inc. ha selezionato } IF numLearned = numToLearn THEN EnableDialogItem (myDlog, OK); ParamText (nome, IToS (numToLearn-numLearned), '', ''); { Invalida lo spazio relativo di modo che appaia il num corretto di incantesimi ancora da memorizzare } InvalRect (iBox); END; { proc } PROCEDURE UndoAll; VAR i, thisSpellID: Integer; BEGIN { Togli i nuovi incantesimi dalla lista a sinistra e rimettili a destra } FOR i := 1 TO numLearned DO BEGIN { Allora: (tenendo presente che i vettori sono one-based mentre le liste hanno celle numerate dallo zero in poi) ¥ Togliere l'ultima cella a bookList (la numero bookList.numCelleV-1) ¥ Il n¡ dell'incantesimo da spostare in alreadyKnown[numKnown+numLearned-i] ¥ Trovare il nome relativo ¥ Aggiungere quel nome in fondo alla lista mayLearnList ¥ Aggiungere quel numero in fondo al vettore listOfSpellsThatMayBeLearned, cio nelle posizione mayLearnList.numCelleV } {1} c.v := bookList.numCelleV-1; CancellaCella (bookList, c); {2} thisSpellID := alreadyKnown[numKnown+numLearned-i+1]; {3} nomeSpell := SpellID2SpellName (thisSpellID); {4} c.v := mayLearnList.numCelleV; NuovaCella (mayLearnList, c, @nomeSpell); {5} listOfSpellsThatMayBeLearned[mayLearnList.numCelleV] := thisSpellID; END; { Aggiorna il display } DisableDialogItem (myDlog, OK); { Per il caso in cui li avesse giˆ scelti tutti } DisableDialogItem (myDlog, kUndoLearn); numLearned := 0; ParamText (nome, IToS (numToLearn), '', ''); InvalRect (iBox); END;BEGIN { 1. Sanity check. Ha senso quel che mi chiedono? } IF whatToLearn <> 0 THEN BEGIN { Mi stan chiedendo di imparare un incantesimo che conosco giˆ? } FOR i := 1 TO numKnown DO IF alreadyKnown [i] = whatToLearn THEN BEGIN DoSoundAsync (sndImpossible); LearnNewSpells := FALSE; GenericDreamAlert (kAlreadyKnowsThisSpell); Exit (LearnNewSpells); END; { Mi stanno chiedendo di imparare un incantesimo di tipo sbagliato? } { Bug fix 1.6: Usava ancora il vecchio inadeguato algoritmo della v1.0 } SpellID2SpellData (whatToLearn, iType, spellKind); { Se iType = 0, spell non imparabile. spellKind = mago o chierico } IF (iType = 0) OR (isCleric <> spellKind) THEN BEGIN DoSoundAsync (sndImpossible); LearnNewSpells := FALSE; GenericDreamAlert (kWrongKindOfSpell); Exit (LearnNewSpells); END; END; { Fine dei check quando posso imparare uno specifico inc. } { 2. loading and setup of dialog box } myDlog := GetNewDialog(resSpellLearnDialog,NIL,WindowPtr(-1)); IF myDlog = NIL THEN DeathAlert (errMissingApplRes, -192); GetPort (oldPort); SetPort (myDlog); { 3. Set the draw procedures for my user items. A. Displays the char's icon. } SetItemProcedure (myDlog, kIconUserItem, @DrawClassIcon); {install draw proc} { B. Lists } SetItemProcedure (myDlog, kBookList, @DrawList); {install list proc} SetItemProcedure (myDlog, kMayLearnList, @DrawList); {install list proc} { 4. Crea le liste } GetItemRect (myDlog, kBookList, iBox); bookList := NuovaLista (myDlog, iBox, 1, WantVScroll+WantAutoScroll, 0); c.h := 0; c.v := 0; { se gli incantesimi sono parecchi, il ciclo sfarfalla. Quindi disabilito temporaneamente il ridisegno sulla lista } LSetDrawingMode (FALSE, bookList.theList); FOR i := 1 TO numKnown DO BEGIN nomeSpell := SpellID2SpellName (alreadyKnown[i]); NuovaCella (bookList, c, @nomeSpell); c.v := succ (c.v) END; LSetDrawingMode (TRUE, bookList.theList); GetItemRect (myDlog, kMayLearnList, iBox); mayLearnList := NuovaLista (myDlog, iBox, 1, WantVScroll+WantAutoScroll, LOnlyOne + LNoNilHilite); c.h := 0; IF whatToLearn <> 0 THEN BEGIN c.v := 0; nomeSpell := SpellID2SpellName (whatToLearn); listOfSpellsThatMayBeLearned[1] := whatToLearn; NuovaCella (mayLearnList, c, @nomeSpell) END ELSE BEGIN { se gli incantesimi sono parecchi, il ciclo sfarfalla. Quindi disabilito temporaneamente il ridisegno sulla lista } LSetDrawingMode (FALSE, mayLearnList.theList); cursorToUnknown := 1; { Costruisco un array di spell imparabili } FOR i := 1 TO maxLevel DO BEGIN { Scorri la lista degli inc. esistenti di qs. livello } spellLoop := allSpells[i, isCleric]; WHILE spellLoop <> NIL DO BEGIN { Conosce giˆ qs. incantesimo? } isUnknown := TRUE; j := 0; WHILE isUnknown AND (j < numKnown) DO BEGIN j := succ (j); isUnknown := (alreadyKnown[j] <> spellLoop^^.id) END; { Se si, aggiungi in lista } IF isUnknown THEN BEGIN listOfSpellsThatMayBeLearned[cursorToUnknown] := spellLoop^^.id; c.v := cursorToUnknown-1; NuovaCella (mayLearnList, c, @spellLoop^^.name); cursorToUnknown := succ(cursorToUnknown); END; { Passa al prox. inc. in lista } spellLoop := spellLoop^^.next END; { While che scorre } END; { for } { Fine inserimento. Ora disegna la lista } LSetDrawingMode (TRUE, mayLearnList.theList); { Se non c' nessun incantesimo imparabile, esci subito. } IF cursorToUnknown = 1 THEN BEGIN item := Cancel; GOTO 9; END; END; { 5. Get ready with the text } ParamText (nome, IToS (numToLearn), '', ''); { 6. Var init } numLearned := 0; ClearFamily (noItems); allItems := NoItems; FOR j := kStdOkItemIndex TO kMayLearnList DO allItems[j] := TRUE; GetItemRect (myDlog, kNumSpellsToLearn, iBox); { Il rect serve a DoÉ } { 7. User interface init } DisableDialogItem (myDlog, kUndoLearn); { Il bottone Undo va abilitato solo quando ha giˆ fatto qualcosa } DisableDialogItem (myDlog, OK); { Il bottone OK va abilitato solo quando ha imparato il numero corretto di incantesimi } IF whatToLearn = 0 THEN DisableDialogItem (myDlog, Cancel);{ Il bottone cancel non ha senso se sta imparando gli incantesimi per passaggio liv } ShowWindow (myDlog); { 8. Go with DialogLord } REPEAT item := DialogLord (myDlog, kLastItem, noItems, noItems, allItems, noItems, noItems, noItems, noItems, 0, e); CASE item OF kUndoLearn: UndoAll; kMayLearnList: CASE FindList (@mayLearnList, e, c, @bookList) OF DoubleClick, DraggedToOther: IF numLearned < numToLearn THEN DoSelectNewSpell (c.v+1); END; { case } kBookList: IF FindList (@bookList, e, c, NIL) = Click THEN ; END; { case } UNTIL item <= Cancel;9: { 9. Free memory } ListaShutdown (mayLearnList); ListaShutdown (bookList); SetPort (oldPort); ResetItemProcedure (myDlog, kIconUserItem); ResetItemProcedure (myDlog, kBookList); ResetItemProcedure (myDlog, kMayLearnList); DisposeDialog (myDlog); { 10. If result is OK, then sort the array and transcribe the event } IF item = OK THEN BEGIN FOR i := numKnown+1 TO numKnown+numLearned DO AddToTranscript (nome, ktLearnsSpell, SpellID2SpellName(alreadyKnown[i]), 0); numKnown := numKnown + numLearned; FOR i := 1 TO numKnown-1 DO FOR j := numKnown DOWNTO i+1 DO IF alreadyKnown[i] > alreadyKnown[j] THEN BEGIN iType := alreadyKnown[i]; alreadyKnown[i] := alreadyKnown[j]; alreadyKnown[j] := iType END; { bubblesort swap } END; { if ok } { 11. return result } LearnNewSpells := item = OKEND;VAR { Per l'uso di DrawRefillList e di RefillSpells } RSbookList, RSmindList: ListInfoRec;{$S DefProcs}PROCEDURE DrawRefillList (where: DialogPtr; item: INTEGER);VAR myRect: Rect;BEGIN IF item = kRSBookList THEN Ridisegna (RSbookList) ELSE Ridisegna (RSmindList);END;{$S Magic}PROCEDURE RefillSpells (VAR bookOfSpells: KnownSpellsArray; numKnown: Integer; VAR mindOfChar: MemorizedSpellsArray; maxSpells: NumSpellsArray);(*** Anche qui va messo SELF nella var globale per DrawCharIcon!!! ***)VAR myDlog: DialogPtr; oldPort: GrafPtr; aSpellLevel, numInMind, iType, i, j, item: Integer; iBox: Rect; nomeSpell, nomeCaster: String; c: Cell; e: EventRecord; noItems, allItems: Family; stillAvailable: NumSpellsArray; { Num inc ancora da memorizzare prima di essere pieno } dummy: Boolean; PROCEDURE ShowSpellNumbers; VAR allInAll, stillToDo: String; i: Integer; BEGIN { Crea la stringa con il num max di inc. memorizzabile } allInAll := ''; FOR i := 1 TO kMaxSpellLevel DO allInAll := concat (allInAll, IToS (maxSpells[i]), ' '); { Crea la stringa con il num di inc. ancora da memorizzare } stillToDo := ''; FOR i := 1 TO kMaxSpellLevel DO stillToDo := concat (stillToDo, IToS (stillAvailable[i]), ' '); { Display } ParamText (nomeCaster, allInAll, stillToDo, ''); InvalRect (iBox); END; PROCEDURE DoForgetSpell (serialInMind: Integer); VAR i: Integer; levelOfThisSpell: 1..kMaxSpellLevel; BEGIN { Toglilo dal video } CancellaCella (RSmindList, c); { Aggiungi un incantesimo al numero ancora disponibile } levelOfThisSpell := (mindOfChar[serialInMind] MOD 100) DIV 10 + 1; stillAvailable[levelOfThisSpell] := succ (stillAvailable [levelOfThisSpell]); numInMind := pred (numInMind); { Aggiorna il display del num. inc. ancora da memorizzare } ShowSpellNumbers; { Ricompatta il vettore degli incantesimi in memoria } FOR i := serialInMind+1 TO kMaxSpellInMemory DO mindOfChar [i-1] := mindOfChar [i] END; PROCEDURE DoMemorizeSpell (serialInBook: Integer); { é stato selezionato l'incantesimo numero serialInBook tra quelli memorizzati in bookOfSpells. c contiene la cella selezionata. é selezio- nata la lista del libro } VAR levelOfThisSpell: 1..kMaxSpellLevel; BEGIN { 1. Pu˜ farlo? Ha ancora spazio in memoria per un inc. di qs. livello? } levelOfThisSpell := (bookOfSpells[serialInBook] MOD 100) DIV 10 + 1; IF stillAvailable [levelOfThisSpell] < 1 THEN BEGIN DoSoundAsync (sndImpossible); Exit (DoMemorizeSpell) END; { 2. Metti in memoria e nella lista della memoria} c.v := numInMind; nomeSpell := SpellID2SpellName (bookOfSpells[serialInBook]); NuovaCella (RSmindList, c, @nomeSpell); numInMind := succ (numInMind); mindOfChar[numInMind] := bookOfSpells[serialInBook]; { 3. Tieni nota di quali e quanti inc. ha selezionato } stillAvailable [levelOfThisSpell] := pred (stillAvailable [levelOfThisSpell]); { 4. Invalida lo spazio relativo di modo che appaia il num corretto di incantesimi ancora da memorizzare } ShowSpellNumbers END; BEGIN { 1. loading and setup of dialog box } myDlog := GetNewDialog(resRefillSpellDialog,NIL,WindowPtr(-1)); IF myDlog = NIL THEN DeathAlert (errMissingApplRes, -192); GetPort (oldPort); SetPort (myDlog); { 2. Set the draw procedures for my user items. A. Displays the char's icon. } SetItemProcedure (myDlog,kRSCharIcon,@DrawClassIcon); { B. Lists } SetItemProcedure (myDlog,kRSBookList,@DrawRefillList); SetItemProcedure (myDlog,kRSMindList,@DrawRefillList); { 3. Get the name of the caster - for displaying } nomeCaster := thisCharacter.nome; { Bug fix 1.6.1 - Paladin's bless non va considerato! } IF mindOfChar[1] = kSpelSpecialPaladin THEN BEGIN { Toglilo di mezzo, tanto verrˆ reinserito pi tardiÉ } FOR i := 2 TO kMaxSpellInMemory DO mindOfChar[i-1] := mindOfChar[i]; mindOfChar[kMaxSpellInMemory] := 0 END; { 4. Crea le liste } GetItemRect (myDlog, kRSBookList, iBox); RSbookList := NuovaLista (myDlog, iBox, 1, WantVScroll+WantAutoScroll, 0); c.h := 0; c.v := 0; { se gli incantesimi sono parecchi, il ciclo sfarfalla. Quindi disabilito temporaneamente il ridisegno sulla lista } LSetDrawingMode (FALSE, RSbookList.theList); FOR i := 1 TO numKnown DO BEGIN nomeSpell := SpellID2SpellName (bookOfSpells[i]); NuovaCella (RSbookList, c, @nomeSpell); c.v := succ (c.v) END; LSetDrawingMode (TRUE, RSbookList.theList); { Ciclo in cui conto quanti incantesimi sono giˆ in memoria e quanti sono ancora caricabili. Inoltre creo la lista a video. } stillAvailable := maxSpells; { Inizialmente suppongo che non ne abbia } GetItemRect (myDlog, kRSMindList, iBox); RSmindList := NuovaLista (myDlog, iBox, 1, WantVScroll+WantAutoScroll, LOnlyOne + LNoNilHilite); { se gli incantesimi sono parecchi, il ciclo sfarfalla. Quindi disabilito temporaneamente il ridisegno sulla lista } LSetDrawingMode (FALSE, RSmindList.theList); i := 1; c.v := 0; WHILE (mindOfChar[i] <> 0) & (i <= kMaxSpellInMemory) DO BEGIN { Decrementa il vettore che tiene traccia di cosa pu˜ ancora prendere } { Bug fix 1.6.2: usava l'algoritmo di dream 1.0 per calcolare il livello degli incantesimi } SpellID2SpellData (mindOfChar[i], aSpellLevel, dummy); stillAvailable [aSpellLevel] := pred (stillAvailable [aSpellLevel]); { Inserisci pure, sia nella lista a video che nel vettore in mem } nomeSpell := SpellID2SpellName (mindOfChar[i]); NuovaCella (RSmindList, c, @nomeSpell); c.v := succ (c.v); i := succ (i); END; { while Spell exists in mind } IF mindOfChar[i] = 0 THEN numInMind := i-1 ELSE numInMind := i; { Fine inserimento. Ora disegna la lista } LSetDrawingMode (TRUE, RSmindList.theList); { 5. Get ready with the text } GetItemRect (myDlog, kRSNumSpells, iBox); { Il rect serve a DoÉ } ShowSpellNumbers; { 6. Var init } ClearFamily (noItems); allItems := noItems; allItems[kStdOkItemIndex] := TRUE; allItems[kRSMindList] := TRUE; allItems[kRSBookList] := TRUE; { 7. User interface init } ShowWindow (myDlog); { 8. Go with DialogLord } REPEAT item := DialogLord (myDlog, kRSLastItem, noItems, noItems, allItems, noItems, noItems, noItems, noItems, 0, e); CASE item OF kRSMindList: CASE FindList (@RSmindList, e, c, @RSmindList) OF DraggedOut: DoForgetSpell (c.v+1) END; { case mind list } kRSBookList: CASE FindList (@RSBookList, e, c, @RSmindList) OF DoubleClick, DraggedToOther: DoMemorizeSpell (c.v+1); END; { case book list } END; { case item of } UNTIL item = kStdOkItemIndex; { 9. Free memory } ListaShutdown (RSmindList); ListaShutdown (RSbookList); SetPort (oldPort); ResetItemProcedure (myDlog,kRSCharIcon); ResetItemProcedure (myDlog,kRSBookList); ResetItemProcedure (myDlog,kRSMindList); DisposeDialog (myDlog); { 10. If result is OK, then sort the array and transcribe the event } FOR i := 1 TO numInMind-1 DO FOR j := numInMind DOWNTO i+1 DO IF mindOfChar[i] > mindOfChar[j] THEN BEGIN iType := mindOfChar[i]; mindOfChar[i] := mindOfChar[j]; mindOfChar[j] := iType END; { bubblesort swap }END;{$S Magic}FUNCTION SpellCheck (fightTime: Boolean): Boolean;VAR theSpell: TIncantesimo; result: Boolean; { Risultato della funzione }BEGIN theSpell := attackAdditionalInfo.attackingSpell; result := TRUE; { salvo eccezioni, non finisce qui } IF theSpell = NIL THEN DeathAlert (0, 6666); { Ha senso il casting in questo momento? } IF fightTime & NOT theSpell.specifiche[2] THEN BEGIN result := FALSE; SpellAlert (kCantCastInFight, 0); END; IF NOT fightTime & NOT theSpell.specifiche[1] THEN BEGIN result := FALSE; SpellAlert (kCantCastInPeace, 0) END; IF result & NOT fightTime & (theSpell.targetType IN [OneMonster, AllMonsters..StraightBolt]) THEN DeathAlert (errFaultySpell, 0); { 5. Showdown } IF NOT result THEN BEGIN theSpell.Free; attackAdditionalInfo.attackingSpell := NIL END; SpellCheck := resultEND;{$S Magic}PROCEDURE GiveSpellToCreature (theSpell: TIncantesimo; theCreat: TCreatura);BEGIN { D“ all'inc. chi il suo target } theSpell.target := theCreat; { Dai al target l'incantesimo } theSpell.nextSpell := theCreat.activeSpells; theCreat.activeSpells := theSpell;END;{$S Magic}PROCEDURE GiveCloneToCreature (theSpell: TIncantesimo; theCreat: TCreatura);VAR theClone: TIncantesimo;BEGIN theClone := NIL; theClone := TIncantesimo (theSpell.Clone); FailNIL (theClone); GiveSpellToCreature (theClone, theCreat)END;END. { Unit }