Salta ai contenuti

Script forniti

Questa pagina fa parte di Scrivere script Lua. La distribuzione fornisce questi script sotto scripts/, e mgctl init li copia nella root: ciascuno è un esempio da leggere e modificare. Come uno script viene associato a un template è spiegato in Script dei mobile e Script degli oggetti, dove sono elencati wander.lua e potion.lua.

Usa local cooldown = require("common.cooldown") per controllare una scadenza con le stesse regole negli script. Passa item, mobile o npc come archivio per usare le proprietà salvate esistenti; le chiavi restano quelle fornite. Crea un archivio separato cooldown.memory() per lo stato temporaneo, come l’attesa fra pozioni di cura.

Funzione Risultato
cooldown.ready(store, serial, key, now?) True se non c’è una scadenza valida o è stata raggiunta
cooldown.remaining(store, serial, key, now?) Secondi fino alla scadenza, mai sotto zero
cooldown.start(store, serial, key, seconds, now?) Salva now + seconds; true solo se l’archivio accetta la scrittura
cooldown.clear(store, serial, key) Rimuove la scadenza; true solo se l’archivio accetta la scrittura
cooldown.memory() Un nuovo archivio indipendente senza persistenza
local cooldown = require("common.cooldown")
cooldown.start(mobile, player, "daily.reward.next", 86400)
local seconds_left = cooldown.remaining(mobile, player, "daily.reward.next")
local temporary = cooldown.memory()
cooldown.start(temporary, player, "heal", 11)
local can_heal = cooldown.ready(temporary, player, "heal")

Il valore opzionale now è un numero finito per controlli deterministici; altrimenti l’helper usa world.now(), secondi UTC interi. Alla scadenza esatta è pronto. Le durate devono essere finite e non negative; zero è pronto subito. I seriali devono essere interi positivi a 32 bit e le chiavi stringhe non vuote né composte solo da spazi. Gli argomenti non validi generano un errore Lua prima della scrittura. Una scadenza salvata assente, non numerica o non finita viene considerata pronta. Un seriale valido senza entità può quindi risultare pronto, mentre start restituisce comunque false se l’archivio nativo non può scrivere: questo helper non verifica l’accesso agli oggetti o il diritto a una ricompensa.

Le scadenze nelle proprietà seguono la persistenza del salvataggio del mondo. Gli archivi in memoria appartengono al consumatore che li conserva: un nuovo archivio è vuoto, non viene salvato e non viene svuotato automaticamente al logout. Chiama clear quando lo stato non serve più. Ricaricare un consumatore che ricrea il proprio archivio locale azzera quello stato; ricaricare solo l’helper non cancella un archivio esistente. Le correzioni dell’orologio influiscono sulle scadenze UTC.

Gli script forniti trick_or_treat.lua e fillable.lua mantengono le proprietà esistenti trick_or_treat.next e fill.next. potion.lua mantiene in memoria l’attesa di cura. Questo helper non programma callback: usa timer.after o il persistente item.start_timer quando del codice deve essere eseguito più avanti.

Usa local random = require("common.random") per scegliere valori da liste senza ripetere la logica. L’helper usa math.random per impostazione predefinita e non ne imposta mai il seed. Ogni chiamata accetta una funzione RNG opzionale, utile per test deterministici.

Funzione Risultato
random.choice(list, roll?) Un valore da una lista contigua; nil se la lista è vuota
random.chance(probability, roll?) True con probabilità da 0 a 1; 0 è sempre false e 1 sempre true
random.weighted(entries, roll?) Un value in base al suo weight intero; nil se il totale è zero
local random = require("common.random")
local color = random.choice({ "red", "blue" })
local succeeds = random.chance(0.25)
local weighted_color = random.weighted({
{ value = "red", weight = 60 },
{ value = "blue", weight = 40 },
})
local predictable = random.choice({ "red", "blue" }, function(count) return count end)

Le liste devono avere chiavi intere consecutive da 1, senza buchi o campi aggiuntivi. I pesi devono essere interi non negativi e il loro totale non deve superare 2.147.483.647. Una voce pesata richiede un valore diverso da nil; false è un valore valido. Le voci con peso zero non vengono mai selezionate. Argomenti o risultati RNG non validi generano un errore Lua.

Per choice e weighted, roll(count) deve restituire un intero da 1 a count inclusi. Per chance, roll() deve restituire un numero in [0, 1); il successo richiede un valore strettamente inferiore alla probabilità. Liste vuote, peso totale zero ed estremi della probabilità non effettuano estrazioni. L’RNG predefinito viene risolto a ogni chiamata, quindi si può sostituire il generatore nei test senza modificare i seed globali.

Halloween usa choice per battute, caramelle e graphic del sangue. Natale usa weighted per la distribuzione 60/24/16 delle decorazioni, mantenendo i limiti delle estrazioni intere originali. Per la notazione dei dadi, continua a usare il modulo nativo dice.roll e dice.try_roll.

Il file scripts/mobiles/monster.lua della distribuzione è lo script dei mostri che attaccano i giocatori, seguendo l’IA corpo a corpo di ModernUO: insegue un giocatore e lo combatte standogli accanto. Un template lo adotta con script_id = "monster"; lo fanno le creature il cui NPCAI di UOX3 è malvagio, incantatore malvagio o caotico (gli orchi, gli orchi giganti, i lucertoloni, i draghi, i non morti e gli altri 200 circa template e quelli basati su di essi; migrazione da UOX3); i buoni combattenti e incantatori, che combattono solo i criminali, no. Gli incantatori, tra cui wraith, spectre e lich, lanciano da lontano gli incantesimi di Magery (common/caster.lua) quando il loro template imposta spell_circles. Il comportamento è il modulo condiviso common/creature.lua, creature.new({ hunts = true }). Un mostro si trova in uno di tre stati:

Stato Cosa fa Termina quando
wander Passeggia nella propria casa, l’area della regione di spawn: circa un passo ogni due secondi, soprattutto dritto. Passeggia con npc.wander, che lo riporta dall’esterno, come dopo un inseguimento. Un ciclo di pensiero su venti riposa da 15 a 25 secondi, con il suono idle e un movimento inattivo Vede un giocatore
chase Minaccia il giocatore con il suono start_attack e un’animazione, passa in modalità guerra e gli si avvicina con npc.walk_to, un passo ogni ciclo di pensiero, senza correre. Accanto a lui si orienta verso di lui e lo combatte con combat.attack, una volta: colpi, impatti e morte sono del servizio di combattimento Il giocatore si nasconde, se ne va, dista più di 32 caselle o non si avvicina per 20 secondi (fuori portata)
guard Smette di combattere, resta in modalità guerra per 10 secondi guardandosi intorno Vede un giocatore, oppure il tempo termina: torna a wander, in pace

Un mostro colpito, o mancato, risponde all’attacco (se ne occupa il servizio di combattimento) e si rivolge a chi lo combatte qualunque cosa stesse facendo, passeggiando o facendo la guardia, anche se non lo aveva visto: passa in modalità guerra e lo insegue, senza minacciarlo di nuovo. Uno che già combatte si rivolge a un nuovo aggressore che non gli è nascosto ed è più vicino del suo bersaglio, come decide il servizio di combattimento; una creatura domata mantiene il bersaglio che le è stato ordinato.

Un inseguimento segue la ricerca del percorso, che aggira muri e porte chiuse e, quando la preda non si può raggiungere, porta al punto più vicino; non trova mai una preda che non ha in linea di vista, ma segue una che ha visto entro 32 caselle. Un passo conta solo quando avvicina la creatura alla preda più di quanto lo sia stata in questo inseguimento: 40 think, 20 secondi, senza un passo simile, sia che resti ferma, sia che il passo venga rifiutato, sia che vada avanti e indietro lungo un muro dietro una preda che non può raggiungere (dentro una casa chiusa a chiave, su un tetto, oltre un fiume), le fanno rinunciare alla preda, restare di guardia e poi passeggiare. Una preda che si muove può essere ripresa alla vista successiva, perché è un nuovo inseguimento. Il suo stato non viene salvato: dopo un riavvio una creatura riparte dal passeggiare, e ciò che una mente le aveva lasciato (correre, modalità guerra) viene azzerato.

Cerca una preda ogni due secondi mentre passeggia e ogni secondo quando fa la guardia, tra le prime sei di npc.mobiles_in_sight: entro 16 caselle e in linea di vista, da occhio a occhio, la più vicina per prima. La sua preda è qualsiasi giocatore e, tra gli NPC, quelli con il nome blu, i cittadini, mobile.notoriety innocente: un mostro in una città attacca la gente per strada oltre ai giocatori. Lascia in pace quelli gialli, venditori, banchieri e guardie, che non si possono ferire, e le altre creature, animali o mostri. Non vede mai un giocatore nascosto, un fantasma, un game master o un amministratore. Una volta che insegue la preda la segue senza vederla (npc.can_see con in_sight false), fino al limite di inseguimento. Una preda che non riesce a raggiungere viene lasciata in pace finché non si muove. La sua minaccia e il suo gesto sono le azioni di un corpo da mostro; una creatura con corpo umano o animale, come un brigante, non ne esegue, perché quei corpi numerano le azioni in modo diverso. Ciò che un mostro sta facendo è mantenuto in memoria per seriale, non salvato, e dimenticato quando muore: dopo un riavvio, o quando nessun giocatore è abbastanza vicino da farlo pensare, riparte dal movimento casuale. I numeri (16, 32, i tempi) sono costanti all’inizio del file.

Le guardie cittadine attaccano i mostri, come in ModernUO: guard.lua considera ricercato qualsiasi NPC il cui npc.script_id è monster, come un criminale, a meno che sia morto o domato, e ovunque si trovi: una guardia che un game master ha generato in un cimitero fuori dalle città combatte lì i mostri, senza una postazione da custodire. Un criminale che non è un mostro è ricercato solo in una regione sorvegliata. La guardia appare accanto a lui, lo colpisce e lo uccide con un colpo.

Il file scripts/mobiles/guard.lua della distribuzione è lo script delle guardie cittadine: quelle presenti nelle città tramite spawn e quelle chiamate da un giocatore dicendo “guards” (vedi ultima.crime). Un template lo adotta con script_id = "guard"; lo fanno guard, m_guard e f_guard. Una guardia uccide con un colpo un criminale che è un NPC, come quella di ModernUO, e si limita a stargli addosso se è un giocatore: lo script risparmia i giocatori, anche se ora i giocatori possono morire. Una guardia si trova in uno di due stati:

Stato Cosa fa Termina quando
post Passeggia intorno alla propria postazione, l’area della regione di spawn, circa un passo ogni quattro secondi, con npc.wander, che la riporta anche dall’esterno Vede un criminale: passa ad arrest
arrest Passa in modalità guerra; quando non è accanto al criminale appare su una casella libera a un passo da lui (world.spot_beside; sulla sua se nessuna è libera), con uno sbuffo di fumo dove si trovava e dove arriva e il suono del teletrasporto; dice “Rimpiangerai le tue azioni, porco!” (messaggio 30138). Poi resta sul criminale, rivolta verso di lui, e lo rincorre con npc.walk_to quando si muove. Accanto a un NPC criminale colpisce (un’animazione di attacco) e un secondo dopo l’NPC è morto: mobile.kill, con la guardia come uccisore, quindi muore come qualsiasi altro e lascia il cadavere L’NPC viene ucciso; oppure il criminale è perdonato o il suo tempo termina, si nasconde, lascia la regione sorvegliata, si allontana oltre 24 caselle dalla guardia o dalla postazione, oppure non può essere raggiunto per 10 secondi: ritorno alla postazione, in pace

Cerca un criminale ogni secondo: il giocatore o NPC più vicino di npc.nearby entro 12 caselle il cui mobile.criminal o mobile.is_murderer è true (un nome rosso è ricercato come uno grigio), che si trova in una regione sorvegliata (world.is_guarded; un mostro non domato è ricercato ovunque si trovi) a non più di 24 caselle dalla postazione della guardia (npc.home, nessuna per una guardia generata a mano), e che vede (npc.can_see); solo un criminale costa il controllo lungo la linea di vista. Il riferimento è la postazione, non la guardia, quindi un criminale non può condurre una guardia fuori città passo dopo passo. Un criminale che non riesce a raggiungere viene lasciato in pace finché non si muove. Non vede mai un giocatore nascosto, un game master o un amministratore. Un NPC appena ucciso è ancora presente mentre cade: la guardia non lo attacca di nuovo. Un teletrasporto rifiutato lascia la guardia dov’è, affinché corra verso il criminale.

Una guardia chiamata porta la prop guard.summoned: è già arrivata accanto al criminale e ha pronunciato la battuta, quindi resta su di lui in silenzio senza passeggiare, e quando quel criminale viene lasciato andare non arresta nessun altro e attende di essere mandata via. Ciò che una guardia sta facendo viene mantenuto in memoria per seriale, non salvato. I numeri (12, 24, i 10 secondi) sono costanti all’inizio del file.

La guardia arciere, template archerguard (le guardie chiamate a Ilshenar e Malas, sul modello dell’ArcherGuard di ModernUO, senza il suo cavallo e le sue statistiche: sono quelle della guardia con un arco), è questa guardia con un arco in mano e combat.range di 10. Davanti a un NPC che attacca, un criminale o un mostro, che sia entro la sua portata e nella sua vista, non gli si avvicina: resta dov’è, lo guarda e avvia lo scontro (combat.attack), e il servizio di combattimento spara con l’abilità Archery; l’arresto finisce quando il bersaglio è morto. Fuori portata, o fuori vista, gli si avvicina come ogni guardia, e spara da lì. Con un giocatore è la stessa guardia delle altre: gli si avvicina e gli sta addosso.

Gli animali, secondo l’IA animale di ModernUO, usano lo stesso modulo con hunts = false: passeggiano nella propria casa, ogni tanto si riposano con il suono di inattività e un’animazione, e non attaccano mai un giocatore. scripts/mobiles/animal.lua (script_id = "animal", le creature il cui NPCAI di UOX3 è 6: orsi, lupi e simili) reagisce quando viene colpito, come un mostro. scripts/mobiles/scared_animal.lua (script_id = "scared_animal", NPCAI 12) no: un animale pauroso colpito smette di combattere e fugge, fino a dodici celle e al massimo dieci secondi, lontano da chi l’ha colpito, poi riprende a passeggiare. Il servizio di combattimento fa rispondere al colpo ogni NPC colpito tranne uno che fugge: il modulo delle creature imposta la prop combat.flees su una creatura creata con flees = true, alla sua generazione e di nuovo a ogni think (a una creatura domata viene tolta: combatte quando è colpita, e così una il cui template non fugge più), e il servizio legge la prop, non l’id dello script, quindi qualsiasi script con quell’opzione si comporta allo stesso modo.

scripts/common/creature.lua è ciò che i tre script sopra condividono, preso con local creature = require("common.creature"). creature.new(options) restituisce la tabella che uno script mobile definisce, con il suo on_think; le opzioni sono hunts (attacca i giocatori che vede), prey (una funzione che dice chi attacca, al posto dei giocatori e della gente delle città: la usano le guardie delle terre selvagge), flees (fugge da un colpo invece di rispondere) e flee_at (vedi sotto). Una creatura il cui template imposta spell_circles lancia anche incantesimi, vedi common/caster.lua. I pezzi di una mente che le guardie condividono con essa sono funzioni del modulo: creature.is_given_up(mind, who) e creature.give_up(mind, who, there) (un mobile che non si è potuto raggiungere viene lasciato stare finché non si muove), creature.forget_given_up(mind) (lo dimentica) e creature.clear_stale_war_mode(serial) (un mobile rimasto in modalità guerra dopo un ricaricamento degli script). pet_orders.is_owned(serial) dice se una creatura ha un padrone (domata o evocata).

Arcieri. Una creatura che impugna un arco o una balestra ha combat.range di 10 o 8 (Combattimento): il suo inseguimento si ferma dove la preda è entro quella portata e nella sua linea di vista, npc.can_see, la guarda e avvia lo scontro, e il servizio di combattimento spara. Troppo lontana, cammina finché la preda è un passo dentro la portata; entro la portata ma senza linea di vista, si avvicina. Non arretra davanti a una preda che si avvicina: neppure gli arcieri di ModernUO, a meno che il costruttore lo chieda.

Fuga da feriti, come le creature di ModernUO: una che combatte (non un animale pauroso, che scappa da un colpo) e ha meno di flee_at percento dei punti vita scappa con una probabilità su dieci a ogni think, per 10-30 secondi, dritta lontano dalla preda, correndo. La percentuale è il flee_at del template, altrimenti l’opzione dello script: 20 per un mostro, 10 per un animale; un template con flee_at = -1, come il FLEEAT=-1 di UOX3 dà ai non morti, agli elementali e ai demoni, non scappa mai. Mentre scappa non risponde a un colpo: imposta la prop dell’NPC combat.passive, che il servizio di combattimento legge, e quando finisce torna a passeggiare, e quindi a cacciare di nuovo.

Morsi che sanguinano, come il Bleed Attack classico: una creatura il cui template imposta bleed_chance (il percento) fa sanguinare la vittima di un colpo andato a segno, col modulo bleed — cinque colpi da due secondi, il danno che rallenta da 5–10 a 1–2 e doppio per un PNG, una pozza di sangue sotto la vittima a ogni colpo, e il quinto colpo chiude le ferite. La benda la fascia. Il dire wolf sanguina un colpo su cinque, il lupo grigio uno su dieci.

Calmata da un bardo (Peacemaking): la prop bard.pacified_until tiene il secondo (world.now()) in cui la calma finisce. Il servizio di combattimento la legge per qualsiasi NPC, con o senza script, quindi una creatura calmata non risponde a un colpo; finché dura, creature.lua ferma anche il suo combattimento e le impedisce di cacciare, e passeggia soltanto (una creatura con un padrone continua con i suoi ordini, come la calma di ModernUO che impedisce solo di prendere un bersaglio). Quando il tempo è scaduto la prop viene tolta. Sopravvive a un ricaricamento degli script e a un riavvio. Un colpo spezza la calma con una probabilità di (hits_max - hits) * 0.001, una su mille per ogni punto vita che le manca (on_damage della tabella che crea creature.new: uno script che definisce un proprio on_damage su di essa lo sostituisce).

