Salta ai contenuti

Script degli oggetti

Questa pagina fa parte di Scrivere script Lua. Le funzioni che uno script chiama sul proprio oggetto sono nel riferimento: item. Ogni hook che il server chiama, con i suoi argomenti e se viene eseguito dentro l’azione, è nell’unico elenco degli hook degli script.

Un template di oggetto indica lo script con script_id, il nome di una tabella Lua globale definita da scripts/items/<script_id>.lua. Il server carica i file direttamente in scripts/items/ all’avvio, dopo il bootstrap.

Usa uno shard game o standalone in esecuzione e un personaggio con accesso GameMaster o Administrator. I percorsi qui sotto sono relativi alla root del server. Questi nomi creano nuovi contenuti; non sostituiscono lo script delle pozioni distribuito.

  1. Crea templates/items/tutorial_greeting_stone.toml:
[[item]]
id = "tutorial_greeting_stone"
name = "greeting stone"
item_id = 0x0ED4
movable = true
script_id = "tutorial_greeting_stone"
  1. Crea scripts/items/tutorial_greeting_stone.lua:
tutorial_greeting_stone = {}
function tutorial_greeting_stone.on_use(serial, user)
item.message(serial, user, "The stone welcomes you.")
return true
end
  1. Riavvia il server per caricare il nuovo template e lo script. I template vengono letti all’avvio; script reload da solo non carica un nuovo template TOML.
  2. Nel client di gioco, pronuncia .add tutorial_greeting_stone e scegli il terreno accanto al personaggio. Il comando deve segnalare Added greeting stone con il seriale e la posizione. Vedi add per selezione e permessi.
  3. Fai doppio clic sulla pietra entro 2 caselle e in vista. Il personaggio deve leggere The stone welcomes you. Il valore restituito true segna l’uso come gestito.

Dopo aver modificato soltanto il file Lua, inserisci script reload items/tutorial_greeting_stone.lua nella console del server e fai di nuovo doppio clic sullo stesso oggetto. Se il comando segnala un template sconosciuto, verifica il percorso TOML e riavvia; se l’oggetto compare ma non dice nulla, verifica gli errori Lua e che nome del file, tabella e script_id coincidano.

