Gump
I gump sono le finestre di dialogo aperte dal server su un giocatore: un layout di sfondi, testi, pulsanti, caselle di selezione e campi di testo, con la risposta del giocatore che torna al server.
Un gump è composto da due file:
templates/gumps/<id>.xml: il layout, verificato rispetto atemplates/gumps/gump.xsd;scripts/gumps/<id>.lua: ciò che accade quando il giocatore risponde o lo chiude.
Le parti che cambiano con i dati provengono dallo script: uno <slot> nell’XML, oppure un intero gump
costruito in Lua. Inizi con i gump? Segui Il tuo primo gump.
Il layout
Section titled “Il layout”<?xml version="1.0" encoding="utf-8"?><gump id="release_pet" x="100" y="100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="gump.xsd"> <background x="0" y="0" gump="5054" width="270" height="140" /> <html x="20" y="15" width="230" height="40" cliloc="1070722" args="${pet_name}" /> <text x="20" y="60" hue="1152">Release ${pet_name}?</text> <button x="20" y="100" up="4005" down="4007" on_click="release" /> <html x="55" y="100" width="75" height="20" cliloc="1011011" /> <button x="135" y="100" up="4005" down="4007" on_click="cancel" /> <html x="170" y="100" width="75" height="20" cliloc="1011012" /></gump>Con xsi:noNamespaceSchemaLocation="gump.xsd", VS Code (con un’estensione XML) e Rider
completano elementi e attributi e segnalano gli errori mentre scrivi. Il server controlla ogni file
rispetto allo stesso schema all’avvio: un errore lo arresta indicando file, riga e motivo.
<gump> accetta id (minuscolo; dovrebbe corrispondere al nome file, e lo script viene trovato tramite
ID, non tramite nome file), x e y, e closable,
movable, disposable e resizable (true salvo impostazione a false), e master, l’ID di un gump master da cui
si prende la cornice. I suoi controlli appaiono su ogni pagina;
i controlli dentro ciascuna <page> appaiono sulle pagine 1, 2, … in ordine.
| Elemento | Mostra |
|---|---|
background |
Uno sfondo ridimensionabile (gump, width, height) |
alpha_region |
Una regione trasparente |
image, image_tiled |
Un’immagine gump (gump, anche image accetta un hue facoltativo), oppure ripetuta in un riquadro (image_tiled, senza hue) |
item |
Una grafica di oggetto (item, hue facoltativo) |
sprite |
Un’immagine gump ritagliata in un riquadro (gump, width, height; source_x e source_y dicono da dove parte il ritaglio) |
portrait |
L’immagine di una creatura, dal suo body (body, hue facoltativo); vedi Ritratti delle creature |
text |
Una riga di testo (hue, message), oppure un messaggio client in un riquadro (cliloc, args, color, con width e height) |
label_cropped |
Testo tagliato entro un riquadro (hue, message), oppure un messaggio client (cliloc, args, color) |
html |
Testo HTML in un riquadro (background, scrollbar, message, color facoltativo), oppure un messaggio client (cliloc, color, args) |
button |
Un pulsante con le sue due immagini (up, down, entrambe obbligatorie): on_click (una funzione dello script), id (per on_button), page (cambia pagina) oppure open (apre un altro gump) |
button_item |
Un pulsante con la grafica di un oggetto sopra le sue immagini (up, down, item, hue, width, height), che risponde come button |
checkbox |
Una casella di selezione con le due immagini (off, on, entrambe obbligatorie; ID switch, checked, bind) |
group con radio |
Pulsanti di opzione di cui uno può essere attivo (off, on, entrambi obbligatori; switch, checked, bind) |
text_entry |
Un campo di testo (ID entry, hue, max_length fino a 239, bind, literal; il testo interno è quello iniziale, con ${name} riempito e gli spazi compressi salvo literal="true", che tiene com’è un testo scritto da qualcuno) |
tooltip |
Il tooltip del controllo precedente (cliloc, args) |
item_property |
Il tooltip di un oggetto reale (serial) |
slot |
Dove lo script aggiunge controlli all’apertura del gump (name, x, y); vedi Slot |
Un testo proviene da una di tre fonti:
| Fonte | Come | Tradotto da |
|---|---|---|
| L’elemento | <text ...>Hello ${name}</text> |
Nessuno |
| Un messaggio server | message="30083" su text, label_cropped, html |
Il server, da data/messages, nella lingua del server |
| Un messaggio client | cliloc="1011011" su html, text, label_cropped e tooltip |
Il client, nella lingua del giocatore |
Un text o label_cropped con un cliloc viene disegnato come un riquadro (un text richiede width e height)
e accetta color, un colore a 15 bit, mai hue. Usa messaggi client per i testi standard già presenti nel client (CONTINUE 1011011,
CANCEL 1011012, …) e messaggi server per i tuoi testi, traducibili dallo shard. Entrambi in
un gump possono mescolare due lingue finché non esistono lingue per giocatore.
Cosa rifiuta il server
Section titled “Cosa rifiuta il server”Oltre a ciò che indica lo schema, il server si arresta all’avvio, indicando file e riga, per:
- un
buttonobutton_itemsenza esattamente uno traon_click,id,pageoopen; - un
htmlcon siaclilocsiamessage, oppure concliloce testo proprio; - un
textcon uncliloce senzawidtheheight, oppure untextolabel_croppedcon uncliloce unhue, unmessageo un testo proprio; - un
text,label_croppedohtmlcon siamessagesia testo proprio; - lo stesso
iddi pulsante, lo stessoswitch(checkbox e radio insieme) o lo stessoentrydue volte in un gump; - un pulsante
pageche passa a una pagina assente nel gump (una pagina da segnaposto si conosce solo all’apertura, e una che il gump non ha dà allora un pulsante che non fa nulla); - uno
<slot>in un gump con elementi<page>; - un
openche indica un gump inesistente; - lo stesso
iddi gump in due file.
gump.send verifica un gump costruito in Lua con le stesse regole.
Segnaposto
Section titled “Segnaposto”${name} in un testo o numero viene riempito dagli argomenti con cui si apre il gump; uno
mancante è vuoto. Gli ID switch e entry, e max_length, accettano solo numeri semplici, così
lo schema può verificarli; anche id e page di un pulsante accettano un segnaposto (un id di pulsante che non è un
numero da 1, o che ha un altro pulsante, non viene disegnato, con un avviso nel log). args (separati da tabulazioni) riempie i marcatori ~1_NAME~ di un messaggio client. @, { e }
vengono rimossi dagli argomenti dei messaggi client, così il nome di un giocatore non può compromettere il layout.
Gump in sequenza
Section titled “Gump in sequenza”Un gump può condurre a un altro, con gli stessi argomenti, come una procedura guidata:
bind="name"su untext_entry,checkboxoradioscrive la risposta nell’argomentonamequando il giocatore risponde: il testo,true/false, oppure loswitchdel radio attivo (falsequando nessuno del gruppo è attivo);open="other_gump"su un pulsante apre quel gump con gli argomenti, inclusi i valori associati.
<text_entry x="20" y="40" width="200" height="20" entry="1" bind="name">${name}</text_entry><button x="20" y="80" up="4005" down="4007" open="step2" />step2 può mostrare ${name}, e ogni callback di entrambi i gump trova args.name. Il server
verifica all’avvio che il gump indicato da un open esista. Uno script decide autonomamente dove andare con
gump.open(player, "step2", args).
Ritratti delle creature
Section titled “Ritratti delle creature”Un gump non può disegnare un mobile. <portrait x="20" y="20" body="${body}" /> disegna la grafica di un oggetto che
somiglia alla creatura, quello che data/portraits.toml indica per quel body; un body
senza voce lascia il posto vuoto, con un avviso nel log. gump.portrait(body) dà quell’ID oggetto
a uno script, e g:portrait{ x = 20, y = 20, body = 226 } aggiunge l’immagine a un gump costruito.
<slot name="rows" x="20" y="45" /> (numeri semplici, nessun segnaposto) viene riempito all’apertura del gump: il server chiama la funzione
rows dello script del gump con un builder, rows(g, player, args), e colloca ciò che aggiunge nello
slot, con coordinate relative allo slot. Le pagine create dalla funzione (g:page(), g:paginate) vengono
aggiunte come pagine del gump, quindi uno slot non può stare in un gump con elementi <page>: il server lo rifiuta
all’avvio. Una funzione slot mancante lascia lo slot vuoto con un avviso; una che fallisce o chiama
wait() impedisce l’apertura del gump. Le aggiunte dello slot vengono verificate come un file di gump. Solo
gump.open da uno script riempie gli slot: un gump aperto da C# li lascia vuoti, con
un avviso nel log.
Gump costruiti in Lua
Section titled “Gump costruiti in Lua”gump.create(id, x, y) fornisce un builder i cui metodi aggiungono i controlli degli elementi XML con lo
stesso nome e gli stessi attributi; gump.send(player, g, args) lo apre:
local g = gump.create("pet_list", 100, 100)g:background{ x = 0, y = 0, gump = 9200, width = 300, height = 300 }g:text{ x = 20, y = 15, hue = 1152, text = "Your pets" }g:pager{ previous = { x = 20, y = 260 }, next = { x = 250, y = 260 } }
for i, pet in ipairs(pets) do local row = g:paginate(i, 10) g:button{ x = 20, y = 45 + row * 22, up = 4005, down = 4007, on_click = function(player, response, args) npc.say(pet.serial, "*follows*") end } g:text{ x = 55, y = 45 + row * 22, text = pet.name }end
gump.send(player, g, {})| Metodo | Aggiunge |
|---|---|
g:background{}, g:alpha_region{}, g:image{}, g:image_tiled{}, g:item{}, g:sprite{}, g:portrait{} |
L’elemento con lo stesso nome |
g:text{}, g:label_cropped{}, g:html{}, g:text_entry{} |
L’elemento, con text = "..." come testo |
g:button{}, g:button_item{}, g:checkbox{}, g:radio{}, g:tooltip{}, g:item_property{} |
L’elemento; on_click può essere una funzione |
g:group() |
Un gruppo radio: i radio successivi vi appartengono |
g:page() |
Una nuova pagina: ciò che segue appare su di essa |
g:pager{ previous = {...}, next = {...} } |
Dove g:paginate colloca i pulsanti (x, y, up, down); chiamalo prima di g:paginate |
g:paginate(index, per_page, columns) |
Una nuova pagina ogni per_page oggetti, con pulsanti tra pagine; restituisce la riga dell’oggetto nella pagina, da 0, e con columns anche la colonna |
g:paginate legge il pager quando viene eseguito, quindi g:pager viene prima. In sua assenza, o per un valore
omesso, il pulsante successivo è a (260, 340) con immagini 4005 e 4007, e il precedente
a (20, 340) con 4014 e 4015.
Ogni metodo tranne g:paginate restituisce g, quindi le chiamate possono essere concatenate. Un pulsante il cui on_click è una
funzione la chiama con (player, response, args); la funzione vede le variabili circostanti, come
pet sopra, e appartiene allo script che ha inviato il gump. gump.send verifica il gump come un
file e fallisce indicando il motivo, come un checked che non è true o false. Un gump
costruito risponde come uno XML: testi, bind, open e on_close sono gli stessi. I segnaposto vengono
sottoposti a escape in html; il testo concatenato da te, come "Hi " .. name, no, quindi scrivi
text = "Hi ${name}" e passa name negli argomenti. L’escape copre &, <, > e ", così un
segnaposto in un attributo tra virgolette, come href="${link}", non può uscirne.
I nomi on_click che iniziano con __ sono riservati al server.
Lo script
Section titled “Lo script”-- scripts/gumps/release_pet.luarelease_pet = {}
-- An on_click button: the player, the answer and the arguments the gump was opened with.function release_pet.release(player, response, args) npc.say(args.pet, "*leaves*")end
function release_pet.cancel(player, response, args) end
-- Buttons with an id instead of on_click.function release_pet.on_button(player, button, response, args) end
-- The gump went away without a button: "player" (closed by the player), "replaced" (opened again),-- "server" (gump.close, or too many gumps open) or "disconnect".function release_pet.on_close(player, args, reason) endresponse è { button = n, switches = { [id] = true }, text = { [id] = "..." } }: gli switch
attivi e il testo di ogni campo, già verificati dal server.
Apri, chiudi e interroga un gump da qualsiasi script:
gump.open(player, "release_pet", { pet_name = "Fido", pet = serial }) -- false for an unknown gumpgump.close(player, "release_pet")gump.is_open(player, "release_pet") -- true while the player has it opengump.close_all(player) -- every gump of the player; gives how many were openDa uno script, gump.open, gump.send, gump.close e gump.close_all eseguono il lavoro al turno successivo
del game loop, quindi un true significa in coda: un gump che poi non si apre resta solo nel log. close_all
restituisce quanti erano aperti al momento della chiamata.
In gioco, .gump release_pet pet_name=Fido lo apre su di te per provarlo.
Gli argomenti accettano stringhe, numeri e booleani e ritornano a ogni callback.
Sicurezza
Section titled “Sicurezza”Il server conserva ogni gump aperto su ciascun giocatore e verifica ogni risposta:
- una risposta a un gump non inviato al giocatore, o a cui ha già risposto, viene scartata;
- una risposta con un pulsante, switch o campo di testo assente nel gump, oppure lo stesso campo di testo due volte, viene scartata;
- un testo più lungo di 239 caratteri viene scartato (lo schema mantiene
max_lengtha 239 o meno); - aprire un gump con lo stesso ID chiude quello già aperto, e un giocatore mantiene al massimo 64 gump.
Una risposta contraffatta non raggiunge mai uno script. I client da 5.0.0a ricevono il pacchetto compresso (0xDD), i precedenti quello semplice (0xB0); vedi la guida di riferimento dei pacchetti.
Un plugin apre i gump di templates/gumps con IGumpTemplateService:
// On the game loop, with a callback:templates.Open(session, "release_pet", new Dictionary<string, string> { ["pet_name"] = "Fido" }, (session, answer) => { /* answer.Click is the on_click name, answer.Response the answer */ });
// Off the loop, waiting for the on_click name (null when closed, or for an id button):var choice = await templates.AskAsync(session, "decorate_confirm", new Dictionary<string, string>());.decorate chiede in questo modo con templates/gumps/decorate_confirm.xml; gli altri gump forniti sono
templates/gumps/go.xml, il menu di viaggio di .go, templates/gumps/jail_sentence.xml,
il gump di .jail, e templates/gumps/gmtools.xml, gli strumenti di
.gmtools: un gump con due slot, barra laterale e pannello, e templates/gumps/items.xml, la
ricerca degli oggetti di .items: un gump con uno slot che contiene una casella di ricerca, un elenco
con pagine e i pulsanti che danno l’oggetto, e templates/gumps/npcs.xml, la ricerca degli NPC di .npcs,
costruita allo stesso modo con l’immagine del body e un pulsante che crea.
La cartella fornisce ventidue gump in tutto. Gli altri sono craft_menu.xml (il menu di creazione), guild_menu.xml (la gilda di una
pietra di gilda), help_menu.xml, help_page_kind.xml, pages.xml e pages_detail.xml (il menu di aiuto e la coda
delle pagine GM), pet_release.xml, stable_claim.xml, report_murder.xml, resurrect.xml, polymorph_forms.xml (le forme
che offre Polymorph) e i tre tutorial_*.xml (saluto, elenco, nome).
La risposta contiene anche answer.Open, il gump indicato da un pulsante open (null per qualsiasi altro
pulsante), e answer.Bound, i valori dei controlli con bind per nome. AskAsync segue
autonomamente i pulsanti open, aggiungendo i valori associati agli argomenti; con Open la callback deve
aprire il gump successivo. Open restituisce false quando la sessione è terminata. Uno <slot> resta vuoto in
un gump aperto da C#: solo gump.open da uno script chiama le funzioni degli slot.
Un layout costruito nel codice passa da IGumpService, con una voce per comando:
var layout = new GumpLayout() .Add(new GumpBackground { X = 0, Y = 0, GumpId = 5054, Width = 270, Height = 120 }) .Add(new GumpButton { X = 20, Y = 80, Up = 4005, Down = 4007, ButtonId = 1 });
gumps.Open(session, new GumpInstance{ Id = "my_gump", Layout = layout, X = 100, Y = 100, OnResponse = (session, response) => { /* response.ButtonId, Switches, Texts */ }, OnClosed = (session, reason) => { /* replaced, server or disconnect */ }});Le voci sono GumpPage, GumpGroup, GumpBackground, GumpAlphaRegion, GumpImage,
GumpImageTiled, GumpItem, GumpButton, GumpCheckbox, GumpRadio, GumpText,
GumpLabelCropped, GumpHtml, GumpHtmlLocalized, GumpTextEntry, GumpTooltip,
GumpItemProperty e GumpFlag.
Lettere leggibili
Section titled “Lettere leggibili”La pergamena dei documenti mostra testo semplice salvato con un corpo scorrevole. Una lettera idonea nello zaino del lettore aggiunge un pulsante localizzato Ritira allegati. Il piè di pagina riduce l’altezza del corpo per mantenerlo all’interno della pergamena. La chiusura è di sola lettura. Le risposte seguono gli stessi controlli di seriale, tipo e pulsante offerto degli altri gump; proprietà e sessione originale vengono ricontrollate prima del ritiro. Un ritiro riuscito riapre lo stesso testo senza pulsante; un rifiuto per capienza lo riapre per un nuovo tentativo.
Creato con ❤️ da Tom