Provocata da un bardo (Provocation): le prop bard.provoker, bard.provoke_target e bard.provoke_until mettono una creatura contro un’altra. Finché durano, creature.lua la fa inseguire e combattere quella creatura e nessun altro (un combattimento iniziato da chi l’ha colpita viene fermato a ogni think), senza scappare da un colpo. Come in ModernUO, la provocazione finisce subito quando il bersaglio è perso (ha lasciato il mondo, è morto, è su un’altra mappa o più lontano delle 16 tile che la creatura vede) e quando il tempo è scaduto e il bardo è perso (andato, su un’altra mappa o più lontano di 16 tile; un bardo nascosto, o morto, che resta vicino la tiene viva oltre i 30 secondi). La morte toglie le prop.

Una tua creatura lo prende allo stesso modo: mycreature = creature.new({ hunts = true }) in scripts/mobiles/mycreature.lua. Una root i cui script sono sostituiti manualmente ha bisogno anche di scripts/common/, altrimenti le creature smettono di pensare.

scripts/common/caster.lua è il modo in cui combatte una creatura il cui template imposta spell_circles (il cerchio più alto di Magery da cui lancia, da 1 a 8, il SPATTACK di UOX3), secondo le regole del classico mago non intelligente. common/creature.lua chiama caster.think(serial, mind, target, distance) a ogni think dell’inseguimento di una creatura che non è domata né evocata; restituisce true quando la creatura mantiene il suo posto. Una creatura senza spell_circles, o senza Magery, non prende altra strada: caster.highest_circle(serial) è 0 e il mostro combatte come prima.

Dove sta. Entro 12 caselle dalla preda e nella sua linea di vista si ferma, si orienta verso la preda e lancia quando arriva il suo turno; fuori portata o fuori vista insegue come qualsiasi mostro, e così fa quando il suo mana è sotto quello di un incantesimo del primo cerchio (la creatura lo rigenera con la sua Intelligence, come ogni NPC, e nel frattempo combatte in mischia). Accanto alla preda combatte in mischia come un mostro e lancia comunque quando arriva il suo turno. Mentre il ritardo di un incantesimo corre non si muove (npc.step viene rifiutato e npc.walk_to risponde "blocked" subito, con il suo percorso conservato), e un colpo non la disturba.

La scelta. Il cerchio più alto è clamp(floor((Magery + 20) / (100 / 7)), 1, 8) e non oltre spell_circles. Avvelenata, lancia Cure su sé stessa, anche quando la preda è fuori vista; altrimenti, con una probabilità pari a un decimo del suo Magery come frazione, Greater Heal quando le mancano più di 50 punti vita (Heal quando quel cerchio è fuori dalla sua portata o il mana non basta) e Heal quando le mancano più di 10. Una creatura selvatica non ha attesa tra una cura e l’altra: l’attesa del mago classico, sqrt(600 - Intelligence) secondi, è per quelle domate, che qui non lanciano mai. Altrimenti uno su sedici: Poison (due, a meno che la preda sia avvelenata), un potenziamento su di sé (Bless, Strength, Agility o Cunning), una maledizione (Curse una volta su quattro da Magery 40, altrimenti Weaken, Clumsy o Feeblemind; due), Paralyze (Magery sopra 50, una preda non paralizzata), Mana Drain (Mana Vampire una volta su due da Magery 80), e il resto è danno: uno tra un numero di posti doppio dei cerchi, Magic Arrow, Harm, Fireball, Lightning e Mind Blast due volte ciascuno, poi Energy Bolt, Explosion e Flame Strike. Un incantesimo oltre i cerchi che ha, o per cui non ha mana, viene sostituito dal più alto incantesimo di danno che ci sta, oppure non lancia; senza un incantesimo scelto, o con un lancio che il motore ha rifiutato, la creatura combatte come un mostro (insegue e colpisce) e sceglie di nuovo al think successivo. Vengono scelti solo incantesimi che colpiscono un mobile: niente evocazioni, campi, Teleport, Recall, Invisibility o Resurrection.

La pausa. Dopo un lancio, contata dal suo inizio, così che il ritardo dell’incantesimo ne fa parte, il successivo non arriva prima di 6 - 1.25 s o 6 - 0.75 s secondi, s = 3 * Magery / 100: circa da 2,25 a 3,75 secondi a 100 (mai più breve del ritardo dell’ottavo cerchio, 2,25 secondi). I think sono due al secondo, quindi si conta in think. Un NPC non ha alcun recupero dopo un lancio nel motore: questa pausa è la sua sola.

Il lancio è spell.cast_at (Magery): gli script degli incantesimi sono quelli del giocatore, e così sono il mana e il controllo di Magery (un fallimento); niente reagenti e niente parole. caster.random(count) e caster.chance() sono i dadi, math.random, che un test sostituisce.

Il repository fornisce anche due gatti di Moongate v2, orione e vega (templates/mobiles/moongate_cats.toml con scripts/mobiles/orione.lua e vega.lua): generali con .spawn orione o .spawn vega.

La distribuzione fornisce anche scripts/items/door.lua, lo script del template decoration_door che .decorate assegna a porte e cancelli. Un doppio clic su una porta chiusa apre lei e la porta collegata (prop door.link): la grafica passa alla successiva, la porta si sposta di lato secondo la prop facing e riproduce il suono del suo decoration_type (metallo, legno, cancello o segreta). Un doppio clic su una porta aperta chiude entrambe quando nessuno si trova in uno dei due vani. Una porta aperta si chiude da sola dopo 20 secondi, poi riprova ogni 10 secondi mentre il vano è occupato. Una porta che non può spostarsi di lato, come una al bordo della mappa, resta chiusa. Un NPC che cammina verso un luogo e trova una porta chiusa sulla sua strada la apre tramite on_npc_use(serial, opener): la porta e quella collegata si aprono e si richiudono da sole come per un giocatore, e una porta chiusa a chiave, o una porta doppia con una delle due ante a chiave, resta chiusa senza una parola. Lo stato aperto è la prop door.open, con la posizione chiusa in door.x, door.y e door.z, salvata con la porta, così come il timer di chiusura automatica (il timer close della porta, avviato con item.start_timer): una porta lasciata aperta quando il server si arresta si chiude quando torna attivo. Una porta salvata aperta da una versione precedente non ha timer e resta aperta finché qualcuno la usa. Una porta chiusa con la prop locked non si apre per i giocatori, che leggono “È chiuso a chiave.” (messaggio 398, nella lingua del server), a meno che portino in qualsiasi punto dello zaino una chiave la cui prop key.value è il key.value della porta (messaggio 405: la aprono e resta chiusa a chiave); game master e amministratori la aprono (messaggio 404). La prop proviene dai dati della decorazione (props = { facing = "west_cw", locked = true }), come le porte laterali della banca di New Haven. .lock assegna a una porta un numero di chiave e .key ne crea la chiave.

scripts/items/light.lua accende e spegne candele, candelabri, lanterne, lampioni, applique e torce: lo usano il template decoration_light e i template delle luci di templates/items. Un doppio clic su una luce spenta le assegna la grafica accesa (le coppie di ModernUO), una forma della luce se non ne ha e il suono 0x47; un doppio clic su una accesa le assegna la grafica spenta e il suono 0x3BE, conservando la forma per la volta successiva. Una luce senza grafica spenta, come un braciere, resta com’è. Le luci posizionate da .decorate hanno la prop protected: solo game master e amministratori le accendono o spengono. I lampioni cittadini si accendono e spengono da soli: ogni 30 secondi il server chiama on_darkness(serial, dark) su un lampione il cui punto è diventato buio o luminoso (ultima.world.lamp_post_light), e light.lua cambia la grafica silenziosamente.

scripts/items/kindling.lua è l’abilità Camping, come il Kindling di ModernUO: il bottone dell’abilità non fa nulla, il legnetto sì. Un doppio clic su un legnetto a portata — portato, o a terra entro 2 caselle — prova skill.check(user, "camping", 0, 100); un fallimento legge “You fail to ignite the campfire.” (501696). Al successo un pezzo dello stack è speso e un falò è acceso dove giace il legnetto, per quello a terra, o sul primo lato del giocatore su cui si può stare e dove non sta nessuno (world.standing_z e world.can_fit; il fuoco prende l’altezza di quel pavimento), e senza nessuno la richiesta è rifiutata con “There is not a spot nearby to place your campfire.” (501695). Un legnetto sollevato sul cursore non si usa. Il fuoco è il template campfire (0xDE3, non spostabile; il suo script e decays = false sono stati aggiunti al template in templates/items/building/lighting.toml), acceso con circle300.

scripts/items/campfire.lua accompagna il fuoco alla sua fine, come quello di ModernUO, con i timer nominali che il legnetto avvia: 60 secondi che brucia, 30 che muore (0xDE9, circle150), 10 spento (0xDEA, nessuna luce), e l’oggetto è cancellato. Mentre brucia o muore è una fonte di calore per cucinare (common/heat.lua conta entrambe le sue grafiche tra i fuochi). Il giaciglio e il logout immediato delle ere successive non sono costruiti.

scripts/items/food.lua è lo script di ciò che si può mangiare, come Food di ModernUO: i template convertiti del cibo contengono script_id = "food". Il doppio clic su un pezzo ne mangia uno: il valore di fame del giocatore aumenta della prop food.fill dell’oggetto (3 in sua assenza), fino a 20; recupera da 6 a 8 punti di stamina, più uno per ogni 5 punti del riempimento, emette il suono e, con un corpo di tipo Human (mobile.body_type), il gesto di mangiare, e legge quanto si sente sazio nella lingua del client (messaggi da 500868 a 500872). Un giocatore sazio legge “Sei semplicemente troppo pieno per mangiare ancora!” (500867) e non mangia nulla. Una prop poison.level — quella che vi mette l’abilità Poisoning — avvelena chi mangia il pezzo.

scripts/items/drink.lua è lo script di ciò che si può bere, come le bevande di ModernUO: i template convertiti delle bevande contengono script_id = "drink". Un doppio clic ne beve un sorso: il valore di sete del giocatore aumenta della prop drink.fill dell’oggetto (3 in sua assenza), fino a 20, ed emette il suono e, con un corpo di tipo Human, il gesto di bere. La grafica indica quanti sorsi contiene un contenitore pieno: una brocca o una bottiglia 5, una caraffa 10, un bicchiere o una tazza 1; i sorsi rimasti vengono mantenuti nella prop drink.uses. Una volta vuota, una brocca, un bicchiere o una tazza passa alla grafica vuota, viene rinominata e resta; una bottiglia o caraffa scompare. Un giocatore dissetato legge “Sei semplicemente troppo pieno per bere ancora!” e non beve nulla. Riempimento, versamento e ubriachezza non sono ancora presenti.

Cinque abilità della finestra delle abilità, in scripts/skills/, come in ModernUO. Il giocatore sceglie un bersaglio e legge i testi del client come messaggi di sistema (ModernUO li mostra sopra chi è esaminato). Ognuna aspetta il delay di data/skills.toml. La quinta, Arms Lore, legge un oggetto: arms_lore.lua.

  • anatomy.lua: un mobile entro 8 caselle; il controllo da 0 a 100 che riesce dice quanto sembra forte e agile e, da 65 punti, quanta resistenza gli resta. Ciò che legge è sbagliato fino a 25 meno uno ogni 4 punti dell’abilità. Un controllo fallito legge che non riesce a farsi un’idea delle sue caratteristiche fisiche; se stessi, un PNG invulnerabile e un oggetto hanno testi propri.
  • evaluating_intelligence.lua: lo stesso per la mente: da 0 a 120, “He”, “She” o “It” (mobile.is_female) e, da 76 punti, il mana rimasto; sbagliato fino a 20 meno uno ogni 5 punti.
  • forensic_evaluation.lua: un cadavere entro 10 caselle, da 0 a 100: un cadavere umano dice da chi è stato ucciso (corpse.killer e corpse.killer_name, “no one” se non da qualcuno); quello di un animale o di un mostro legge “You notice nothing unusual.”. Un mobile, da 40 a 100, “You notice nothing unusual.”, perché non c’è una gilda dei ladri. Chi ha disturbato il cadavere e chi lo ha studiato prima non viene ancora registrato.
  • detecting_hidden.lua: un luogo entro 12 caselle, o se stessi: ogni giocatore o PNG nascosto entro un decimo dell’abilità in caselle (la metà se il controllo fallisce, e nessuna sotto 10 punti) viene mostrato se l’abilità del cercatore più un tiro da -10 a 10 non è inferiore al suo Hiding più il proprio; legge “You have been revealed!”. Lo staff viene trovato solo da altro staff; l’abilità aspetta 10 secondi. Un contenitore trappolato scelto direttamente — la prop trap.type — è nominato «[trapped]» (500813), come un’etichetta sopra il forziere, quando il cercatore gli sta entro un decimo della sua abilità in caselle e il controllo da 0 a 100 passa: nominato, non disarmato (lo fa remove_trap). Case e fazioni non ci sono ancora.

scripts/skills/arms_lore.lua è l’abilità Arms Lore, come in ModernUO. on_use chiede “What item do you wish to get information about?” (500349) e dà il cursore: il pezzo deve essere del giocatore, portato o indossato, oppure stare a terra entro 2 caselle (ciò che item.in_range dice di un oggetto a terra); qualunque altra cosa indicata non dice nulla, e una cosa che non è né arma né armatura dice “This is neither weapon nor armor.” (500352). Il controllo da 0 a 100 che fallisce dice “You are not certain…” (500353). Un successo legge la condizione del pezzo e poi il suo valore in combattimento, con i testi del client:

  • La condizione, quando il suo massimo è noto (le prop durability e max_durability del pezzo, al max_hits del suo template), è il decimo dei suoi punti rimasti (1038285 + esso): il nono di un pezzo intero, e 1038294 dice “is in perfect condition.”
  • Il valore di un’arma è la metà tra il suo damage_min e il suo damage_max (ciò che item.combat_stats legge dal suo template), un livello ogni cinque fino a trenta e nessuno sotto tre, uno in più per un’arma a due mani, sulla base del suo tipo: una spada, un’ascia o un’arma in asta taglia (1038220), una mazza schiaccia (1038222), un’arma da scherma trafigge (1038218), un arco, una balestra o un’arma da lancio si scaglia (1038224), un’arma senza tipo ha la base semplice (1038216).
  • La protezione di un pezzo di armatura è il quinto del suo valore, che i testi fermano a 35 (1038295 + esso).

Non ci sono ancora: i pezzi portati o indossati da un altro, per cui c’è l’abilità Snooping (ModernUO li legge anch’essa), il veleno di un’arma (Moongate non ne ha da leggere), la barda di un drago di palude e le proprietà degli oggetti di AOS. L’abilità aspetta il secondo di data/skills.toml.

scripts/skills/tracking.lua è l’abilità Tracking, come in ModernUO. on_use toglie la freccia dell’ultima ricerca (arrow.clear), dice “What do you wish to track?” (1011350) e apre il gump track_what (templates/gumps/track_what.xml): animali, mostri, PNG umani o giocatori. Il gump non si chiama tracking perché un gump risponde alla tabella di script del proprio id, e quel nome è quello dell’abilità. Il suo script, scripts/gumps/track_what.lua, prova skill.check(user, "tracking", 0, 21.1): un fallimento dice “You see no evidence of those in the area.” (1018092), e un successo prova di nuovo l’abilità da 21.1 a 100 (il guadagno passivo di ModernUO).

Un successo guarda fino a 10 tile più 10 per ogni 10 punti di abilità (110 a 100 punti), con world.mobiles_in_range in sguardi di al più 32 tile. La ricerca parte dai 10 tile del raggio di base e raddoppia il riquadro finché l’elenco non è pieno di mobile più vicini del suo bordo, così una folla vicina non viene mai scorsa fino al fondo di un raggio di cento tile; passa per 1500 mobile al massimo, il che la tiene dentro le 150000 istruzioni di una ripresa dello script. Elenca i 12 più vicini del tipo, dal più vicino, con l’immagine del corpo (gump.portrait), il nome e la direzione (i messaggi 30264–30271 di data/messages). I tipi si distinguono con mobile.is_player e mobile.body_type: un animale, un mostro o un umano che non è un giocatore, oppure un giocatore. Non sono elencati: il cercatore stesso, i morti, un membro dello staff nascosto che il cercatore non supera di grado (solo un amministratore trova un game master nascosto, nessuno un amministratore nascosto, come il controllo del livello di accesso di ModernUO) e, per un giocatore, chi ha Hiding e Stealth troppo alti per il Tracking e il Detecting Hidden del cercatore (la probabilità è il numero intero di 50 * (2 * tracking + detecting hidden) / (hiding + stealth) contro un tiro da 0 a 99, la regola Samurai Empire di ModernUO). Nessuno del tipo dice “You see no evidence of animals in the area.” (502991), di creature (502993) o di persone (502995).

Un pulsante dell’elenco mostra la freccia sul posto di quel mobile (arrow.show, con il suo serial per i client dalla 7.0.9.0) e ne dice distanza e direzione (messaggio 30272; messaggio 30273 per un mobile sulla stessa tile, che non ha direzione). Un mobile che ha lasciato il mondo, è più lontano del doppio del raggio o è un membro dello staff nascosto fuori dalla portata del cercatore dice “You have lost your quarry.” (503177) e toglie la freccia di quello prima. La freccia non è provata con un client vero; resta dove è stata messa, il server non segue la sua preda e il clic del client su di essa (per toglierla) non viene letto. L’abilità aspetta 10 secondi. Non ci sono ancora gli elfi che richiedono il doppio dell’abilità, i cambi delle forme del negromante e il bonus di pedinamento.