Funzione Quando
on_use(serial, user) Un giocatore fa doppio clic sull’oggetto, trasportato (indossato o nei propri contenitori) oppure a terra entro 2 caselle (il use_range del template) e visibile; più lontano, il giocatore legge “È troppo lontano.” e non viene eseguito nulla. Gli oggetti dentro un contenitore a terra non possono ancora essere usati: il giocatore legge “È troppo lontano.”. Un on_use assente, oppure che genera un errore, lascia proseguire l’azione predefinita. Restituisci true per interrompere l’azione predefinita, come l’apertura di un contenitore; non restituire nulla per lasciarla proseguire. Un handler che chiama wait conta come gestito; dopo l’attesa l’oggetto potrebbe essersi spostato, quindi controllalo di nuovo, per esempio item.owner(serial) == user.
on_ghost_use(serial, user) Un giocatore morto fa doppio clic sull’oggetto (l’ankh lo usa). Senza di esso il giocatore legge che i morti non possono usarlo.
on_npc_use(serial, npc) Un NPC che cammina verso un luogo trova l’oggetto sulla sua strada e può usarlo; una porta si apre (fino a tre tentativi). La chiamata è in coda, quindi ciò che restituisce viene scartato: l’NPC non sa se ha funzionato.
on_move_over(serial, mobile) Un giocatore è entrato nella casella dell’oggetto, a terra all’altezza del giocatore, fino a 14 unità sopra i piedi, oppure sotto di essi e abbastanza alto da raggiungerli (la regola di ModernUO). Viene eseguito dopo che il passo è stato confermato e mostrato ai giocatori vicini; un NPC esegue invece on_npc_move_over. Una volta che uno script ha spostato il giocatore fuori dalla casella, gli altri oggetti della casella non vengono eseguiti. Arrivare tramite teletrasporto non lo attiva, quindi due teletrasporti che puntano l’uno all’altro non creano un ciclo.
on_npc_move_over(serial, npc) Un NPC è entrato nella casella dell’oggetto, con la stessa regola sull’altezza. Viene eseguito al turno del game loop successivo al passo, quindi l’NPC potrebbe essersi già spostato: controlla dove si trova
on_speech(serial, speaker, text, keywords, type) Un giocatore ha pronunciato text a portata d’orecchio dall’oggetto a terra (i comandi non vengono ascoltati): 15 caselle a voce normale o come emote, 1 casella per un sussurro, 18 per un urlo. speaker è il seriale del giocatore; keywords sono le parole chiave del parlato individuate dal client, un array di numeri; type è il modo in cui è stato detto, come SpeechType.Whisper. Ogni oggetto a terra con script nel raggio viene interrogato, dopo gli NPC, quindi uno script controlla il proprio raggio e le parole. Può chiamare wait.
on_equip(serial, wearer) L’oggetto è passato su un layer del mobile wearer, rilasciato sul paperdoll. Un oggetto indossato sollevato e respinto non ha mai lasciato il proprio layer, e gli oggetti caricati o generati già indossati non attivano nulla. Non può rifiutare l’oggetto: lo fa can_equip.
on_unequip(serial, wearer) L’oggetto ha lasciato il layer di wearer: rilasciato in un contenitore o a terra, unito a una pila, oppure tolto dal mondo mentre era indossato, per decadimento, item.delete, item.consume, .remove, una statuetta di cavalcatura usata o l’equipaggiamento di un NPC che finisce nel suo cadavere. L’oggetto potrebbe non esserci più, quindi item.* restituisce nil. Disconnettersi, rimuovere un NPC o eliminare un mobile con i suoi oggetti non attivano nulla.
on_pickup(serial, picker) Il giocatore picker solleva l’oggetto da un contenitore, dal paperdoll o da terra; sollevare parte di una pila solleva questo oggetto, e il resto lasciato indietro non è nuovo. Mentre è tenuto, item.consume e item.delete lo rifiutano. Un oggetto tenuto termina in on_drop, in on_equip quando viene indossato da un nuovo portatore, oppure in nulla: quando viene respinto, indossato di nuovo sul layer da cui proviene o quando il suo giocatore si disconnette tenendolo.
on_drop(serial, dropper) Il giocatore dropper posa l’oggetto tenuto: in un contenitore, a terra o su una pila (l’oggetto allora non esiste più, quindi item.* restituisce nil). Non quando viene respinto o indossato. Un oggetto indossato che viene posato esegue prima on_unequip, poi on_drop.
can_pick_up(serial, picker) Il giocatore picker sta per sollevare l’oggetto da un contenitore, dal paperdoll o da terra, quando tutte le regole del server lo consentono e prima che una pila venga divisa. Restituisci false per rifiutare: l’oggetto rimane dov’è e il client non mostra alcun messaggio proprio. Un oggetto indossato viene sollevato prima di essere tolto, quindi questo è anche il punto in cui uno script mantiene un oggetto sul suo portatore
can_drop(serial, dropper, destination) Il giocatore dropper sta per posare l’oggetto tenuto, ovunque: a terra, in un contenitore o su una pila. destination è il serial che il client indica per il rilascio: un contenitore, un oggetto, un mobile, oppure 4294967295 per il terreno. Restituisci false per rifiutare: l’oggetto torna da dove è stato sollevato
can_equip(serial, wearer) L’oggetto tenuto sta per essere indossato da wearer, quando il layer è libero e le regole lo consentono. Restituisci false per rifiutare: l’oggetto torna da dove è stato sollevato e on_equip non viene eseguito. Non viene interrogato per un oggetto indossato sollevato e rimesso sul suo layer, che non ha mai lasciato
can_insert(serial, mobile, item) Interrogato su un contenitore: il giocatore mobile sta per inserirvi item, oppure per unirlo a una pila direttamente al suo interno, quando le regole consentono il rilascio. serial è il contenitore, trasportato o a terra; un contenitore che contiene quel contenitore non viene interrogato. Restituisci false per rifiutare: l’oggetto torna da dove è stato sollevato. Viene interrogato dopo il can_drop dell’oggetto e una volta per ogni rilascio
on_context_menu(serial, player) Quel giocatore chiede il menu contestuale dell’oggetto: uno che porta, oppure a terra o in un contenitore lì, in vista. Restituisci le voci che l’oggetto aggiunge, una tabella di { id, cliloc, range, enabled }, oppure nulla; risponde subito e non deve chiamare wait.
on_context_menu_select(serial, player, id) Il giocatore ha scelto una di quelle voci: id è quello della voce. Chiamato solo per una voce che il giocatore ha visto e che è nella sua portata.
on_timer(serial, name) È scaduto un timer dell’oggetto, avviato con item.start_timer. I timer vengono verificati una volta al secondo. Il timer non esiste più quando la funzione viene eseguita: avvialo di nuovo lì per qualcosa che si ripete. Un timer scaduto mentre il server era fermo, o mentre il personaggio che trasportava l’oggetto era offline, viene eseguito appena l’oggetto torna nel mondo. Può chiamare wait
on_darkness(serial, dark) Ogni 30 secondi e subito dopo .globallight, su un lampione (template decoration_light, proprietà decoration_type da LampPost1 a LampPost3) il cui punto è diventato buio (true) o luminoso (false)
on_create(serial) Un oggetto appena creato entra nel mondo: l’equipaggiamento, lo zaino e il bottino di un NPC generato, prima dell’on_spawn di quell’NPC, e una cassa creata da una regione di spawn, con tutto ciò che contiene. Gli oggetti iniziali di un nuovo personaggio, il resto di una pila divisa e gli oggetti creati da item.give, item.create, item.add_loot o .decorate non attivano nulla.
on_delete(serial) L’oggetto viene eliminato, in qualunque modo: decadimento, una pila che lo assorbe, item.delete, item.consume di tutto, .remove, il suo contenitore eliminato con il contenuto, l’equipaggiamento di un NPC rimosso. Chiesto una volta per oggetto, prima che esca dal mondo: l’oggetto, le sue proprietà e il suo contenitore si possono ancora leggere. Gli oggetti di un contenitore vengono chiesti prima del contenitore (un contenitore che decade è già vuoto quando gira il suo; l’equipaggiamento di un NPC rimosso va prima il contenuto, con l’inventario ancora al suo posto finché non sono girati tutti). Un oggetto che non è mai entrato nel mondo non viene chiesto. Serve a ripulire ciò che l’oggetto possiede, come un timer o una proprietà di un altro oggetto. Non deve eliminare né spostare l’oggetto o il suo contenitore: un gestore che lo fa non può fermare l’eliminazione. Non gira nulla quando un giocatore esce dal gioco
can_decay(serial) Scade il tempo di decadimento di un oggetto a terra. Restituisci false per tenerlo: viene richiesto dopo un altro tempo di decadimento, decay_minutes del suo template (60 di default), non a ogni controllo. Chiesto all’oggetto che decade, non a ciò che contiene, e non a un oggetto che non decade mai
on_added(serial, container, actor) L’oggetto è entrato in un contenitore, o nello zaino di un mobile, per qualunque via: un trascinamento, item.move_into, item.give, un acquisto, un pezzo costruito, bottino, un oggetto creato direttamente dentro. actor è il mobile che l’ha causato: il giocatore che l’ha lasciato, l’acquirente di un acquisto, il mobile a cui item.give l’ha dato; 0 quando non è stato nessuno, come per il bottino messo in un cadavere, un item.move_into di uno script o il contenuto di uno scrigno. Non per un oggetto indossato, che ha on_equip, né per una pila che cresce o si divide, né per gli oggetti caricati all’avvio o al login, piazzati da .decorate o creati da un allegato di libro, che entra nello zaino in silenzio
on_removed(serial, container, actor) L’oggetto è uscito da un contenitore o da uno zaino senza essere eliminato: è stato spostato in un altro contenitore, messo a terra o indossato. Sollevarlo non è uscire, perché un oggetto sollevato resta dov’è finché non viene lasciato (on_pickup). Un oggetto eliminato esegue invece on_delete
on_item_added(serial, item, actor) Chiesto a un contenitore: item vi è entrato, per una qualunque delle vie di on_added. serial è il contenitore. Lo zaino di un mobile non ha uno script e non gli viene chiesto nulla; un contenitore il cui script non ha la funzione non costa nulla
on_item_removed(serial, item, actor) Chiesto a un contenitore: item ne è uscito, o è stato eliminato mentre era dentro (allora actor è 0). item può non esistere più quando la funzione gira
on_move_off(serial, mobile) Un giocatore o un NPC è sceso dalla cella dell’oggetto, con la regola dell’altezza di on_move_over. Per un NPC gira al turno dopo il passo, con il mobile già sulla nuova cella, e non gira affatto se nel frattempo l’oggetto ha lasciato la cella; per un giocatore gira subito, dentro il passo, prima di on_move_over della cella in cui è entrato. Un teletrasporto non attiva nulla
on_mobile_near(serial, mobile, distance) Un giocatore o un NPC è entrato nel watch_range dell’oggetto, un campo del template da 1 a 8 caselle (0 o assente non osserva nulla e non costa nulla), con un passo, un teletrasporto o entrando nel mondo. Scatta una volta per ingresso da fuori dal raggio, non a ogni passo dentro; distance è la maggiore tra i due assi. Un mobile nascosto e un fantasma non vengono notati
on_tooltip(serial, viewer) La lista delle proprietà dell’oggetto viene costruita per il giocatore viewer. Restituisci un elenco di righe da aggiungere dopo quelle del server: una stringa, oppure { cliloc = 1060658, args = "Name\tvalue" }. Se ne prendono al massimo 16, e al massimo cinque di esse stringhe semplici: il client mostra solo cinque righe di testo libero, meno quando le righe del server ne usano già alcune (le righe cliloc hanno solo il limite di 16). Una riga malformata viene scartata e un errore non aggiunge nulla. Risponde subito, non deve chiamare wait, e viene chiesta ogni volta che il client chiede la lista, solo per un oggetto il cui script la definisce: aprire un contenitore la esegue una volta per ogni oggetto con script al suo interno. Un clic singolo mostra solo il nome e non la esegue
can_drop_on(serial, dropper, item) Chiesto a un oggetto che non è un contenitore: il giocatore dropper sta per lasciarvi sopra l’item tenuto in mano, che sta a portata o è portato con sé. Restituisci false per rimandare l’oggetto dov’era stato sollevato. Chiesto dopo il can_drop dell’oggetto e prima delle altre regole, quindi prima che una pila si fonda. I contenitori usano can_insert
on_carving(serial, carver, items) Chiesto allo script di un cadavere (scripts/items/corpse.lua): un coltello sta per togliere degli oggetti, prima che ne venga creato qualcuno. items è il tiro della tabella di smembramento del cadavere, un array di { item = template id, amount = n }. Restituisci false per annullare (non viene creato nulla, il cadavere non viene segnato come lavorato e chi lo lavora si sente dire che non può usare la lama su quello), una tabella della stessa forma per sostituire il tiro, o qualsiasi altra cosa per tenerlo. Risponde subito e non deve chiamare wait; una voce tiene il message con cui è arrivata. Vedi Smembrare i cadaveri
on_dropped_on(serial, dropper, item) Il rilascio su questo oggetto è stato consentito ed è avvenuto: item è stato messo accanto, o fuso in esso (e allora non esiste più). Anche quando nient’altro ha preso l’oggetto e nessun contenitore l’ha rifiutato: l’oggetto è allora tornato dov’era stato sollevato, e lo script può prenderlo da lì con item.consume o item.delete. Un rilascio rifiutato per il peso o lo spazio di un contenitore non attiva nulla

