Skip to content

Questions and forms

A quick question

sagl.ui.messageBox resolves to the button's label, or null if the player pressed Esc.

The message box

sagl.on("spray:offer", async ({ price }) => {
  const answer = await sagl.ui.messageBox({
    title: "Pay 'n' Spray",
    text: `Respray your car for $${price}? It'll lose two wanted stars.`,
    buttons: ["No", "Yes"],
  });
  sagl.emit("spray:answer", answer === "Yes");
});

A form

A centred document with text inputs, a checkbox and buttons; it takes the mouse and keyboard while open, and Esc closes it.

client/scripts/register.js
sagl.on("register", ({ reason }) => {
  const form = sagl.ui.open(`
    <rml>
    <head>
      <link type="text/rcss" href="/sagl/ui/theme/base.rcss"/>
      <style>
        body { width: 100%; height: 100%; background-color: #00000080; }
        .panel { position: absolute; left: 50%; top: 45%; width: 420dp; margin-left: -210dp;
                 transform: translateY(-50%); }
        label { display: block; color: #accbf1; margin-bottom: 4dp; }
        .terms { display: flex; flex-direction: row; align-items: center; margin-bottom: 14dp; }
        .terms span { margin-left: 8dp; }
        input.checkbox { width: 18dp; height: 18dp; border: 2dp #accbf1; background-color: #000000a0;
                         tab-index: auto; nav: auto; }
        input.checkbox:checked { background-color: #accbf1; }
        #error { display: block; color: #ff6060; margin-bottom: 10dp; }
      </style>
    </head>
    <body>
      <div class="panel">
        <h1>Sign up</h1>
        <span class="subtitle" id="reason"></span>
        <label>Character name</label>
        <input class="text" type="text" id="name" maxlength="24"/>
        <label>Age</label>
        <input class="text" type="text" id="age" maxlength="2"/>
        <div class="terms"><input type="checkbox" id="rules"/><span>I've read the rules</span></div>
        <span id="error"></span>
        <button id="cancel">Cancel</button><button class="primary" id="ok">Sign up</button>
      </div>
    </body>
    </rml>`);

  form.setText("reason", reason ?? "");
  form.focus("name");

  form.on("click", "cancel", () => form.close());
  form.on("click", "ok", () => {
    const name = form.getValue("name").trim();
    const age = Number(form.getValue("age"));
    if (!/^[A-Za-z]+ [A-Za-z]+$/.test(name)) return form.setText("error", "First and last name, please.");
    if (!(age >= 18 && age <= 90)) return form.setText("error", "Ages are 18-90.");
    if (!form.isChecked("rules")) return form.setText("error", "Read the rules first.");
    sagl.emit("register", { name, age });
    form.close();
  });
});
index.ts (gamemode)
server.on("playerJoin", (p) => p.emit("register", { reason: "Welcome! Make your character." }));
server.channel("register").on((player, data) => {
  const { name, age } = data as { name: string; age: number };
  player.data.character = { name, age };      // check it again here: never trust the client
});