scripts/items/instrument.lua è lo script dei nove strumenti musicali (il tamburo, i due tamburelli, l’arpa a colonna e quella da grembo, i due liuti e i due flauti di bambù; script_id = "instrument", che il convertitore UOX3 assegna dalla grafica), come il BaseInstrument di ModernUO. Un doppio clic su uno portato addosso, o entro una tile, lo suona: tira Musicianship (skill.check(user, "musicianship", 0, 120), che insegna l’abilità) e il suono è buono con una probabilità pari all’abilità su cento, con il suono di quello strumento secondo la sua grafica (il tamburo 0x38 o 0x39, i tamburelli 0x52 o 0x53, l’arpa a colonna 0x43 o 0x44, l’arpa da grembo 0x45 o 0x46, i liuti 0x4C o 0x4D, i flauti di bambù 0x504 o 0x503). Più lontano dice “That is too far away.” (500446), e lo stesso giocatore aspetta 6 secondi prima di suonare di nuovo (“You must wait to perform another action.” 500119). Suonare a mano non consuma un uso dello strumento, come in ModernUO; le abilità del bardo sì: uno strumento tira da 350 a 450 usi al suo primo uso in un’abilità (la prop dell’oggetto instrument.uses), ogni tiro di un’abilità ne consuma uno, e all’ultimo viene detto “The instrument played its last tune.” (502079) e lo strumento viene eliminato.

scripts/common/bard.lua è ciò che le abilità del bardo condividono. Lo strumento che un giocatore suona è quello su cui ha fatto doppio clic per ultimo, finché è nello zaino o in una borsa dentro di esso (non nella cassaforte della banca, né sollevato sul cursore); altrimenti l’abilità chiede “What instrument shall you play?” (500617) e prende quello indicato (500619 per ciò che non è uno strumento, 1062488 per uno che non è nello zaino). La musica arriva a 8 + skill / 15 tile, con i punti dell’abilità che si usa (Peacemaking per Peacemaking, non Musicianship). La difficoltà di una creatura, come il GetBaseDifficulty di ModernUO, è hits * 1.6 + stamina + mana + skill points (la semplice somma dei punti delle sue abilità), compressa sopra 700 a 700 + (value - 700) * 3 / 11, più 100 per una creatura con incantesimi, divisa per 10 e al massimo 160; i termini di ModernUO per un soffio di fuoco, un’immunità al veleno, la forza di un veleno e un paragon non ci sono. Un tiro contro di essa è l’abilità tra diff - 25 e diff + 25, con diff abbassata della metà della Musicianship oltre 100: la durata di ciò che il tiro dà si conta da quello stesso numero. Venditori, banchieri, guaritori, stallieri e guardie non si possono incantare. Gli strumenti e le abilità del bardo non sono provati con un client vero; non ci sono strumenti slayer ed eccezionali, la fabbricazione degli strumenti e i bardi tra gli NPC.

scripts/skills/peacemaking.lua è l’abilità Peacemaking, come in ModernUO. on_use rivela il bardo, prende lo strumento (vedi sopra), chiede “Whom do you wish to calm?” (1049525) e un bersaglio. Lo strumento deve essere ancora nello zaino quando il cursore risponde (altrimenti 1062488), e un bardo morto nel frattempo non fa nulla. Se stessi è la forma ad area: si tira Musicianship (un suono scadente dice “You play poorly.” 500612), poi l’abilità da 0 a 120 (“You attempt to calm everyone, but fail.” 500613). Un successo ferma il combattimento e la modalità di guerra di ogni mobile nel raggio tranne il bardo, i morti, gli invulnerabili e chi non si può incantare, dice ai giocatori “You hear lovely music, and forget to continue battling!” (500616) e calma le creature per un secondo (500615 quando c’era qualcosa nel raggio, anche una creatura già calma, oppure 1049648 quando non c’era nulla). Un bersaglio che non è un mobile, o è morto o invulnerabile, dice 1049528; oltre il raggio, “That is too far away.” (500446); fuori vista, “Target can not be seen.” (500237); un venditore o una guardia, 1049526; una creatura già calma, 1049527. Si tira prima Musicianship, poi l’abilità tra diff - 25 e diff + 25 con diff la difficoltà del bersaglio meno 10 (anche per un giocatore, dalle sue statistiche e abilità), abbassata come sopra; un fallimento dice 1049531 e un successo 1049532, e una creatura è calmata per clamp(100 - diff / 1.5, 10, 120) secondi (vedi common/creature.lua). Un giocatore ha solo il combattimento fermato. Ogni tiro consuma un uso dello strumento e suona il suono buono o quello cattivo. L’abilità aspetta 10 secondi (delay di data/skills.toml) dalla pressione del pulsante, anche se il cursore viene messo via; i 5 secondi di ModernUO dopo un successo non ci sono, né le zone sicure.

scripts/skills/provocation.lua è l’abilità Provocation, come in ModernUO. on_use rivela il bardo, prende lo strumento (vedi sopra), chiede “Whom do you wish to incite?” (501587) e una creatura, poi “Whom do you wish them to attack?” (1008085) e una seconda. La prima deve essere una creatura che si può colpire, entro il raggio e in vista (altrimenti 501589, “That is too far away.” 500446, “Target can not be seen.” 500237): un cittadino blu dice solo “Think not that I fail to see what thou art doing!” (501591) e costa al bardo 75 di karma tramite common/karma.lua, e non succede altro; una con un padrone dice 501590; altrimenti lo strumento si sente suonato bene subito e non consuma un uso. La seconda viene rifiutata se non è una creatura (501589), se una delle due non si può incantare (venditori, banchieri, guaritori, stallieri, guardie) o non usa nessuno degli script costruiti su common/creature.lua (monster, animal, scared_animal: 1049446), se sono su un’altra mappa o più lontane tra loro del raggio (1049450), se è la stessa della prima (501593), e se è più lontana dal bardo del raggio o fuori dalla sua vista (500446, 500237). Si tira prima Musicianship (500612), poi l’abilità da diff - 25 a diff + 25, con diff la difficoltà media delle due meno 5, abbassata della metà della Musicianship oltre 100 (fallimento 501599, successo 501602). Un successo consuma un uso e mette le due creature l’una contro l’altra: le prop bard.provoker, bard.provoke_target e bard.provoke_until (world.now() più 30), e una calma finisce (vedi common/creature.lua). I giocatori non si possono provocare. L’abilità aspetta 10 secondi dalla pressione del pulsante. Le uccisioni dei provocati sono le loro: il bardo non ne ha il merito, e i paragon non ci sono.

scripts/skills/discordance.lua è l’abilità Discordance, come in ModernUO nella forma che ha senza resistenze (Moongate non ne ha). on_use rivela il bardo, prende lo strumento (vedi sopra), chiede “Choose the target for your song of discordance.” (1049541) e un bersaglio; un clic a terra non fa nulla. Lo strumento deve essere ancora nello zaino quando il cursore risponde (1062488). Il bardo stesso e ciò che non è un mobile dicono “A song of discord would have no effect on that.” (1049535); oltre il raggio, “That is too far away.” (500446); fuori vista, “Target can not be seen.” (500237). Un giocatore, morto o no, viene solo suonato male: nessun tiro, nessun uso, nessuna parola. Una creatura morta o invulnerabile, un venditore, banchiere, guaritore, stalliere o guardia dicono 1049535, e una già in discordia 1049537. Si tira prima Musicianship (“You play poorly, and there is no effect.” 500612), poi l’abilità da diff - 25 a diff + 25, con diff la difficoltà della creatura meno 10, abbassata della metà della Musicianship oltre 100 (fallimento 1049540, successo 1049539). Un successo consuma un uso e mette una maledizione su forza, destrezza e intelligenza della creatura: una percentuale di ogni valore di base, tante percento quanto un quinto dei punti di Discordance (24 percento a 120 punti, 20 a 100). La maledizione (mobile.add_stat_curse con refresh) dura 4 secondi e un timer della discordia la rinnova ogni 2 secondi. La discordia regge finché il bardo è vivo, non nascosto, sulla mappa della creatura e entro il raggio della sua musica, misurato in linea retta; 15 secondi dopo che ciò non è più vero (il conteggio ricomincia se il bardo torna), o subito quando la creatura muore o se ne va, finisce e l’ultima maledizione sparisce entro 4 secondi. L’abilità aspetta 12 secondi dalla pressione del pulsante, qualunque sia l’esito. Su una creatura pesa sui suoi colpi solo la forza (punti vita, stamina e mana non seguono le sue statistiche), e l’abbassamento delle abilità della creatura di ModernUO non c’è, né l’effetto che mostra sulla creatura.

scripts/skills/spirit_speak.lua è l’abilità Spirit Speak, come in ModernUO prima di AOS. on_use mostra prima il giocatore (mobile.set_hidden, come fa usare un’abilità in ModernUO) e prova skill.check(user, "spirit_speak", 0, 100): un fallimento dice “You fail your attempt at contacting the netherworld.” (502443), un successo suona il suono 0x24A, dice “You establish a connection to the neitherworld.” (502444) e conserva, nella prop spirit_speak.until del mobile, il secondo world.now() fino al quale il giocatore capisce i morti: un minuto e mezzo ogni 50 punti di abilità, in secondi interi, almeno 15 (18 a 10 punti, 90 a 50, 180 a 100), come il base / 50 * 90 di ModernUO.

Ciò che la prop fa sta nel gestore del parlato: le parole di un fantasma vengono inviate come sono state dette, e non come “oOo”, ai morti, allo staff, a un giocatore vivo il cui spirit_speak.until è ancora nel futuro, e a uno il cui Spirit Speak di base è 100 o più, che capisce i morti per sempre senza usare l’abilità. Un successo spinge la prop più avanti; un fallimento lascia il tempo che aveva. L’abilità aspetta il secondo di data/skills.toml. La cura di AOS (10 mana, gratis vicino a un cadavere non canalizzato), la rivelazione degli spiriti nascosti e le lingue razziali di UOX3 non sono costruite; niente invia un messaggio “la connessione svanisce” quando la prop finisce.

scripts/skills/animal_lore.lua è l’abilità Animal Lore: on_use dice “What animal should I look at?” (500328), dà un cursore e rifiuta una creatura fuori portata o fuori vista (500446, 1049654), una che non è un animale (mobile.body_type; una creatura morta ha lasciato il mondo) e, in base all’abilità, una creatura non addomesticata sotto i 100 punti o non addomesticabile sotto i 110. Il controllo è skill.check(user, "animal_lore", 0, 120); se riesce costruisce un gump a due pagine con gump.create da pet.lore, mobile.stats e mobile.skills, con tutte le etichette prese dai cliloc del client. Vedi Animal taming.

scripts/items/pickaxe.lua è lo script dei picconi e delle pale (script_id = "pickaxe") e scripts/items/ore.lua quello dei quattro mucchi di minerale di ferro (script_id = "ore"): vedi Estrazione e fusione. Uno scavo sceglie un luogo (target.pick_location), che dà il land della casella e la graphic di uno statico scelto lì: lo script contiene i terreni che sono roccia e gli statici che sono il pavimento di una grotta. Il personaggio colpisce (mobile.animate, mobile.play_sound, timer.after), nel luogo deve restare del minerale (harvest.amount), l’abilità Mining viene provata tra i limiti del metallo del luogo (skill.check; 0 e 100 per il ferro), e uno scavo riuscito toglie dal luogo (harvest.take) e dà un mucchio (item.give). Una fusione sceglie una forgia, un oggetto (item.item_id, item.in_range) o uno statico, prova l’abilità tra 25 e 75, e trasforma il mucchio in lingotti (item.consume, poi item.give) oppure ne brucia metà; un singolo minerale di ferro che fallisce rimpicciolisce, e un metallo sopra l’abilità del minatore viene rifiutato senza prova. Un mucchio su un cursore viene rifiutato (item.is_held). Il metallo di un luogo è la vena della sua zona (harvest.vein): scripts/common/metals.lua contiene minerale e lingotti di ogni metallo, il Mining richiesto, i limiti di uno scavo e la difficoltà di una fusione. Le costanti in cima a ogni script sono i suoi numeri e i suoi elenchi.

scripts/items/axe.lua è lo script delle asce (script_id = "axe" su nove asce base, da cui le loro asce lo prendono): vedi Taglio della legna. L’ascia deve essere in mano a chi vi fa doppio clic (item.worn_by). Il punto scelto deve essere un albero entro 2 caselle: target.pick_location dà la graphic dello statico scelto, e lo script contiene le grafiche che sono alberi. Il personaggio colpisce da una a tre volte (mobile.animate, mobile.play_sound, timer.after), nel luogo deve restare della legna (harvest.amount), l’abilità Lumberjacking viene provata tra i limiti del tipo di legno del luogo (skill.check; vedi sotto), e un taglio riuscito toglie dal luogo (harvest.take) e dà 10 tronchi (item.give). Le costanti in cima allo script sono la distanza, i colpi e i tronchi; gli alberi sono in scripts/common/trees.lua, condiviso con scripts/items/blade.lua, lo script di coltelli, pugnali e spade (script_id = "blade"), che stacca un legnetto da un albero e smembra i cadaveri (vedi blade.lua e corpse.lua). Usata sui tronchi nello zaino, l’ascia sega la pila in assi (item.template, item.consume, poi item.give). Un luogo è di un solo tipo di legno, la vena della sua zona (harvest.vein): scripts/common/woods.lua contiene tronchi e assi di ogni tipo, il Lumberjacking richiesto (mobile.skills) e i limiti tra cui il taglio viene provato, condiviso con la falegnameria, e la tabella FINDS ciò che un maestro trova insieme ai tronchi. Chi sta tagliando è tenuto in memoria per seriale: un riavvio libera tutti.

scripts/items/blade.lua è lo script di coltelli, pugnali e spade (script_id = "blade"). Un giocatore fa doppio clic su uno che porta con sé e sceglie, entro 2 caselle, un albero (target.pick_location; ne viene staccato un legnetto, al costo di un taglio del suo legno) o un cadavere, che deve stare anche entro un piano di altezza. Un cadavere viene smembrato da carve.corpse(corpse, user): si tira la tabella che il cadavere conserva in corpse.carve, on_carving viene chiesto allo script del cadavere, gli oggetti vengono creati dentro il cadavere, e il cadavere viene segnato corpse.carved; un cadavere si smembra una sola volta, e viene mostrato di nuovo a chi lo smembra con ciò che è stato trovato (item.show_contents). Niente di utile, o un cadavere già smembrato, dice il cliloc 500485; il cadavere di un giocatore, di un animale domestico, o uno che il suo script ha rifiutato, dice 500494. Vedi Smembrare i cadaveri.

scripts/items/corpse.lua è lo script del template del cadavere e non definisce alcun hook proprio: è dove uno script tuo definisce on_carving per annullare o cambiare ciò che prende un coltello.

scripts/common/crafting.lua contiene le regole condivise da tutti i mestieri: vedi Falegnameria. Legge le ricette con craft.get e gli elenchi di materiali con craft.resource, conta e toglie i materiali dalle pile che il giocatore porta (item.find, item.amount, item.consume; una pila sul cursore è esclusa con item.is_held), esegue i due colpi (mobile.play_sound, timer.after), prova le altre abilità della ricetta e poi quella principale tra il doppio del minimo meno il massimo e il massimo (skill.check), così la probabilità al minimo è una su due, e crea l’oggetto (item.give, altrimenti item.create ai piedi del giocatore), con il colore del tipo di legno o di metallo scelto (passandolo a item.give, oppure item.set_hue ai piedi): la tabella MATERIALS del motore lega la risorsa wood a scripts/common/woods.lua e metal a scripts/common/metals.lua, e il tipo scelto è ricordato per mestiere. Una riuscita può essere eccezionale (crafting.roll, le proprietà quality, crafter_id, crafter_name), e ogni tentativo la cui abilità viene provata toglie un uso all’attrezzo (la proprietà uses_remaining, estratta tra 25 e 75, item.delete all’ultimo). Chi sta creando, il gruppo e il tipo di legno o di metallo scelti da ogni giocatore, per mestiere, e l’ultima ricetta iniziata da ciascuno (crafting.make_last) sono tenuti in memoria. scripts/items/carpentry_tool.lua (script_id = "carpentry_tool" sugli attrezzi da falegname) apre il gump di creazione dallo zaino; il gump è templates/gumps/craft_menu.xml con scripts/gumps/craft_menu.lua, uno per tutti i mestieri. Lo script di attrezzo di ogni mestiere è una tabella con l’id del suo mestiere e una riga, crafting.use_tool(user, serial, CRAFT): un attrezzo che non è portato viene rifiutato con il testo del client, uno nuovo estrae i suoi usi e il gump si apre. Un mestiere tuo ha bisogno solo di quella riga.

scripts/common/repair.lua, usato dal gump di creazione: repair.mends(craft_id) dice se un mestiere ha il pulsante Repair item, repair.start(user, craft_id) dà al giocatore un cursore e ripara il pezzo scelto, repair.mend(user, craft_id, serial) è un tentativo (true se il pezzo è stato riparato) e repair.roll() è l’estrazione della probabilità di indebolire. Legge e scrive i punti con item.durability e item.set_durability, e trova i pezzi che il mestiere fa nelle ricette (craft.get). Vedi Riparazione per le regole.

scripts/items/smithing_tool.lua è lo script dei martelli da fabbro, delle mazze e delle tenaglie (script_id = "smithing_tool"): apre il gump di creazione del fabbro (vedi Fabbro), con le regole di crafting.lua. scripts/common/smithy.lua contiene le grafiche delle incudini e delle forge e le trova entro una distanza da un giocatore, tra gli elementi fissi della mappa (world.statics) e gli oggetti a terra (world.items_in_range, item.item_id); la tabella NEEDS del motore chiede al fabbro un’incudine e una forgia entro 2 caselle, e ore.lua legge le sue forge da lì.

scripts/items/tailoring_tool.lua è lo script dei kit da cucito (script_id = "tailoring_tool"): dallo zaino apre il gump di creazione della sartoria (vedi Sartoria), con le regole di crafting.lua.

scripts/items/tinkering_tool.lua è lo script degli attrezzi da tinker e delle cassette degli attrezzi (script_id = "tinkering_tool"): dallo zaino apre il gump di creazione della meccanica (vedi Meccanica), con le regole di crafting.lua e il metallo scelto.