on_equip, on_unequip, on_pickup, on_drop, on_create, on_added, on_removed, on_item_added, on_item_removed, on_move_off, on_mobile_near e on_dropped_on vengono eseguiti subito dopo ciò che li ha causati, al turno successivo del game loop, quando i giocatori lo hanno visto: uno script può allora eliminare o consumare l’oggetto. Non tutti vengono chiamati se l’oggetto è stato eliminato nel frattempo: on_added, on_removed, on_item_added, on_item_removed, on_mobile_near, on_dropped_on e l’on_move_off del passo di un NPC vengono scartati quando l’oggetto a cui sono rivolti (il contenitore, per on_item_added e on_item_removed) non c’è più, mentre on_equip, on_unequip, on_pickup, on_drop e on_create vengono chiamati comunque, e item.* dà allora nil per l’oggetto. L’on_move_off del passo di un giocatore non viene accodato: gira subito, dentro il passo, prima di on_move_over della cella in cui è entrato. Sono notifiche: nessuna può rifiutare il movimento. on_delete è l’eccezione: gira dentro l’eliminazione, prima che l’oggetto sparisca, così che si possa ancora leggere.

can_pick_up, can_drop, can_equip, can_insert, can_drop_on e can_decay sono domande, poste prima del movimento e risposte subito: solo false rifiuta. Una funzione assente, un errore, una chiamata a wait o qualsiasi altro valore lasciano proseguire il movimento, quindi uno script guasto non blocca mai un oggetto. Spiega al giocatore il motivo con mobile.message prima di restituire false. Vengono interrogate per i movimenti che un giocatore compie con il client, staff incluso; uno script che sposta autonomamente un oggetto (item.move_to, item.move_into) non viene interrogato. Mentre viene posta una domanda l’oggetto conta come tenuto, quindi item.delete, item.consume, item.move_into e le funzioni che lo modificano lo rifiutano: rispondi lì alla domanda e agisci sull’oggetto in on_pickup, on_drop o on_equip.