scripts/items/fletching_tool.lua è lo script degli attrezzi da fletcher (script_id = "fletching_tool"): dallo zaino apre il gump di creazione di archi e frecce (vedi Archi e frecce), con le regole di crafting.lua e il legno scelto.

scripts/items/cooking_tool.lua è lo script delle padelle, dei setacci per la farina e dei mattarelli (script_id = "cooking_tool"): dallo zaino apre il gump di creazione della cucina (vedi Cucina), con le regole di crafting.lua e i forni e fuochi di scripts/common/heat.lua.

scripts/items/map_item.lua è lo script delle mappe (script_id = "map_item"): dallo zaino o da terra entro 2 caselle apre la mappa sulla sua area con il modulo map (vedi Mappe); più lontano dice “That is too far away.”.

scripts/items/cartography_tool.lua è lo script delle penne (script_id = "cartography_tool"): dallo zaino apre il gump di creazione della cartografia (vedi Cartografia); una mappa creata viene disegnata da scripts/common/cartography.lua.

scripts/items/inscription_tool.lua è lo script di penna e inchiostro (script_id = "inscription_tool"): dallo zaino apre il gump di creazione dell’inscription (vedi Inscription). scripts/common/crafting.lua chiede il libro e il mana di una ricetta con i moduli spell e mobile.

scripts/items/potion.lua è lo script delle pozioni che un giocatore beve (script_id = "potion"): cura, rinvigorimento, forza, agilità e visione notturna, scelte dal template nella sua tabella EFFECTS; controlla la distanza e una mano libera, e lascia una bottiglia vuota (vedi Pozioni).

scripts/items/alchemy_tool.lua è lo script di mortaio e pestello (script_id = "alchemy_tool"): dallo zaino apre il gump di creazione dell’alchimia (vedi Alchimia). scripts/common/potions.lua dice quale pozione è un oggetto, comprese le pozioni semplici dei venditori, per potion.lua ed explosion_potion.lua.

scripts/items/test_kit.lua è lo script delle sacche di prova per lo staff (templates/items/test_kits.toml, script_id = "test_kit"): .add test_kit_alchemy dà una sacca che si riempie, la prima volta che si apre, con l’attrezzo, i materiali e gli oggetti per provare un mestiere, le pozioni o gli incantesimi di Magery; poi è una sacca normale. Le sacche sono test_kit_alchemy, test_kit_potions, test_kit_explosion, test_kit_cartography, test_kit_tailoring, test_kit_tinkering, test_kit_fletching, test_kit_cooking, test_kit_inscription (penna e inchiostro, 100 pergamene vuote, 50 di ogni reagente e un libro completo) e test_kit_magery (un libro completo, 20 di ogni reagente, tre pergamene per ogni incantesimo costruito dei primi quattro cerchi e due rune di richiamo); la tabella KITS dice cosa contiene ognuna. L’abilità si imposta a parte, come .set skill alchemy 100.

scripts/items/spellbook.lua è lo script dei libri degli incantesimi (script_id = "spellbook"): un doppio clic apre il libro se il giocatore lo indossa o lo porta nello zaino, altrimenti dice che il libro deve essere portato con sé. Una pergamena lasciata sul libro viene scritta in esso dal server. scripts/items/spell_scroll.lua è lo script delle pergamene degli incantesimi (script_id = "spell_scroll"): un doppio clic lancia l’incantesimo della pergamena, che deve stare nello zaino. Entrambi chiamano soltanto il modulo spell; vedi Magery.

Gli script degli incantesimi e common/magic.lua

Section titled “Gli script degli incantesimi e common/magic.lua”

scripts/spells/<key>.lua è lo script di un incantesimo, una tabella globale con il nome della chiave di spells.toml. cast(caster, target, info) è l’effetto, chiamato quando il lancio è riuscito; check(caster, target, info) può rifiutare prima che si spenda qualcosa restituendo un numero di cliloc o un testo. Il bersaglio è { kind = "mobile" | "item", serial }, { kind = "location", map, x, y, z } oppure { kind = "none" }, e info i dati dell’incantesimo con scroll vero per un lancio da pergamena. Gli script costruiti sono il primo cerchio (clumsy, create_food, feeblemind, heal, magic_arrow, night_sight, weaken), reactive_armor, il secondo cerchio (agility, cunning, cure, harm, magic_trap, magic_untrap, protection, strength), il terzo (bless, fireball, magic_lock, poison, teleport, telekinesis, unlock, wall_of_stone) e il quarto (arch_cure, arch_protection, curse, fire_field, greater_heal, lightning, mana_drain, recall), il quinto (blade_spirits, dispel_field, incognito, magic_reflection, mind_blast, paralyze, poison_field, summon_creature), il sesto (dispel, energy_bolt, explosion, invisibility, mark, mass_curse, paralyze_field, reveal), il settimo (chain_lightning, energy_field, flame_strike, gate_travel, mana_vampire, mass_dispel, meteor_swarm, polymorph) e l’ottavo (earthquake, energy_vortex, resurrection, summon_air_elemental, summon_daemon, summon_earth_elemental, summon_fire_elemental, summon_water_elemental); un incantesimo senza script dice di essere disabilitato. Gli incantesimi di Necromancy — la scuola senza cerchi — sono script anche loro, ognuno col proprio mana, la propria abilità minima e il proprio ritardo in spells.toml (un mana, required_skill, cast_skill, scale_skill e cast_delay che vincono sulle tabelle dei cerchi): si lanciano con Necromancy, scalano con Spirit Speaking, con i reagenti dei negromanti e gli incantesimi che il client numera da 100. Sedici dei diciassette sono costruiti (animate_dead, blood_oath, curse_weapon, evil_omen, exorcism, horrific_beast, lich_form, mind_rot, pain_spike, poison_strike, strangle, summon_familiar, vampiric_embrace, vengeful_spirit, wither, wraith_form): le ferite di Pain Spike e Poison Strike, i round di Strangle, i tre che il combattimento legge dalle props — un Blood Oath rende il danno del lanciatore contro chi è legato un quinto peggiore e rimanda la ferita, un Mind Rot fa costare un quarto di mana in più gli incantesimi del maledetto, un’arma maledetta beve metà delle sue ferite — e le quattro forme di common/necromancy.lua, un corpo ciascuna preso con mobile.disguise per una settimana e finito da un secondo lancio della stessa forma, un’altra forma, una morte o la settimana: la Bestia Orribile (746), il Lich (749) che paga un punto vita ogni due secondi e mezzo per il suo corpo, il Vampiro (745 per una donna, 744 per un uomo, tinta 0x847E) che nessun veleno prende e il Wraith (747 per una donna, 748 per un uomo) che beve dal 5 al 20 percento delle sue ferite come mana. Le tre evocazioni fanno alzare le loro creature sul sistema dei seguaci (common/summon.lua): Animate Dead indica un cadavere e due secondi dopo ne fa alzare un non-morto della scala dei negromanti, da Necromancy e Spirit Speaking del lanciatore, un corpo che decade punto vita per punto vita e del quale il quarto congeda il più vecchio; Summon Familiar apre la lista dei cinque famigli, uno alla volta accanto al lanciatore per un giorno; Vengeful Spirit manda un revenant, un evocato che non sente parola di un animale, contro una vittima sola finché non è morta o non è passato il suo tempo. L’Evil Omen maledice il suo bersaglio per un solo evento dannoso: la prossima ferita un quarto peggiore, il prossimo veleno di un livello più alto, e il primo dei due lo spende; il Wither è un gelo attorno al lanciatore, cinque caselle di bersagli indiretti validi, ognuno ferito più forte quanto peggiore è il suo karma. L’Esorcismo, infine, chiede una maestria di Spirit Speaking e un lanciatore dentro uno spawn di campioni, e porta ogni giocatore morto della portata dell’altare al santuario più vicino a ciascuno — il modulo Lua champions sopra il servizio degli spawn; Corpse Skin è fuori per sempre, solo offset di resistenza. Il libro del negromante (0x2253) si apre come ogni libro degli incantesimi; i suoi contenuti vanno col pacchetto più nuovo, e le sue pergamene vi cadono dentro come quelle di magery. I quattro incantesimi del lucchetto e della trappola leggono le props del forziere, su un contenitore: Magic Trap arma una trappola magic di potenza 1 (502942 per una cosa che non è un contenitore, un fizzle per una trappola di altro tipo), Magic Untrap spende una trappola magica (502373 per una cosa non trappata, un fizzle per una trappola di altro tipo), Magic Lock mette locked con una prop lock.magic — un lucchetto che nessun grimaldello apre, solo Unlock (501762 per una cosa che non è un contenitore o già chiusa, e 501763 sul lancio) — e Unlock apre un lucchetto fino alla Magery del lanciatore per 0.8 meno 4 punti (501666 per una cosa che non è un contenitore, 503101 per una non chiusa, 503099 per una al di sopra, col mana speso). scripts/common/magic.lua è ciò che condividono: la probabilità che un bersaglio resista, il fattore del danno, la maledizione e il potenziamento con la loro durata, l’armatura di Protection, il rifiuto di una cura, l’effetto e il suono di un incantesimo e, per gli incantesimi che colpiscono un punto, chi possono colpire (valid_indirect, indirect_targets), il danno che arriva dopo (harm_after), la probabilità di un Dispel e aggress, che rende il lanciatore l’aggressore del bersaglio e conosce l’incantesimo che Magic Reflection ha rimandato indietro (info.reflected, info.reflector): il lanciatore è allora il suo stesso bersaglio e si ferisce da sé, senza crimine. scripts/common/field.lua mette a terra la fila di pezzi di un campo: field.cast(caster, target, info, options) suona il suono, mette a terra la fila e mostra uno sbuffo su ogni pezzo, con options.kind (una chiave di field.kinds: energy, fire, paralyze, poison, stone) al posto dei due template; field.is_field(template) è ciò che Dispel Field disfa. Uno script di incantesimo tiene solo ciò che è suo (i suoi numeri, i suoi testi, i suoi extra) e chiama il resto. Un controllo che rifiuta soltanto un bersaglio morto è X.check = magic.check_alive (una cura, magic.check_healable; un campo, field.check): l’host degli incantesimi trova check come membro della tabella dell’incantesimo, quindi una funzione assegnata lì è uguale a una scritta sul posto. Gli altri aiuti di magic.lua: sound_on e sound_at (il suono dell’incantesimo, niente per uno che non ne ha), select_in_range (i mobile vicino a un punto per cui una funzione dice sì), refuse_indirect (la città sorvegliata, poi nessuno da colpire), refuse_weight e refuse_landing (niente su cui stare, un posto pieno e la regola di viaggio della regione, ciascuno con il proprio testo come parametro), refuse_active (una proprietà di tempo ancora da venire), refuse_disguised e disguise (Incognito e Polymorph), heal, cure_chance e paralyze_seconds; magic.curse accetta una caratteristica o una lista. Un incantesimo dannoso che rovina quello che il bersaglio stava facendo (Curse e le maledizioni di caratteristica, Mana Drain, Mana Vampire, Poison) comincia con magic.begin_assault(caster, who, info): aggress, poi il lancio del bersaglio è disturbato e la sua paralisi finisce, e false, senza fare altro, per un bersaglio che non può essere colpito (Paralyze, che non toglie paralisi e disturba solo dopo la resistenza, non lo usa; Mind Blast e gli incantesimi di danno fanno solo aggress). magic.resist_and_tell(caster, who, info, percent) tira la resistenza del cerchio dell’incantesimo (percent è una probabilità propria dell’incantesimo, come ha Mana Drain) e avvisa un bersaglio che resiste; quello che l’incantesimo fa di suo resta nello script. I tre dardi (Magic Arrow, Fireball, Energy Bolt) sono una sola chiamata, magic.bolt(caster, who, info, least, most, delay, rand): aggress (nient’altro, e nessun dado, per un bersaglio che non può essere colpito), il proiettile dell’incantesimo che vola dal portatore di una riflessione, o dal lanciatore, al bersaglio, il suono sul lanciatore, poi si tira rand(least, most) (il random dell’incantesimo), magic.damage lo fa resistere e lo scala, e il danno arriva delay secondi dopo. Un incantesimo che colpisce più bersagli, o fa i passi in un altro ordine (Lightning, Harm, Flame Strike, Explosion, Meteor Swarm, Chain Lightning), tiene il proprio lancio. Un incantesimo che legge o marca una runa di richiamo usa scripts/common/rune.lua: rune.place(serial), rune.mark(serial, here) e rune.in_pack(caster, serial). Una root i cui script degli incantesimi sono sostituiti a mano ha bisogno di common/rune.lua con Recall, Gate Travel e Mark. I testi che gli incantesimi dicono sono numeri del client, tenuti nella tabella magic.cliloc (WONT_WORK, BLOCKED, IN_EFFECT, RESISTING, CANNOT_DISPEL, CURED_TARGET, NOT_ALLOWED, TOO_ENCUMBERED, CRIMINAL, FIZZLE): uno script di incantesimo dice magic.cliloc.WONT_WORK, mai il numero. Il suono, l’effetto e il proiettile di un incantesimo sono quelli di data/spells.toml (info.sound, info.effect, info.projectile); uno script non tiene alcun valore proprio per essi.

I numeri dei testi del client che molti script dicono sono costanti del modulo cliloc, che il codice C# del server legge dalla stessa fonte: cliloc.TOO_FAR (500446), cliloc.CANNOT_SEE (500237), cliloc.DEAD (1019048), cliloc.IN_BACKPACK (1062334), cliloc.USE_ON_WHAT (1010018) e cliloc.NOT_CARRIED (1042010). Passane una a mobile.message_cliloc, come mobile.message_cliloc(user, cliloc.TOO_FAR). Un numero che dice un solo script resta in quello script; quelli degli incantesimi sono in magic.cliloc. L’elenco è nel riferimento API Lua.

scripts/common/summon.lua è ciò che condividono gli incantesimi che chiamano una creatura: summon.create(caster, template, place, seconds, sound, uncontrollable) chiede la creatura con npc.spawn e, quando c’è, ne fa un seguace del lanciatore (la proprietà owner, l’ordine di animale guard), imposta summon.until, summon.difficulty e summon.focus, e avvia il timer che la manda via; con uncontrollable riceve la proprietà pet.uncontrollable, e common/pet_orders.lua le fa non sentire parole e non prendere cibo (uno spirito di lama, un vortice di energia). summon.refuse e summon.refuse_template dicono, con il cliloc 1049645, che i seguaci del lanciatore non hanno posto per lei (pet.followers, pet.slots_of). L’incantesimo intero di una creatura chiamata accanto al lanciatore è summon.check_near(caster, slots_or_template) e summon.cast_near(caster, info, template, options) (gli elementali, il demone, Summon Creature), quello di una creatura chiamata nel punto scelto è summon.check_at e summon.cast_at (Blade Spirits, Energy Vortex): ogni script tiene il suo template, i suoi secondi e i suoi extra. summon.tick è chiamato da common/creature.lua a ogni pensiero di una creatura: manda via quella il cui tempo è scaduto o il cui padrone è morto, se n’è andato o l’ha lasciata. summon.dismiss la porta via in uno sbuffo (effect.at, npc.delete); lo usa un Dispel. Una creatura evocata che muore non lascia cadavere (l’on_death di creature.lua). Vedi Magery.

scripts/items/magic_field.lua è lo script dei pezzi dei campi (script_id = "magic_field", template in templates/items/magic/fields.toml): i timer dell’oggetto expire e tick terminano un pezzo quando field.until è raggiunto, e un pezzo con field.damage brucia, con combat.harm e il lanciatore come attaccante, chi lo calpesta (on_move_over, on_npc_move_over) o ci sta dentro a ogni tick, una volta al secondo. Una prova di Resisting Spells riduce la bruciatura a un punto; un invulnerabile o un mobile morto viene lasciato in pace. Un pezzo con field.effect = "poison" avvelena, al livello di field.power, e uno con "paralyze" immobilizza per field.power secondi (mobile.paralyze) chi lo calpesta, quando il lanciatore è nel gioco e può colpirlo (magic.valid_indirect, che risparmia il gruppo del lanciatore), il lanciatore essendone l’aggressore. I campi di energia e il muro di pietra non hanno altro effetto che il loro peso. Vedi Magery.

scripts/items/fishing_pole.lua è lo script delle canne da pesca (fishing_pole, fishing_pole_2, script_id = "fishing_pole"): vedi Pesca. Fai doppio clic sulla canna e scegli dell’acqua entro 4 caselle e in vista (target.pick_location, world.is_water, world.line_of_sight). Il personaggio lancia (mobile.animate), l’acqua schizza 1,5 secondi dopo (effect.at, world.play_sound) e il risultato arriva dopo 8 secondi (timer.after). Nel luogo devono restare dei pesci (harvest.amount), l’abilità Fishing viene provata tra 0 e 100 (skill.check), e la presa viene messa nello zaino (item.give) e tolta dal luogo (harvest.take). Le costanti in cima allo script sono la distanza, i secondi e ciò che esce. Chi sta pescando è tenuto in memoria per seriale: un riavvio libera tutti.

scripts/items/bandage.lua è lo script della benda pulita (clean_bandage, script_id = "bandage"), come la Healing classica di ModernUO. Si fa doppio clic, si sceglie a chi è destinata e si aspetta: chi è stato scelto viene curato, o resuscitato.

  • Portata: la benda nello zaino, e chi la riceve entro 1 casella; più lontano, il “too far away” del client. Il curatore deve vedere chi riceve la benda: uno nascosto o dietro un muro “can not be seen”. Usare una benda rivela un curatore nascosto. La benda esce dalla pila quando la cura inizia. Una seconda benda dello stesso curatore sostituisce la prima.
  • L’attesa: 3 secondi per un curatore con 100 di destrezza o più, 4 da 40, 5 sotto; 5 in più per resuscitare un fantasma; 9,4 + 0,6 × (120 − destrezza) / 10 su sé stessi. Il curatore deve restare entro 1 casella e vivo, altrimenti la cura va persa insieme alla benda.
  • Un vivo ferito: riesce con una probabilità di (Healing + 10) %, e cura da Anatomy / 5 + Healing / 5 + 3 fino a Anatomy / 5 + Healing / 2 + 10 punti; un tiro sotto 1 cura 1 e dice che le bende hanno aiutato appena. Una creatura con il corpo di un mostro o di un animale è un caso per Veterinary e Animal Lore, con un punto in più ogni 100 punti vita. Chi non è ferito legge “That being is not damaged!” e la benda resta — a meno che il suo veleno non sia al lavoro, che è un caso per l’antidoto qui sotto.
  • Un avvelenato: antidotato, non curato — nessun punto vita torna. La benda parte anche a piena vita. Il curatore legge «You finish applying the bandages.», poi l’antidoto è tirato dietro un cancello di 60 punti di entrambe le abilità del curatore, con una probabilità di (l’abilità principale − 30) / 50 meno un decimo di punto per livello del veleno: un tiro fallito legge «You have failed to cure your target!» (1010060), uno passato cura — «You have cured the target of all poisons.» (1010058) al curatore a meno che curi sé stesso, e «You have been cured of all poisons.» (1010059) al paziente.
  • Uno che sanguina: fasciato — le ferite si chiudono e nessun punto vita torna, e nessuna abilità è chiesta. Il curatore legge «You bind the wound and stop the bleeding» (1060088) e il paziente che le sue ferite hanno smesso di sanguinare (1060167). Un paziente avvelenato è antidotato prima, e uno che sanguina è esente dal «That being is not damaged!» come un avvelenato.
  • Un fantasma: servono 80 punti di Healing e di Anatomy e una probabilità di (Healing − 68) / 50; poi al fantasma viene chiesto, nel gump degli ankh, se vuole tornare, e gli costa un decimo della fama, come a un ankh.
  • Il cadavere di un animale legato: servono 80 punti di Veterinary e di Animal Lore e una probabilità di (Veterinary − 68) / 50, l’attesa è quella di un fantasma (5 secondi in più), e il proprietario deve essere il curatore o stare entro 3 caselle dal cadavere; l’animale rinasce dove giace il cadavere, con il suo proprietario, la lealtà e il legame, e 10 punti vita. Il cadavere di un animale non legato non può essere bendato.
  • Abilità: entrambe vengono provate per la crescita dopo una cura, anche se il tiro non è riuscito, e dopo una resurrezione riuscita.

Non ci sono ancora veleno e sanguinamento, quindi nessuna cura per essi; e il terreno dove un fantasma viene resuscitato non viene controllato, come non lo è a un ankh.

scripts/skills/stealth.lua è lo script di Stealth, come quello classico di ModernUO. Un giocatore nascosto usa l’abilità e, se il controllo riesce, può fare alcuni passi senza essere mostrato: un decimo della sua Stealth in passi, almeno uno (mobile.set_stealth_steps; il server li conta in MoveRequestPacketHandler). Correre lo mostra sempre. Nascondersi o essere mostrati di nuovo azzera i passi.

  • Prima del controllo: chi non è nascosto riceve l’invito a nascondersi prima (502725); chi ha meno di 80 punti di Hiding “non è nascosto abbastanza bene” (502726), e chi ha un valore di armatura (combat.armor_rating) di 26 o più “non può sperare di muoversi in silenzio” (502727): entrambi vengono mostrati.
  • Il controllo va da -20 a 80 punti, ciascuno aumentato del doppio del valore di armatura. Un successo legge “You begin to move quietly.” (502730); un fallimento legge “You fail in your attempt to move unnoticed.” (502731) e mostra il giocatore. L’abilità aspetta 10 secondi in ogni caso.
  • In sella: chi è in sella viene rifiutato con “You cannot stealth while mounted.” (500837).
  • Non c’è ancora: le regole di Stealth delle versioni successive (il costo dei passi per armatura).

scripts/skills/snooping.lua è lo script di Snooping, come quello di ModernUO. Non si usa dalla finestra delle abilità: un doppio clic sullo zaino di un altro mobile non lo apre, il server chiama on_snoop(user, owner, container) di questo script (ISkillScriptService.Call). Lo fa per lo zaino e per un sacco al suo interno, e non per un giocatore morto.

  • Lo staff fruga chiunque, sempre: un game master o un amministratore non ha bisogno di distanza, abilità o regole, non perde karma e non viene notato, e può frugare un proprietario morto e un altro membro dello staff. Il comando hide lo nasconde prima.
  • Regole per gli altri: entro una casella dal proprietario; niente se il proprietario è morto; un game master o un amministratore non si può frugare, e nemmeno un giocatore invulnerabile (“You cannot perform negative acts on your target.”; Moongate non ha ancora regole sugli atti dannosi per mappa, che questo sostituisce). Un PNG in una regione sorvegliata di una mappa diversa da Felucca si fruga solo se non è umano, oppure attaccabile, oppure assassino: lo dice il commento di ModernUO, anche se il suo codice lascia frugare chiunque un PNG in una città attiva.
  • Un giocatore che non è staff perde 4 di karma, come AwardKarma di ModernUO prende una perdita (di più da un buon nome, niente sotto -400, mai sotto -15000, e la perdita viene comunicata), ed è notato dai giocatori entro 8 caselle (“You notice attempting to peek into ’s belongings.”): sempre sotto 100 punti di Snooping, con una probabilità pari ai punti su cento di passare inosservato.
  • Il controllo va da 0 a 100 punti: un successo apre lo zaino sul client del giocatore (item.show_contents); un fallimento legge “You failed to peek into the container.” e mostra il giocatore, più probabilmente quanto meno ha di Hiding. Lo staff vede sempre.
  • Non c’è ancora: le trappole dei contenitori. Gli oggetti visti non si possono sollevare: il sollevamento di un oggetto che il giocatore non possiede è rifiutato come prima.

scripts/skills/stealing.lua è l’abilità Stealing, come in ModernUO nella sua modalità classica. on_use chiede “Which item do you want to steal?” (502698) con entrambe le mani libere (“Both hands must be free to steal.” 1005584; uno scudo conta, mobile.hands_empty) e rivela il ladro. Il bersaglio è un oggetto dentro un contenitore, oppure un mobile, che dà un oggetto del suo zaino a caso (uno zaino vuoto non dà nulla). I controlli, in quest’ordine: un oggetto di un giocatore, il ladro non della gilda dei ladri, 1005596 “You must be in the thieves guild to steal from other players.”; un ladro della gilda con uccisioni (mobile.murders, il Kills > 0 di ModernUO), 502706; un negoziante invulnerabile, 1005598; un oggetto in una cassa della banca, uno a terra, uno fisso (item.is_movable), uno benedetto o newbie (item.loot_type), quello in un cadavere, quello di un membro dello staff o di un morto, 502710 “You can’t steal that!”; uno indossato, 1005585; uno del ladro stesso, 502704; più lontano di una tile, 502703; fuori vista, 500237; più pesante di 10 stone (item.weight: un contenitore con ciò che contiene, una pila per una unità), 502722; uno zaino che non lo può contenere (item.can_hold, i limiti di peso, chiesto prima del tiro), 1048147. ModernUO chiede prima la distanza e il suo ordine differisce in questi casi limite. Il controllo dell’abilità è il peso in stone per 10, da 22.5 in meno a 27.5 in più; una pila viene tagliata a una quantità casuale fino a un decimo dell’abilità sul peso unitario, e il controllo è di quella quantità. Un successo sposta l’oggetto nello zaino del ladro: una pila viene tagliata con item.split (il resto tiene il serial e resta al suo proprietario, e la parte presa è un oggetto nuovo) e l’oggetto passa con item.move_into e il suo ultimo argomento, transfer, di cui un furto ha bisogno per prenderlo da un mobile (502724); un fallimento dice 502723. L’oggetto viene salvato dall’uscita del nuovo proprietario o dal salvataggio del mondo: un crollo prima restituisce alla vittima un oggetto intero. Una pila tagliata in due sono due righe, quella della vittima con il vecchio serial, riscritta più piccola dal suo salvataggio successivo, e quella del ladro con il serial nuovo: un crollo tra i due salvataggi può far perdere la parte presa, o lasciarla in entrambi gli zaini se è stato salvato prima il ladro. Un oggetto che il proprietario tiene sul cursore viene rifiutato prima del tiro (502710), e un mobile non lo sceglie mai tra gli oggetti del suo zaino. Un furto costa 50 di karma comunque finisca (common/karma.lua), e il ladro viene scoperto con una probabilità di uno su 150 per ogni punto di abilità che gli manca: diventa un criminale quando la vittima è un mobile innocente (per la sua notorietà, non verso il ladro) e non della gilda dei ladri, e i giocatori entro 8 tile leggono “You notice trying to steal from .”. L’abilità aspetta 10 secondi dalla pressione del pulsante, anche se il cursore viene messo via (ModernUO tiene il cursore 30 secondi e 10 dopo il tentativo). Non ci sono: fazioni e sigilli, artefatti rubabili, i Young, le zone sicure, un contenitore aperto come condizione, l’oggetto rubato che torna alla vittima se il ladro muore entro due minuti, e il grigio permanente verso la vittima.

scripts/skills/item_identification.lua è l’abilità Item Identification, come in ModernUO nella sua modalità classica. on_use chiede “What do you wish to appraise and identify?” (500343) e un bersaglio entro 8 tile (“That is too far away.” 500446 oltre; un oggetto che il giocatore porta addosso è sempre a portata). Ciò che mostra è ciò che mostra un singolo clic (item.look): la riga del nome del tooltip sopra l’oggetto o il mobile, con la quantità di una pila, il nome che gli dà il template e il titolo di un mobile, vista solo da quel giocatore. Un’arma, un pezzo di armatura e un oggetto con la prop dell’oggetto unidentified vengono provati con skill.check(user, "item_identification", 0, 100), come gli identificabili di ModernUO, ed è così che l’abilità si allena: un successo toglie la prop (un oggetto che non si può cambiare resta non identificato) e non dice altro; tutto ciò che non è identificato dice “You are not certain…” (500353) dopo l’etichetta, un oggetto ordinario compreso, come fa ModernUO. Nulla imposta ancora unidentified: è il gancio per le bacchette magiche e il bottino a venire, e le proprietà di un tale oggetto non sono nascoste nel suo tooltip. Il cursore non controlla la linea di vista. L’abilità aspetta il secondo di data/skills.toml.

scripts/skills/taste_identification.lua è l’abilità Taste Identification, come in ModernUO. on_use chiede “What would you like to taste?” (502807) e un bersaglio: un oggetto o un mobile entro 2 tile e in vista (“You are too far away to taste that.” 502815 oltre, “Target can not be seen.” 500237 fuori vista; un oggetto che il giocatore porta addosso è sempre a portata). Un mobile dice “You feel that such an action would be inappropriate.” (502816). Il cibo (lo script food) viene provato con skill.check(user, "taste_identification", 0, 100), ed è così che l’abilità si allena: un successo dice, come etichetta sopra il cibo, “It appears to have poison smeared on it.” (1038284) quando la sua prop dell’oggetto poison.level c’è (il veleno minore è il livello 0) e “You detect nothing unusual about this substance.” (1010600) quando non c’è; un fallimento dice “You cannot discern anything about this substance.” (502823), avvelenato o no. Una pozione (gli script potion e explosion_potion) è già nota: “You already know what kind of potion that is.” (502813), poi il nome del client del suo tipo, 1041314 più l’effetto della pozione di ModernUO (così una pozione di cura minore dice “Lesser Heal”, non la pozione gialla che ogni pozione di cura si chiama; una pozione di tipo non noto mostra il suo nome con item.look). Un barilotto di pozioni è vuoto, “There is nothing in the keg to taste!” (502228), perché nessun barilotto contiene ancora pozioni. Tutto il resto dice “That’s not something you can taste.” (502820). L’abilità Poisoning imposta poison.level sul cibo. Un clic a terra non ha risposta (il cursore non la dà), e i caschi di banane e la bacca tribale non sono cibo nei dati UOX3, quindi dicono 502820. L’abilità aspetta il secondo di data/skills.toml.

scripts/items/lockpick.lua è lo script dei grimaldelli (0x14fb, 0x14fc, 0x14fd e 0x14fe, script_id = "lockpick"), come il Lockpick di ModernUO. Si fa doppio clic, si sceglie un oggetto chiuso a chiave entro una casella, e dopo tre secondi viene provata l’abilità; il giocatore deve restare entro una casella.

Si può scassinare un oggetto con queste prop: locked (true finché è chiuso), lock.level e lock.max, i punti di Lockpicking da cui la prova può appena riuscire e da cui non fallisce mai, e lock.required, il minimo per provare. Una serratura senza lock.level “non si può scassinare con mezzi normali”; chi ha meno di lock.required “non vede come si possa manovrare quella serratura”. Un successo apre l’oggetto per sempre (locked è false, lock.picker è il giocatore); un fallimento rompe il grimaldello una volta su quattro, che esce dalla sua pila. Lockpicking è un’abilità che sale con l’uso, come le altre.

scripts/items/treasure_chest.lua è lo script dei quattro forzieri del tesoro dei dungeon (templates/items/treasure_chests.toml, script_id = "treasure_chest"), come i TreasureChestLevel1 a 4 di ModernUO. Un forziere nasce chiuso: chiede 57, 72, 84 e 92 punti di Lockpicking per livello, e la prova va da quel valore meno un tiro da 1 a 10 a quel valore più un tiro da 1 a 10. Un forziere chiuso non si apre (“It appears to be locked.”); un game master lo apre (“That is locked, but you open it with your godly powers.”); uno scassinato si apre come ogni contenitore. Nessuno tranne un game master lascia cadere un oggetto in un forziere chiuso (can_insert), e ciò che è già dentro un forziere aperto su un client si può ancora sollevare. I forzieri creati prima di questa versione non hanno serratura e restano come erano finché non decadono.

Un forziere è anche armato con la trappola del suo livello (common/trap.lua, come li arma il FillableContainer di ModernUO): uno di livello 2 sempre, uno di livello 1 quattro volte su cinque, con un veleno per i forzieri più profondi e un’esplosione altrimenti, e trap.power è il livello per un tiro da 10 a 30. Chi apre un forziere scassinato la fa scattare — “You set off a trap!” — ne prende lo scoppio entro 3 caselle (un’esplosione rand(10, 30) a livello, un dardo rand(5, 15) a livello, un veleno del livello, una trappola magica la sua potenza entro 1 casella) e il forziere resta chiuso quel clic; una trappola scattata è spesa (le tre props se ne vanno). Lo staff apre con i suoi poteri divini e la trappola resta armata, e una trappola fatta scattare da lontano, come fa il Telecinesi, non raggiunge nessuno fuori dalle sue caselle.

scripts/common/trap.lua è la trappola di un contenitore, tenuta nelle sue props: trap.type (explosion, dart, poison o magic), trap.level e trap.power. trap.arm(chest, level) arma il forziere di quel livello (livello 2 e oltre sempre, livello 1 quattro volte su cinque; un veleno quando il livello batte un tiro di cinque, altrimenti un’esplosione; la potenza è il livello per un tiro da 10 a 30). trap.spring(chest, who) è chi lo apre: lo staff scende con i suoi poteri divini e la trappola resta, chiunque altro legge “You set off a trap!” (502999) e prende lo scoppio del tipo — combat.harm senza attaccante, “as for a trap” — entro le sue caselle, e la trappola è spesa. L’abilità Remove Trap disarma queste props.

scripts/items/training_dummy.lua è lo script dei manichini da allenamento (0x1070 e 0x1071 rivolti a sud, 0x1074 e 0x1075 rivolti a est), come il TrainingDummy di ModernUO: i template hanno script_id = "training_dummy", e così anche decoration_training_dummy, che .decorate dà ai manichini dei file. Si fa doppio clic su un manichino con un’arma da mischia in mano, o a mani nude: il giocatore si gira e colpisce (combat.swing), il manichino mostra la grafica oscillante da un quarto di secondo, con il suono di un colpo, e dopo tre secondi torna a riposo, e l’abilità dell’arma (Wrestling per i pugni) viene provata da -25 a 25 punti, quindi può salire fino a 25.

Un arco o una balestra non possono allenarsi su di esso (“You can’t practice ranged weapons on this.”), l’arma deve raggiungerlo (una casella, combat.range), un manichino che oscilla ancora fa aspettare il giocatore, e un’abilità a 25 legge “Your skill cannot improve any further by simply practicing with a dummy.”. Non c’è alcun controllo per un giocatore in sella: lo script lascia allenare un cavaliere.

scripts/items/archery_butte.lua è lo script dei bersagli per il tiro con l’arco (0x100A rivolto a est, 0x100B rivolto a sud), come l’ArcheryButte di ModernUO: i template hanno script_id = "archery_butte" e use_range = 6, come decoration_archery_butte, che .decorate dà ai bersagli dei file, così il giocatore può fare doppio clic da dove tira. Un mondo decorato prima di questa versione li ha come semplice decorazione: un nuovo .decorate li trasforma in questi.

  • Tiro: con un arco o una balestra, ci si mette davanti al bersaglio, in linea con esso, a cinque o sei caselle, e si fa doppio clic. Una freccia o un dardo viene speso (combat.spend_ammo), il giocatore tira (combat.swing e la freccia che vola, effect.moving) e l’abilità dell’arma viene provata da -25 a 25 punti: può salire, e il tiro può mancare. Tra due tiri allo stesso bersaglio passano due secondi. I testi dicono che cosa non va quando il giocatore sta dietro, fuori linea, troppo lontano o troppo vicino.
  • Punteggio: un tiro a segno vale 50 (il centro, uno su dieci), 10, 5 o 2 punti, e al giocatore viene detto il suo totale a quel bersaglio e quanti tiri ha fatto. La freccia può spezzarsi, più probabilmente quante più munizioni sono conficcate nel bersaglio (il 2 per cento ciascuna), e allora vale di più e va persa. I testi sono detti a chi tira, non agli altri.
  • Raccolta: doppio clic sul bersaglio entro una casella, quando ci sono frecce o dardi conficcati (prop butte.arrows e butte.bolts): vanno nello zaino e i punteggi vengono azzerati.

I bersagli per freccette non esistono ancora.