-- a cursed ring: once worn, it stays on
function ring.can_pick_up(serial, picker)
-- worn: it has an owner and lies in no container
if item.owner(serial) == picker and item.container(serial) == nil then
mobile.message(picker, "The ring will not come off.")
return false
end
end
-- a pressure plate that watches 3 tiles around it (watch_range = 3 in its template)
plate = {}
function plate.on_mobile_near(serial, mobile, distance)
item.set_prop(serial, "last", mobile)
end
function plate.on_tooltip(serial, viewer)
return { "Armed", { cliloc = 1060658, args = "Charges\t3" } }
end
-- a pedestal that eats a gem dropped on it, once the gem is back in the pack
pedestal = {}
function pedestal.can_drop_on(serial, dropper, thing)
if item.get_prop(thing, "cursed") then
return false
end
end
function pedestal.on_dropped_on(serial, dropper, thing)
item.delete(thing)
end

Lo script agisce sul proprio oggetto con il modulo item, passando il suo seriale; user è il seriale del giocatore. Uno script di oggetto minimo, come è nato scripts/items/potion.lua della distribuzione; quello completo è descritto in Pozioni:

potion = {}
function potion.on_use(serial, user)
item.message(serial, user, "You drink the potion.")
item.consume(serial)
return true
end

Gli script distribuiti per porte, luci, cibo, teletrasporti, moongate, orologi e contenitori sono descritti in Script distribuiti; engraved_stone.lua è un esempio di can_pick_up che tiene un oggetto nelle mani del suo proprietario (vedi Pietre incise).

Gli script che ascoltano con events.on vengono avvisati quando un oggetto qualsiasi viene creato o eliminato, con gli eventi item_created e item_deleted: non costano nulla finché nessuno ascolta, e sono nella tabella degli eventi sul bus.

Creato con ❤️ da Tom

Offrimi un caffè