scripts/items/dyes.lua e scripts/items/dye_tub.lua tingono i vestiti in due passaggi, come ModernUO; i template convertiti delle tinture (dyes) e della vasca (dying_tub) contengono script_id = "dyes" e script_id = "dye_tub".

  1. Fai doppio clic sulle tinture e scegli una vasca: si apre il selettore di colori del client con la vasca, che assume il colore scelto, da 2 a 1001, come proprio colore.
  2. Fai doppio clic sulla vasca e scegli cosa tingere: assume il colore della vasca, con il suono della tintura (0x23E).

Si può tingere un oggetto il cui template indica dyeable = true, come gli abiti convertiti da UOX3. Non deve essere indossato, e il giocatore deve raggiungere l’oggetto, la vasca e le tinture: trasportati, oppure a terra entro 1 casella. Né le tinture né la vasca vengono consumate, e una vasca mai tinta ha colore 0, che rimuove il colore. Un oggetto tenuto sul cursore non viene tinto (“Non puoi tingere quello.”), e uno dentro un contenitore a terra conta come troppo lontano: prendilo prima.

I testi sono quelli del client, letti nella sua lingua:

Testo Cliloc
Seleziona la vasca su cui usare le tinture. 500856
Usa questo su una vasca per tintura. 500857
Seleziona gli abiti da tingere. 500859
Non puoi tingere abiti indossati. 500861
Non puoi tingere quello. 1042083
È troppo lontano. 500446

Il giocatore può rispondere al selettore di colori molto più tardi, oppure mai. Quando arriva la risposta lo script controlla di nuovo che tinture e vasca siano raggiungibili, e il server accetta una risposta solo per il selettore che ha aperto: gli altri emulatori la accettano come arriva. scripts/common/dye.lua contiene ciò che i due script condividono (dye.reach, dye.worn, dye.tell). Le vasche speciali (pelle, mobili, nere, metalliche) e le tinture dei capelli non sono ancora presenti.

scripts/skills/hiding.lua è lo script dell’abilità Hiding, come quello di ModernUO senza ciò che richiede un combattimento o una casa. Un giocatore che usa l’abilità viene verificato con skill.check(user, "hiding", 0, 100): la probabilità è pari ai suoi punti su cento, e il tentativo può aumentare l’abilità.

  • Successo: il giocatore è nascosto (mobile.set_hidden), fuori dalla modalità guerra, e legge “Ti sei nascosto bene.” (cliloc 501240).
  • Fallimento: il giocatore viene mostrato, anche se era nascosto, e legge “Non riesci a nasconderti qui.” (501241).

In entrambi i casi attende prima di un’altra abilità il delay di hiding in data/skills.toml, 10 secondi. Il primo passo lo mostra di nuovo, con “Sei stato rivelato!” (500814): il server lo fa per ogni giocatore nascosto di un account regolare, a meno che Stealth non abbia permesso il passo; girarsi sul posto non lo fa. Lo staff si nasconde per osservare e resta nascosto. Parlare, essere colpiti e la vista di chi sta vicino non lo mostrano ancora.

scripts/skills/meditation.lua è lo script dell’abilità Meditation, come quello di ModernUO prima di AOS. on_use mostra prima il giocatore e rifiuta l’uso: al mana pieno (“Sei in pace.”, 501846), sotto un decimo dei punti vita (“La mente è forte ma il corpo è debole.”, 501849), con qualunque armatura addosso (“Le forze rigenerative non possono penetrare la tua armatura!”, 500135 — qui non esiste un permesso di meditazione, quindi qualunque armatura indossata blocca la trance, la regola AOS di ModernUO) e senza una mano libera (“Le tue mani devono essere libere per lanciare incantesimi o meditare.”, 502626 — la regola del bere di mobile.has_free_hand; il libro degli incantesimi e il libro delle rune di ModernUO non sono letti). Altrimenti il controllo è skill.check(user, "meditation", 0, 100): un successo imposta la prop meditating, suona il suono 0xF9 e legge “Entri in una trance meditativa.” (501851); un fallimento legge “Non riesci a concentrarti.” (501850). L’abilità attende 10 secondi, il delay di data/skills.toml.

Finché la prop è impostata, il mana torna due volte più in fretta: il ritmo di rigenerazione da intelligenza e Meditation viene dimezzato (l’effetto passivo dell’abilità c’è anche senza la trance). La trance finisce quando la prop viene tolta, come fa un passo, una ferita, un colpo, un incantesimo, indossare un oggetto e l’uso di un’altra abilità, ma non di Meditation stessa; finisce anche quando il mana è pieno (“Sei in pace.”, 501846, controllato ogni secondo dalla rigenerazione) e quando il personaggio entra nel mondo, perché la prop viene salvata con lui. Il rifiuto di ModernUO per un giocatore occupato (501845) non c’è: uno script non può chiedere di un cursore pendente, e l’uso di un’abilità lo sovrascrive.

scripts/skills/begging.lua è lo script dell’abilità Begging, come quello di ModernUO prima di ML. on_use mostra prima il giocatore e chiede “Da chi vuoi supplicare?” (500397) con un cursore. I rifiuti, ognuno col testo del client: una cosa che non è un essere vivente e un corpo che non è umano (500399), un giocatore (“Forse chiedere andrebbe meglio.”, 500398), un bersaglio oltre 2 caselle (500401 lui, 500402 lei) e un mendicante a cavallo (500404, una regola che ModernUO ha lasciato cadere solo con ML). Altrimenti il giocatore si inchina (HumanAnimationType.Bow) e, due secondi dopo, il tentativo avviene.

  • Chi ha fama cattiva: con karma sotto 0, una probabilità di 0.5 - karma/8570 che l’NPC rifiuti tutto il tentativo, dicendo “Thou dost not look trustworthy… no gold for thee today!” (500406) sopra la testa.
  • Il tiro è skill.check(user, "begging", 0, 100); un fallimento legge “They seem unwilling to give you any money.” (500404) — ModernUO e ServUO mandano questo all’NPC, dove nessuno lo vede, un difetto ereditato da RunUO.
  • L’oro: un decimo delle monete che l’NPC porta (item.find del template dell’oro), limitato da 10 + fame/2500 da 10 a 14, passa dallo zaino dell’NPC a quello del giocatore (a terra ai suoi piedi se lo zaino non ha posto) col suono della pila (0x2E4 una moneta, 0x2E5 fino a cinque, 0x2E6 oltre). L’NPC dice “I feel sorry for thee…” (500405), o “I have not enough money to give thee any!” (500407) senza monete; un NPC senza zaino riceve invece il rifiuto 500404.
  • Il karma: ricevuto l’oro, ciò che il mendicante ha sopra -3000, al massimo 40, attraverso common/karma.lua, che dice quanto è stato perso. L’oro coniato dal nulla di UOX3 e la Begging di Sphere (che non dà nulla) non sono seguiti.

L’abilità attende 10 secondi, il delay di data/skills.toml — l’attesa effettiva di ModernUO dal bottone; il suo segnaposto di 30 secondi del cursore non è tenuto. Il tiro del rifiuto di chi ha fama cattiva è begging.chance, che un test sostituisce.

scripts/items/shepherds_crook.lua è l’abilità Herding, come lo ShepherdsCrook di ModernUO: il bottone dell’abilità non fa nulla, il bastone sì. Un doppio clic su un bastone a portata (i template base_crook e base_shepherds_crook, che ogni bastone eredita) chiede “Target the animal you wish to herd.” (502464). I rifiuti: una cosa che non è un essere vivente (502472), un animale oltre 10 caselle (500446), uno che non può essere addomesticato — pet.lore non nomina alcuna min_skill (502468) — e uno che appartiene già a un giocatore (502467). Poi il posto: “Click where you wish the animal to go.” (502475), e il tiro tra la difficoltà di addomesticamento dell’animale meno 30 e quella più 30 e un tiro da 0 a 9 punti; quando perfino il culmine della finestra è sotto l’abilità del pastore legge comunque “That wasn’t even challenging.” (502471). L’animale è controllato di nuovo quando il posto è scelto: uno morto o addomesticato nel frattempo è rifiutato con gli stessi testi. Al successo le prop herding.x e herding.y sono impostate sull’animale e il pastore legge “The animal walks where it was instructed to.” (502479); un fallimento legge la 502472.

La camminata è dell’animale: common/creature.lua fa camminare una creatura con le prop un passo a think (npc.walk_to), finché non è arrivata o nessuna via vi conduce, e allora le pulisce; un passo rifiutato (blocked) è solo un ritardo e le conserva, fino a 20 think di fila (il conteggio herding.blocked), poi l’ordine è lasciato; un combattimento o una fuga vengono prima, e la camminata riprende dopo, e un animale addomesticato la lascia. La regola di UOX3 di condurre l’animale verso te (ti segue) non è tenuta.

scripts/skills/poisoning.lua è lo script dell’abilità Poisoning, come quello di ModernUO prima di AOS. Il giocatore sceglie il veleno da usare (502137) — una delle quattro pozioni di veleno dell’alchimia (potions.kind), portata o entro 2 caselle; ogni altra cosa non è una pozione di veleno (502139). Poi sceglie cosa avvelenare (502142): un’arma a una mano che taglia o buca — una spada, un’ascia, un’arma in asta o un’arma da scherma (item.combat_stats), come chiede ModernUO prima di AOS — oppure del cibo (un template con script_id = "food"); ogni altra cosa è rifiutata (502145). Il veleno suona (0x4F), la pozione è spesa e una bottiglia vuota torna indietro, e due secondi dopo l’abilità è provata tra il minimo e il massimo della finestra della pozione: minore 0-60, normale 30-70, maggiore 60-100, mortale 95-100 punti.

La pozione è ricontrollata alla seconda scelta (ancora a portata, non sul cursore), e il bersaglio quando arriva la prova: uno sparito, fuori portata o tenuto sul cursore non prende nulla (“You fail to apply a sufficient dose of poison”, 1010518, e la dose è persa), e un avvelenatore morto o uscito dal mondo non riceve nessun messaggio.

Al successo il veleno cavalca il bersaglio: la prop poison.level, il livello che conta il PoisonService — e un’arma anche poison.charges, 18 meno due per ogni livello — e il giocatore legge “You apply the poison” (1010517) e perde 20 karma attraverso common/karma.lua. Ogni colpo che va a segno spende una carica e il veleno passa a chi è colpito una volta su due (CombatService), come vuole la regola classica; entrambe le props spariscono all’ultima carica. Lo passano solo una spada e un’arma da scherma: un’ascia e un’arma in asta prendono il veleno ma lo tengono; un pezzo di cibo avvelenato avvelena chi lo mangia (food.lua). A un fallimento un avvelenatore sotto 80 punti commette un errore grave una volta su venti (poisoning.mistake, un test la sostituisce): lo legge (502148) e si avvelena da solo; altrimenti una lama legge la 1010516 e il resto la 1010518. L’abilità attende 10 secondi, il delay di data/skills.toml. Niente dardi fukiya né shuriken (niente ninjitsu), niente colpo infettivo di AOS.

scripts/skills/remove_trap.lua è lo script dell’abilità Remove Trap, come quello di ModernUO. Chiede un fabbro delle serrature e uno scout: 50 punti di Lockpicking (502366) e 50 di Detect Hidden (502367) prima del cursore, e chi ne ha di meno è mandato ad allenarla. Il giocatore sceglie la trappola da disarmare (502368) — un mobile è “inappropriato” (502816), una cosa né trasportata dal giocatore né entro 2 caselle troppo lontana, e una senza la prop trap.type «doesn’t appear to be trapped» (502373).

Il lavoro suona (0x241) — un forziere che il giocatore porta viene prima guardato dall’alto, come la regola Direction di ModernUO gira verso la cosa che si ha in mano — e l’abilità è provata tra la potenza della trappola e quella più 30 punti (trap.power): al successo le tre props della trappola spariscono — la prossima apertura del forziere non fa scoppiare nulla — e il giocatore legge «You successfully render the trap harmless» (502377); al fallimento legge «You fail to disarm the trap… but you don’t set it off» (502372). Un disarmo fallito non fa mai scattare la trappola, come in ModernUO. L’abilità attende 10 secondi, il delay di data/skills.toml. Niente trappole delle fazioni.

Punti vita, mana e stamina tornano da soli (vedi ultima.regeneration). Uno script cambia il ritmo di un mobile con le sue props, in secondi per punto: mobile.set_prop(who, "regen.hits", 2) lo cura cinque volte più velocemente del valore predefinito; nil gli restituisce il ritmo configurato. Le props sono regen.hits, regen.mana e regen.stamina.

Muoversi sottrae stamina a un giocatore (vedi fatigue_enabled in ultima.regeneration): correndo, e a ogni passo quando trasporta più di mobile.max_weight. Uno script che dà o toglie oggetti cambia subito ciò che il mobile trasporta; la barra di stato del giocatore segue al prossimo aggiornamento dello stato.

I due script di teletrasporto sotto condividono scripts/common/teleport.lua, un modulo Lua che prendono con local teleport = require("common.teleport"): teleport.send(serial, who) manda un mobile dove indicano le props dell’oggetto (teleport.x, teleport.y, teleport.z, teleport.map), con il fumo di source_effect e dest_effect e il suono di sound_id, e teleport.is_on(value) legge un flag che i file di decorazione contengono come testo. Un tuo script che teletrasporta può prenderlo allo stesso modo. Uno script conserva il modulo acquisito: dopo script reload common/teleport.lua, ricarica anche gli script che lo usano (vedi Ricaricamento e appartenenza). mgctl init aggiunge scripts/common/ a una root esistente e conserva gli script già presenti; una root i cui script degli oggetti vengono sostituiti manualmente richiede anche scripts/common/, altrimenti i suoi teletrasporti smettono di funzionare.

scripts/common/numbers.lua è il modo in cui gli script forniti scrivono un numero letto dal giocatore: numbers.with_thousands(1234567) restituisce "1,234,567". Il banchiere e l’assegno bancario lo prendono con local numbers = require("common.numbers"), e un tuo script può fare lo stesso. numbers.in_ranges(list, value) dice se un numero sta in una delle coppie { first, last } di un elenco, ed è come la fucina, il forno e i fuochi, gli alberi e il piccone riconoscono un grafico. Una root i cui script sono sostituiti manualmente ne ha bisogno, altrimenti banchieri, assegni bancari e gli script che riconoscono un grafico smettono di funzionare.

scripts/common/karma.lua toglie a un giocatore il karma di un atto, come Titles.AwardKarma di ModernUO per una perdita: karma.lose(user, cost) toglie cost e un centesimo del karma posseduto quando è positivo (un buon nome paga di più, uno cattivo paga meno, e nulla quando il suo karma è sotto -cost * 100), mai sotto -15000, e dice al giocatore quanto ha perso (“You have lost a little / some / good / a lot of karma”: da 1019063 a 1019066), “a little” quando la perdita è nulla. Snooping (4) e Provocation (75) lo usano.

common/geo.lua, speech.lua e skill_points.lua

Section titled “common/geo.lua, speech.lua e skill_points.lua”

Tre piccoli moduli tengono ciò che molti script scrivevano ognuno per sé. scripts/common/geo.lua conta le caselle come il raggio di visuale, la maggiore delle due differenze di coordinata: geo.distance(a, b) per due posti { x, y, map } (math.huge quando uno è nil o le mappe differiscono, così > range è vero per entrambi), geo.within(a, b, range) per lo stesso come booleano, geo.near(serial_a, serial_b, range) per due serial, sopra world.distance, che è falso quando non c’è una distanza, geo.same_storey(a, b) per sapere se due posti distano al più 16 in altezza, quindi stanno su un solo piano, geo.top_container(serial) per il contenitore più in alto in cui un oggetto sta (l’oggetto stesso quando non sta in nessuno) e geo.place_of(serial) per dove si trova un oggetto: dove sta il mobile che lo porta, o dove sta a terra il contenitore in cui si trova. scripts/common/speech.lua ha speech.has_keyword(keywords, wanted) per le parole chiave che il client ha sentito e speech.addressed(serial, text), vero quando le parole cominciano con il nome dell’NPC, senza badare alle maiuscole. scripts/common/skill_points.lua ha skill_points.of(who, name), i punti di un’abilità con il nome che le danno i file di dati (magery, evaluating_intelligence), 0 per una mai allenata e un errore per un nome che non è un’abilità; uno script che ha già l’abilità in mano scrive mobile.skill_points(who, SkillType.Mining).

Le tre funzioni sotto di loro stanno nei moduli: world.distance(a, b) dà le caselle tra due mobile o oggetti a terra per serial (nil per uno sconosciuto, un oggetto in un contenitore o due mappe), mobile.skill_points(serial, skill) dà un’abilità come numero senza costruire una tabella, e pet.owner(serial) dà il proprietario di una creatura, nil se non ne ha (una prop a 0 non è un proprietario). Una root i cui script sono sostituiti manualmente ha bisogno di scripts/common/geo.lua, speech.lua e skill_points.lua insieme agli script che li chiedono, altrimenti quegli script smettono di funzionare.

common/gump_util.lua, skill_ui.lua e il menu di common/shop.lua

Section titled “common/gump_util.lua, skill_ui.lua e il menu di common/shop.lua”

Altri tre moduli tengono ciò che i gump, le abilità che fanno scegliere qualcosa e i venditori scrivevano ognuno per sé. scripts/common/gump_util.lua è per gli script che riempiono uno slot di un gump: gump_util.guard_staff(player, admin) è il controllo del grado in cima a uno script (un amministratore quando admin è impostato), gump_util.staff_only(fn, admin) e gump_util.staff_button(g, spec, fn) fanno controllarlo di nuovo a un bottone al clic, perché il grado può essere sparito mentre il gump era aperto, e passano tutti gli argomenti del clic. gump_util.row(g, y, text, on_click, opts) è il bottone con il suo testo tagliato accanto, con la grafica gump_util.ART.pick, act, release o back; gump_util.paged(g, list, opts, fn) imposta il paginatore e chiama fn(entry, y, index) per ogni voce di un elenco impaginato, e gump_util.page_arrows disegna n / m e le frecce di un elenco che si pagina riaprendo il gump. gump_util.text(response, index, trim) legge ciò che è stato digitato in un campo, tagliato alle estremità solo quando trim è impostato. I gump go, pages, pages_detail, gmtools, jail_sentence, stable_claim, items, npcs e craft_menu li usano; l’elenco del tutorial conserva il solo builder, come esempio del tutorial dei gump.

scripts/common/skill_ui.lua è per le abilità che chiedono al giocatore di scegliere: skill_ui.pick_object(user, prompt, fn) dice il testo del client, dà il cursore e chiama fn(serial) con ciò che è stato scelto. skill_ui.pick_living(user, opts, fn) aggiunge i controlli di Anatomy ed Evaluating Intelligence, in quest’ordine e ognuno con il suo testo: il giocatore stesso, una cosa che non è viva, una invulnerabile e una più lontana di opts.sight, che non dice nulla. skill_ui.margin(points, base, step) e skill_ui.read_tenth(value, margin) sfumano un numero letto per ciò che manca all’abilità. scripts/common/shop.lua ha anche shop.menu(serial, player, range), le voci Buy e Sell seguite dalle voci Train di training.lua, e shop.select_any(serial, player, id), che passa la scelta prima al negozio e poi alle lezioni: il bottegaio, il guaritore e lo stalliere usano le due. Una root i cui script sono sostituiti manualmente ha bisogno di scripts/common/gump_util.lua e skill_ui.lua insieme agli script che li chiedono, e di training.lua insieme a shop.lua.

scripts/items/teleporter.lua è lo script del template decoration_teleporter che .decorate assegna al Teleporter di ModernUO: su on_move_over teletrasporta il giocatore alle props teleport.x, teleport.y e teleport.z con mobile.teleport, mostra uno sbuffo di fumo dove il giocatore è partito (prop source_effect) e arrivato (prop dest_effect), poi vi riproduce la prop sound_id quando il teletrasporto ne ha una. La prop active = false disattiva un teletrasporto, e la prop deny_mounted gli fa rifiutare un cavaliere (“You must dismount before proceeding.”, 1077252). Viaggiano solo i giocatori, a meno che la prop creatures sia true: allora viaggia anche un NPC che vi cammina sopra. Un teletrasporto con la prop teleport.map, un numero MapType, porta il giocatore su quella mappa: il client cambia mappa, poi riceve la stagione quando differisce da quella mostrata, la luce, il meteo e la musica del luogo; quando la mappa non è caricata non succede nulla. Il template ha visibility = "game_master": un oggetto a terra viene inviato solo agli account consentiti dalla sua visibilità, quindi i giocatori camminano su un teletrasporto che non vedono mai.

scripts/items/keyword_teleport.lua è lo script del template decoration_keyword_teleporter che .decorate assegna al KeywordTeleporter di ModernUO, come il mantra di un santuario: su on_speech teletrasporta il giocatore che dice la prop substring (trovata ovunque nel testo, senza distinzione di maiuscole) o il cui client invia la parola chiave del parlato della prop keyword, trovandosi entro range caselle (0, il valore predefinito, è la casella stessa del teletrasporto). Con un delay ("0:0:1" o un numero di secondi) il teletrasporto avviene più tardi, se il giocatore è ancora entro la portata. Destinazione, fumo, suono e active sono quelli del teletrasporto semplice.

scripts/items/public_moongate.lua è lo script del template decoration_public_moongate che .decorate posiziona su ogni destinazione di moongates.toml, come il PublicMoongate di ModernUO: su on_move_over, e su on_use dalla casella accanto, costruisce un gump con gump.create, una pagina per mappa di moongates.facets() e un pulsante per città, prima la pagina della mappa del giocatore, e riproduce il suono 0x20E. Un pulsante teletrasporta il giocatore con mobile.teleport, anche su un’altra mappa, e vi riproduce 0x1FE. Un giocatore che si è allontanato più di una casella mentre il gump era aperto viene avvisato e resta; scegliere la città del portale stesso non fa nulla.

scripts/mobiles/healer.lua è lo script dei guaritori (script_id = "healer"): a ogni think chiede a npc.ghosts_in_sight(serial, 4) i fantasmi entro 4 celle nella sua linea di vista. Un fantasma che non c’era al think precedente viene guardato (npc.look_at), riceve il suono 0x1F2 e le scintille SparkleHeal, e il gump resurrect con l’argomento healer, come il BaseHealer di ModernUO. Un guaritore aspetta 2 secondi (4 think) tra due offerte, e un fantasma incontrato durante l’attesa riceve l’offerta quando finisce. Un criminale viene rifiutato con il testo del client 501222, e un assassino (rosso) con 501223, e un giocatore con karma negativo si sente dire 501224 e riceve comunque l’offerta. Un guaritore malvagio, il cui id di template inizia con evil (evilhealer, evilwhealer), non rifiuta nessuno e non dice nulla. Un guaritore di un template che finisce con whealer, uno errante, fa un passo con npc.wander a ogni quarto think. Un guaritore con un negozio vende e compra come un venditore, tramite scripts/common/shop.lua: bende, pozioni, ginseng e aglio. Insegna anche le abilità che possiede, tramite scripts/common/training.lua (vedi Istruttori).

scripts/items/ethereal_mount.lua è lo script delle statuette eteree (script_id = "ethereal_mount"): il suo on_use chiama mount.ride_ethereal(user, serial), che dice con le parole del client perché rifiuta, e restituisce true così il doppio clic non apre nient’altro. Vedi Cavalcature.

scripts/items/mount_stone.lua è lo script delle pietre della cavalcatura (script_id = "mount_stone"): il suo on_use chiama mount.toggle_stone(user, serial), che cavalca la cavalcatura della pietra oppure, quando il giocatore sta già sulla cavalcatura di questa stessa pietra, scende; la pietra resta nello zaino. Dove il giocatore sale o scende compaiono una nuvola di fumo e il suo suono (0x1FE). Restituisce true così il doppio clic non apre nient’altro. Vedi Cavalcature.

scripts/items/engraved_stone.lua è lo script della pietra incisa (script_id = "engraved_stone"). on_use apre il gump di sola lettura stone_read (templates/gumps/stone_read.xml) con il titolo e le righe conservati nelle prop stone.title e stone.text, oppure dice che non c’è ancora scritto nulla; restituisce true. can_pick_up lascia sollevare la pietra al proprietario (la prop owner, assegnata all’acquirente dal venditore) e allo staff, e dice a chiunque altro che non è sua. can_drop rifiuta un rilascio in un contenitore che chi rilascia non porta con sé, così la pietra non può andarsene dentro una borsa. I limiti e le regole che lo script e i gump condividono sono in scripts/common/engraved_stone.lua. Il gump per scrivere, stone_write (templates/gumps/stone_write.xml, aperto da .write), ha i campi costruiti da stone_write.rows in scripts/gumps/stone_write.lua, che ricontrolla il proprietario e ripulisce i testi quando salva. Vedi Pietre incise.

scripts/items/engraved_ring.lua è lo script degli anelli che un gioielliere incide (script_id = "engraved_ring"). Il testo è conservato nella prop ring.text: on_tooltip aggiunge con esso la riga “Engraved: …” del client, e on_use lo dice al giocatore. scripts/common/engraving.lua è ciò che i gioiellieri condividono: la voce “Engraving:” del menu contestuale, la parola “engrave” detta a un gioielliere per nome entro 4 caselle, l’ordine preso dal gump ring_engrave (templates/gumps/ring_engrave.xml, costruito da scripts/gumps/ring_engrave.lua) e l’oro esatto lasciato sul gioielliere che lo paga: 500 monete d’oro per un anello, 1000 per una fede nuziale. shopkeeper.lua e common/shop.lua lo richiamano. Vedi Anelli incisi.

scripts/mobiles/stablemaster.lua è lo script degli addestratori di animali (script_id = "stablemaster"). Le parole stable e claim, dette entro 12 celle, e le voci Stable e Claim All del menu contestuale guidano il modulo stable: stable dà un cursore (target.pick) e chiama stable.stable sull’animale scelto, rispondendo con il testo del client per ogni StableResultType; claim dice l’introduzione dell’elenco e apre il gump stable_claim (templates/gumps/stable_claim.xml), oppure dice che non ci sono animali; Claim All chiama stable.claim sul primo posto finché l’elenco non è vuoto. Più addestratori sentono le stesse parole e stable.attend ne fa rispondere uno. Un addestratore di animali mantiene il suo negozio e le sue lezioni tramite scripts/common/shop.lua e training.lua. scripts/gumps/stable_claim.lua riempie il gump con un pulsante e il nome dell’animale per ogni animale di stable.pets, otto per pagina; un pulsante controlla che il giocatore sia entro 12 celle dall’addestratore e chiama stable.claim; un elenco cambiato da quando è stato mostrato viene mostrato di nuovo.

scripts/mobiles/hireling.lua è lo script dei dieci mercenari di Jhelom (script_id = "hireling"), fatto con common/creature.lua, di cui avvolge on_think, on_speech, on_drag_drop e on_death. Uno libero risponde alle parole hire, servant e a un saluto (SpeechKeywordType.Hire, Servant, Greeting) entro 6 celle, e alla voce Hire (cliloc 6120) del suo menu contestuale, con la sua paga: le dieci skill del suo template sommate, diviso 35, più 1. L’oro rilasciato su di lui (item.item_id 0x0EED) per almeno quella paga chiama pet.hire, prende l’oro con item.consume e lo tiene nella prop hire.gold, con hire.next_pay portata avanti di pet.hire_period() secondi. Il suo on_think salda in un passo i periodi trascorsi, secondo world.now(), e chiude il contratto con npc.delete e il testo 503235 del client quando l’oro non copre il periodo seguente; on_release azzera le due prop e on_death tiene fuori dal cadavere ciò che indossava. Uno assunto ha un padrone (pet_orders.is_owned), quindi le parole del padrone sono quelle di un animale. Vedi Mercenari.

scripts/common/pet_orders.lua è ciò che common/creature.lua esegue per una creatura che ha un owner: is_owned(serial) dice se ne ha uno (domata o evocata); think segue l’ordine nella prop pet.order (follow, come, stay o guard; follow se non ne ha), e listen, dall’ on_speech degli script delle creature, legge le parole del proprietario (SpeechKeywordType.PetCome, AllStay e le altre) entro 14 tile. Delle parole “all” solo all kill è eseguita dal primo animale che chiama pet.attend(owner), per ogni animale del proprietario a portata, dato che c’è un solo cursore; ogni altro ordine “all” è obbedito da ogni animale per conto suo; kill chiede un bersaglio con target.pick; release apre il gump pet_release (templates/gumps/pet_release.xml), il cui pulsante Release chiama pet.release dopo aver verificato che l’animale sia ancora del giocatore e entro 14 tile. Ogni ordine tranne release tira prima pet.obey(owner, pet) (la probabilità è pet.control_chance): un animale che rifiuta ringhia e si agita e non esegue l’ordine. feed(serial, giver, given) è ciò che gli script delle creature restituiscono da on_drag_drop: il cibo del proprietario va a pet.feed, che prende la pila e aumenta la lealtà (pet.loyalty); il cibo che la creatura non mangia viene restituito. Il cibo può anche legare l’animale (al proprietario viene detto 1049666). menu(serial, player) e select(serial, player, id) sono ciò che creature.new restituisce da on_context_menu e on_context_menu_select: i nove comandi del padrone (Kill, Follow, Guard, Stop, Stay, Rename, Add Friend, Remove Friend, Release) e i tre di un amico, ognuno uguale alla sua parola; Rename dice solo di modificare il nome nella barra di stato. Gli amici sono la prop pet.friends (seriali interi separati da virgole, al massimo cinque); is_friend(serial, who) la legge e listen ascolta un amico solo per follow, stay e stop, con il tiro di pet.obey sulle skill dell’amico; il follow di un amico tiene l’amico nella prop pet.follow, che think segue al posto del padrone finché un ordine del padrone non la toglie. Vedi Animal taming e lealtà, cibo e obbedienza.

scripts/skills/animal_taming.lua è l’abilità Animal Taming: on_use dice “Tame which animal?” (502789), dà un cursore (target.pick) e restituisce un’attesa di 1 secondo. La scelta viene rifiutata con il testo del client se non è una creatura, è un giocatore, non è in taming.toml (pet.info), ha un padrone, ci sono troppi seguaci (pet.followers e pet.max_followers), supera l’abilità o è a più di 3 caselle. Poi tre o quattro volte ogni 3 secondi (timer.after) ricontrolla la distanza (7), che il domatore sia vivo, la linea di vista (world.line_of_sight), che la creatura sia ancora selvatica e non ferita (mobile.stats), e dice una frase gentile; l’ultima tira skill.check(user, "animal_taming", min - 0.1, min + 49.9) e pet.tame. Vedi Domatura degli animali.

scripts/gumps/polymorph_forms.lua è lo script dell’elenco di forme dell’incantesimo Polymorph (templates/gumps/polymorph_forms.xml). Ognuno dei suoi 18 pulsanti chiama la funzione con il suo nome (chicken, dog, wolf, panther, gorilla, black_bear, grizzly_bear, polar_bear, human_male, slime, orc, lizardman, gargoyle, ogre, troll, ettin, daemon, human_female), che conserva il corpo nelle proprietà magic.polymorph_body e magic.polymorph_until (quindici secondi) e lancia di nuovo l’incantesimo, dal libro o da una pergamena dello zaino. Lo script dell’incantesimo scripts/spells/polymorph.lua cambia poi il corpo con mobile.disguise. Vedi Magery.

scripts/items/ankh.lua è lo script del template decoration_ankh, i due pezzi di ogni AnkhWest e AnkhNorth che .decorate posiziona. Non ha on_use: i vivi non hanno nulla da fare con un ankh. La sua on_ghost_use viene eseguita quando un giocatore morto lo usa con un doppio clic (Morte e resurrezione): da più di 2 celle dice “È troppo lontano.” (testo del client 500446), altrimenti apre il gump resurrect (templates/gumps/resurrect.xml). Il suo pulsante Continue chiama resurrect.accept in scripts/gumps/resurrect.lua, che, se il giocatore è ancora morto ed entro 2 celle dall’ankh (8 dal guaritore, se ha offerto un guaritore), chiama mobile.resurrect, riproduce il suono 0x214 e l’effetto SparkleHeal sul giocatore e gli toglie un decimo della fama; da cinque omicidi a breve termine toglie anche abilità e statistiche (Conteggi degli omicidi). Cancel non fa nulla.

scripts/items/moongate.lua è lo script del template moongate, il portale con una destinazione che il comando moongate posiziona ai piedi di un game master, come il Moongate di ModernUO. Su on_move_over, e su on_use dalla casella accanto, attende un secondo con timer.after, poi porta il giocatore, se è ancora lì, alle props teleport.x, teleport.y e teleport.z, sulla mappa della prop teleport.map (un numero MapType o il suo nome; la mappa del giocatore in sua assenza), e riproduce 0x1FE. Un portale senza i tre numeri, con una mappa inesistente o non caricata, o con un punto fuori dalla mappa dice al giocatore “Questo moongate non sembra portare da nessuna parte.” (messaggio 30114). Toccare nuovamente il portale durante quel secondo non avvia nulla. Quando il portale si trova in una regione sorvegliata e la destinazione no (world.is_guarded), chiede prima conferma: un gump con OKAY e CANCEL e il suono 0x20E; OKAY da più di una casella di distanza dice “È troppo lontano.” (messaggio 393) con mobile.message. Le regole ModernUO su sigilli, giovani giocatori, assassini e lancio di incantesimi non sono ancora presenti; i seguaci del giocatore entro 3 caselle passano con lui (mobile.teleport con l’ultimo argomento true). I portali dell’incantesimo Gate Travel sono del template magic_gate, che usa questo script: il suo timer di oggetto expire (on_timer) porta via il portale dopo 30 secondi, e l’incantesimo Dispel Field lo porta via, con il portale all’altro capo (la prop gate.partner).

scripts/items/bulletin_board.lua è lo script delle bacheche (il template di oggetto decoration_bulletin_board posizionato da .decorate, e bulletin_board e bulletin_board_2 per una bacheca aggiunta manualmente). Il suo on_use chiama board.open(serial, user) e restituisce true: il client del giocatore riceve la bacheca e l’elenco dei messaggi, e da lì legge, pubblica, risponde e rimuove i propri. Qualsiasi template di oggetto con questo script è una bacheca, ciascun oggetto con i propri messaggi.

scripts/items/clock.lua è lo script degli orologi (template degli oggetti clock e clock_2, e decoration_clock per quelli posizionati da .decorate), come il Clock di ModernUO: su on_use il giocatore legge sopra l’orologio la parte del giorno (“È pomeriggio”) e l’ora al minuto (“1:07 per l’esattezza”) del luogo in cui si trova, da world.time, come testi del client inviati con item.message_cliloc.

scripts/items/fillable.lua è lo script del template decoration_fillable che .decorate assegna ai contenitori cittadini, il FillableContainer di ModernUO: casse, scatole, forzieri e barili dei negozi e librerie delle biblioteche. Su on_use, prima dell’apertura del contenitore, un contenitore il cui tempo è arrivato (prop fill.next, secondo il conteggio di world.now()) e che contiene due cose o meno, una pila contando per la propria quantità, riceve fino al doppio di ciò che manca per arrivare a tre, ciascuna un’estrazione della tabella del bottino del proprio tipo con item.add_loot; una libreria si riempie fino a cinque libri. Poi attende da 60 a 90 minuti; un riempimento che non ha potuto aggiungere nulla viene riprovato all’apertura successiva. Non viene eseguito nulla mentre nessuno apre il contenitore, e i tempi sopravvivono al riavvio. Il tipo è la prop content_type, come baker per la tabella fillable_baker di templates/loots/fillable_containers.toml; in sua assenza il contenitore assume il tipo del venditore più vicino entro 20 caselle, indicato da mobile.template, e lo conserva. Senza venditori intorno resta vuoto e cerca di nuovo cinque minuti dopo. ModernUO inizia l’attesa quando viene estratto un oggetto e chiude a chiave e mette trappole al contenitore: questi aspetti non sono ancora presenti. Le tabelle cittadine usano templates/loots/randomshields.toml (uno scudo semplice, Loot.ShieldTypes di ModernUO) e i due prodotti di templates/items/town_goods.toml (mazzuolo e scalpello, aste per frecce) mancanti nei file di oggetti convertiti.

scripts/gumps/gmtools.lua è lo script del gump degli strumenti del game master (templates/gumps/gmtools.xml), aperto da .gmtools. Il gump ha due slot, riempiti da due funzioni: tools disegna la barra laterale, un pulsante per ogni voce della tabella tools nello script, e panel disegna il pannello di quello selezionato (args.tool, il primo quando non viene fornito o è sconosciuto). Un clic sulla barra laterale riapre il gump su quello strumento.

Ci sono quattro strumenti, meteo, stagione, ora ed eventi. Il pannello meteo legge world.weather_profile e world.weather e ha un pulsante per ciascun tipo, none, rain, snow e storm, che chiama world.set_weather sul giocatore, gli dice The weather of temperate is now storm until the next hour. e riapre il gump. Il pannello stagione legge world.season_here e la stagione della mappa del giocatore (world.season di mobile.location(player).map) e ha un pulsante per ogni stagione e uno per auto, che chiamano world.set_season o world.clear_season, dicono al giocatore The season of your map is now winter. e riaprono il gump. Il pannello ora legge world.time, world.moon, world.light_here e world.global_light e ha un pulsante per ciascuno di quattro livelli di luce e uno per auto, che chiamano world.set_global_light o world.clear_global_light e dicono al giocatore The global light is now 26.. Il pannello eventi (una voce con admin = true, quindi solo per gli amministratori) legge schedule.events e ha pulsanti auto, on e off per ogni evento, che chiamano schedule.set_event, dicono al giocatore Halloween is now off. e riaprono il gump. Solo staff: gli slot sono vuoti per chiunque altro, e ogni pulsante ricontrolla world.is_staff, e world.is_administrator per gli eventi.

Per aggiungere uno strumento, scrivi una funzione pannello con firma function(g, player) e aggiungi { id = "...", title = "...", panel = ... } a tools.

scripts/gumps/items.lua è lo script del gump della ricerca degli oggetti (templates/gumps/items.xml), che apre .items. Il gump ha uno slot, riempito da body(g, player, args): un titolo, un campo di testo legato a query con un pulsante Search che riapre il gump con esso, e poi le corrispondenze di item.search (le prime 40; il titolo dice quando ce ne sono di più), cinque per pagina scelta da args.page, ciascuna disegnata con g:item della sua grafica, il suo nome e id, e due pulsanti che chiamano item.give e item.create a mobile.location(player). Le frecce e n / m riaprono il gump sulla pagina prima o dopo, così un Give mantiene la pagina. Un ${name} digitato viene mostrato com’è digitato. Solo per lo staff: lo slot è vuoto per chiunque altro, e ogni pulsante ricontrolla world.is_staff.

scripts/gumps/npcs.lua è lo script del gump della ricerca degli NPC (templates/gumps/npcs.xml), che apre .npcs, costruito come items.lua. Il suo slot contiene un titolo, un campo di testo legato a query con un pulsante Search, e poi le corrispondenze di npc.search (le prime 40, il titolo dice quando ce ne sono di più), cinque per pagina scelta da args.page, ciascuna disegnata con g:portrait del suo corpo e con un pulsante Spawn che chiama npc.spawn a mobile.location(player). Le frecce riaprono il gump sulla pagina prima o dopo, così uno Spawn mantiene la pagina. Solo per lo staff: lo slot è vuoto per chiunque altro, e ogni pulsante ricontrolla world.is_staff.

scripts/mobiles/guard_outsider.lua è lo script delle guardie delle terre selvagge, per i luoghi fuori dalle città sorvegliate: lo usano i template guard_outsider (un’alabarda) e guard_outsider_archer (un arco) con script_id = "guard_outsider", e un game master ne mette a terra una con .npcs. A differenza delle guardie cittadine di guard.lua, che uccidono un mostro con un colpo e non si possono ferire, queste combattono sul serio: hanno il nome blu (notoriety = "innocent"), così i mostri le attaccano come fanno con i cittadini, da 151 a 191 punti vita, danno da 10 a 24 (prima dei bonus delle sue abilità e della forza), armatura 20, abilità di combattimento da 85 a 95, e colpi, impatti e morte sono del servizio di combattimento: un NPC combatte con la sua abilità di wrestling e il danno del suo template, l’alabarda e l’armatura che indossa sono solo scenografia (e cadono con il suo cadavere), e l’arciere spara con il suo arco. Lo script è common/creature.lua con la sua opzione prey: attacca i mostri selvatici che vede entro 16 caselle (gli NPC con lo script monster che nessuno ha domato, che non sono morti e non hanno il nome blu: un combattente o un paladino delle città ha anch’esso lo script dei mostri), e non inizia mai uno scontro con un giocatore, un cittadino, un animale o un animale domestico; risponde a un colpo come qualsiasi creatura, e il suo template imposta flee_at = -1, quindi non scappa mai. Uno con un arco si ferma dove l’arco arriva e spara.

scripts/gumps/help_menu.lua è lo script del menu di aiuto (templates/gumps/help_menu.xml), che si apre con il pulsante Help del paperdoll. stuck è il pulsante «Sono bloccato»: rifiuta un personaggio in prigione (jail.sentence) o in combattimento (combat.target), uno che sta già aspettando e uno la cui pausa (la proprietà help.stuck_until, secondi dal 1970 da world.now) non è finita, escluso lo staff; prende la città di partenza più vicina da help.nearest_city, dice l’attesa da help.settings e, dopo timer.after di quei secondi, sposta il personaggio con mobile.teleport se è dove era e se ha ancora il permesso. commands esegue help con commands.execute_as, rules dice il messaggio 30200 e call apre help_page_kind.

scripts/gumps/help_page_kind.lua è lo script del secondo passo di Call a game master (templates/gumps/help_page_kind.xml). question, bug, suggestion e harassment interrogano help.can_page; a un giocatore che non può viene detto perché (messaggi 30213 e 30214) prima che scriva qualcosa. Altrimenti gli viene detto di scrivere una riga (30211), prompt.ask la aspetta e help.create_page manda la richiesta con l’HelpPageKindType del pulsante; Esc o una riga vuota dice 30215, un rifiuto alla fine ripete il suo motivo e una richiesta inviata dice 30212. Può chiamare anche un giocatore in prigione.

scripts/gumps/pages.lua è lo script della coda dello staff (templates/gumps/pages.xml, aperta da .pages). rows riempie lo slot con help.pages(), dalla più vecchia, dieci per pagina, un pulsante e una riga per richiesta (#1 Gino, Bug, 3 min, open); una riga apre pages_detail su quella richiesta. Solo per lo staff: lo slot resta vuoto per chiunque altro, e ogni pulsante ricontrolla world.is_staff.

scripts/gumps/pages_detail.lua è lo script di una richiesta (templates/gumps/pages_detail.xml). go porta il game master dal giocatore (mobile.location) o, se è offline, dove ha chiesto; take chiama help.take; answer prende il testo del campo (response.text[1]), rifiuta un campo vuoto e altrimenti chiama help.answer e torna alla coda; close chiama help.close. Una richiesta chiusa nel frattempo lo dice e non cambia nulla.

scripts/common/help_pages.lua è il modulo Lua che i due gump dello staff condividono, preso con require("common.help_pages"): le parole per un genere, una mappa, un’età e uno stato, la riga di una richiesta e gli argomenti del suo gump di dettaglio.

scripts/gumps/jail_sentence.lua è lo script del gump della prigione (templates/gumps/jail_sentence.xml), aperto da .jail senza bersaglio, e da .jail <name> sul giocatore con quel nome. La sua funzione rows riempie lo slot: prima un pulsante che fornisce il cursore con target.pick e riapre il gump sul personaggio selezionato, poi le celle di jail.cells(), dieci per pagina. Con un personaggio selezionato, una cella libera ha un pulsante che legge i giorni digitati nel gump e chiama jail.send(target, cell, days, who, reason), il motivo è ciò che viene digitato nel secondo campo; giorni vuoti, non numerici, frazionari o oltre jail.max_days() non incarcerano nessuno, e il gump si riapre con il motivo. Una cella occupata mostra il nome, il tempo rimanente come 2d 4h, 5h 10m o 12m, e un pulsante che chiama jail.release. Ogni cella ha un pulsante che vi porta il game master con mobile.teleport(who, cell.x, cell.y, cell.z, cell.map), sulla mappa della prigione. Un bersaglio già in prigione ha il proprio rilascio su una riga separata in alto. Un bersaglio fuori dal mondo, un giocatore trovato da .jail <name>, viene mostrato come (offline): mobile.name è nil per lui. jail.send risponde allora JailResultType.Pending e il game master viene informato che la cella è riservata fino al login; una cella così mostra waits for login, dal pending di jail.cells(). Quando il gump viene aperto con candidates, più giocatori con lo stesso nome, la funzione li elenca, al massimo dieci, con account e stato online, ciascuno con un pulsante che apre il gump su di lui, e omette le celle finché non ne viene scelto uno. Ogni pulsante ricontrolla world.is_staff: il grado potrebbe essere stato rimosso mentre il gump era aperto.

scripts/items/jail_note.lua è lo script del template jail_release_note, la nota che un prigioniero trova nello zaino alla fine della pena. Su on_use delega a book.open, mostrando book.content salvato o il vecchio jail.text: giorni scontati, cella, date e multa pagata. La pergamena condivisa esegue l’escape del testo semplice e scorre i testi lunghi. Una nota senza testo, come una creata con .add, non mostra nulla.

scripts/items/readable_scroll.lua delega il doppio clic a book.open. Il template non impilabile readable_scroll è usato dal catalogo dei testi. Crea una lettera personalizzata con book.give(player, "welcome_letter", { contact_name = "Vega" }). Titolo, autore e corpo salvati restano fissi quando un altro giocatore la legge.

scripts/items/readable_book.lua delega il doppio clic a book.open come la pergamena; per un oggetto del template readable_book, book.open invia il libro del client, la copertina e ogni pagina, invece della pergamena (libri e pergamene). I testi importati da ModernUO lo usano: book.give(player, "grammar_of_orcish").

scripts/items/bank_check.lua è lo script degli assegni della banca, che un banchiere scrive per l’oro della banca (“check 5000”). Un doppio clic su un assegno dentro il forziere aperto della banca lo trasforma di nuovo in monete del forziere, in pile da 60000. Un forziere con spazio per una parte dell’oro prende ciò che ci sta e l’assegno tiene il resto. Fuori dal forziere della banca un assegno è soltanto un pezzo di carta che vale quanto dice il suo suggerimento. Vedi Banca.

scripts/items/snow_pile.lua è la palla di neve dell’evento di Natale. Un doppio clic su una pila nello zaino chiede un bersaglio; la palla vola verso un mobile che porta a sua volta una pila, lo colpisce, ed entrambi lo leggono. Un giocatore aspetta 5 secondi tra due palle di neve e non può lanciarne una in sella. Un template di oggetto la usa con script_id = "snow_pile".

scripts/mobiles/banker.lua è lo script dei banchieri. Un giocatore entro 12 caselle dice una parola e il banchiere apre il suo forziere (“bank”), dice il saldo (“balance”), consegna oro (“withdraw 500”), lo prende (“deposit 500”) o scrive un assegno (“check 5000”). Il client trasforma la maggior parte di queste parole in parole chiave del discorso in qualsiasi lingua; “deposit” è solo in inglese. L’oro e gli assegni lasciati sul banchiere vanno in banca (on_drag_drop). Un banchiere non fa affari con un criminale. Quanto consegna in una volta è l’impostazione ultima.bank.max_withdraw. Vedi Banca.

scripts/mobiles/quest_giver.lua è lo script degli NPC che danno le quest migrate da ModernUO (script_id = "quest_giver" del template ml_questgiver, vedi Quest): stanno dove sono (lo script non ha on_think). on_mobile_in_range passa il mobile che si è avvicinato a quest.on_near di scripts/events/quest.lua, che interroga common/quest_shout.lua: un giocatore vivo e visto dall’NPC quando entra in ultima.npcs.sense_range (8 tile di default, come il raggio delle grida di ModernUO), che ha una quest disponibile dal template dell’NPC, lo fa voltare e dire, con npc.say_cliloc_to, a quel giocatore soltanto, uno dei numeri che il suo template ha in scripts/quests/ml_shouts.lua, e l’NPC tace per un minuto (60 secondi di world.now()). Un NPC senza frasi costa l’hook in coda, una chiamata Lua e una ricerca del template per ogni mobile che si avvicina. Insegna le abilità che ha tramite common/training.lua, come shopkeeper: on_speech (la parola train, a cui un NPC che non ha nulla da insegnare non risponde), on_context_menu e on_context_menu_select (le voci Train) e on_drag_drop (l’oro per una lezione). Le voci di quest del menu contestuale e il doppio clic sono del motore.

scripts/mobiles/shopkeeper.lua è lo script dei venditori PNG. Un giocatore sceglie Buy o Sell nel menu contestuale del venditore, oppure dice “vendor buy” o “vendor sell” entro 4 caselle, e si apre la finestra del negozio. Cosa vende un venditore è il suo negozio in templates/shops; la finestra, i prezzi e l’acquisto sono lavoro del server (il modulo vendor), non dello script. I venditori insegnano anche abilità (common/training.lua), i gioiellieri incidono anelli (common/engraving.lua), e mentre l’evento halloween è attivo rispondono a “trick or treat” (common/trick_or_treat.lua). Vedi Venditori.

scripts/common/guild.lua è ciò che condividono gli script dei maestri di gilda. Un giocatore entro 2 celle dice il nome del maestro di gilda e “join” o “member” per sapere il prezzo della sua gilda (500 monete d’oro), lascia esattamente quell’oro su di lui per entrare, e dice il suo nome e “resign” o “quit” per uscire, al più presto una settimana dopo essere entrato. Il server tiene l’iscrizione (il modulo npcguild). guild.listen(serial, speaker, text, keywords) gestisce le parole e guild.drop(serial, giver, item) l’oro.

guildstone_deed.lua, guildstone.lua e gumps/guild_menu.lua

Section titled “guildstone_deed.lua, guildstone.lua e gumps/guild_menu.lua”

Le gilde dei giocatori, come quelle dello shard classico, contro i maestri di gilda PNG qui sopra: una gilda si fonda su una pietra, non si entra con l’oro. scripts/items/guildstone_deed.lua spende l’atto, portato o a terra entro 2 caselle: a un giocatore senza gilda è chiesto il nome (da 30275 a 30279 dicono del problema) e sceglie il terreno, un punto entro 3 caselle su cui si può stare e libero (30293 quando non lo è), e vi è posata una guildstone che tiene la gilda con le sue prop guild e guild_founded, il fondatore suo primo membro; la gilda è fatta dopo la pietra, e un rifiuto lascia l’atto e nessuna pietra. scripts/items/guildstone.lua apre guild_menu a un membro della gilda della pietra entro 2 caselle; a un estraneo è detto 30281 e a una pietra sciolta 30280. Quando un’altra pietra sta entro 5 caselle il gump è aperto con l’altra gilda, così il capo può dichiarare su di essa. scripts/gumps/guild_menu.lua riempie il gump: l’elenco dei membri a pagine, il capo per primo, un clic dà fedeltà, il capo scrive lo statuto, il sito e il colore e dà titoli ed espulsione, e i pulsanti di guerra e alleanza lavorano l’altra gilda. Il gump prende il nome dal menu, non dal modulo: uno script di gump possiede la tabella globale del proprio nome. Vedi Gilde.

scripts/common/holiday_decor.lua mette le decorazioni di un evento festivo intorno al centro delle città principali, su Felucca e su Trammel. place(event_id, templates) viene eseguita quando un evento inizia, remove(event_id) quando finisce. I serial sono conservati nella prop del mondo holiday.<event_id>.items, quindi gli oggetti spariscono anche dopo un riavvio, e posizionare due volte non mette nulla due volte.

scripts/common/trick_or_treat.lua è il gioco di Halloween. Mentre l’evento halloween è attivo (data/schedule.toml), un giocatore che dice “trick or treat” entro 4 caselle da un negoziante riceve una caramella, o uno scherzo. Ogni negoziante riposa da 5 a 10 minuti dopo aver risposto, e una frase viene raccolta da un solo negoziante. shopkeeper.lua chiama listen dal suo on_speech.

events/christmas.lua e events/halloween.lua

Section titled “events/christmas.lua e events/halloween.lua”

Gli hook degli eventi stagionali di data/schedule.toml: on_start(id, name) decora le città (common/holiday_decor.lua) e avvisa tutti, on_end(id, name) toglie le decorazioni e avvisa tutti. Halloween ha solo questi due; il gioco in sé è common/trick_or_treat.lua. Natale ha anche on_login(id, name, player): un personaggio che entra durante l’evento riceve un regalo una volta (due pile di neve, una candela festiva e una decorazione), e non di nuovo per 200 giorni. Vedi Programmazione.

common/quest.lua, common/quest_ui.lua e events/quest.lua

Section titled “common/quest.lua, common/quest_ui.lua e events/quest.lua”

Il motore delle quest, i suoi gump e lo script che lo avvia e risponde al server; vedi Quest per il formato di una quest e come funzionano i pezzi.

scripts/gumps/go.lua è lo script del gump dei luoghi con nome (templates/gumps/go.xml) che .go apre. rows(g, player, args) riempie lo slot “rows” con un livello di data/locations.toml, prima le categorie e poi i luoghi, dodici per pagina. Una categoria apre il gump un livello più in basso, un luogo porta lì il viaggiatore. Solo per lo staff: chiunque altro vede un gump vuoto.

Tra gli script distribuiti non c’è nessun definitions.lua: il motore lo genera all’avvio, per il completamento dell’editor, dai moduli, dalle funzioni e dalle enum registrati (vedi Scrivere un modulo Lua). Non modificarlo.

Creato con ❤️ da Tom

Offrimi un caffè