EGGOXdevelopers

Costumes, combat feedback and interactive readouts

An experience can temporarily dress a player using native artwork in that room (visible placements or hidden stock).

An experience can temporarily dress a player using native artwork in that room (visible placements or hidden stock). Use a placement id, an asset's name, or its thing table. Keep the costume assets in every room that uses them.

costume(player, {character="Ninja", body="White Belt", hand="Katana", companion="Fox"}) sets the visual layer. Slots are character, head, body, legs, hand and companion. costume(player, nil) removes it. Each asset keeps its placement's filter and selected version. Matching wearable and character grids compose frame for frame, exactly as in the wardrobe. The player's own hat stays unless the creator supplies head; other outfit slots come from the costume.

The player's inventory and real equipped items never change. Hide costume restores their own appearance and suppresses scripted visual effects and command animations; gameplay continues. Keep earned rank and equipped game items in saved script data, so cosmetic visibility never becomes an access check. Leaving the experience restores the real wardrobe automatically. A companion is a small following native character asset, visible while the costume is shown.

effect(player, spec) broadcasts short feedback near a thing or player:

  • kind: hit, burst or rise.
  • style: plain (default) or comic, with outlined lettering and a jagged burst.
  • target: optional visible thing/player id or table; defaults to the actor.
  • animation: optional command clip on the character, such as strike.
  • text: optional floating text, at most 40 characters.
  • color: #rrggbb.

Clients bound simultaneous effects and skip distant remote effects. This is presentation only. Validate range, cooldowns, damage and rewards in the script. An authored command turns the character toward its target and finishes before the next command starts. Repeated effects retain only the latest pending command, so rapid input cannot continually restart a windup or build an animation backlog. The associated visual impact plays midway through the clip. Author anticipation, contact and recovery around that timing. Events without a matching clip play their effect immediately. Costume changes and departures clear pending commands. burst feedback displays immediately even with an animation command, so a finishing cue or reward cannot be lost behind queued swings. Its character gesture still plays. A recent hit whose contact frame was skipped during loading is delivered once; stale combat after a background-tab wakeup is discarded.

These are temporary world-space visuals, not inventory items. Text can be a word, a computed damage number or another short value. Comic labels sit above the whole target, including multi-tile objects, while sparks appear on its body. Clients limit the room to 32 active effects (16 for remote effects) and two labels per target. Nearby players can see comic hits; local feedback gets label priority.

on("click", function(p, monster)
  effect(p, {target=monster, animation="strike", kind="hit",
    style="comic", text="SLASH!", color="#ffd27b"})
  -- Or show a number computed by your damage logic:
  -- effect(p, {target=monster, kind="rise", text="-" .. tostring(damage)})
end)

Animating placed things

Room scripts can call set_state(thing, name) to play a state authored in that native item's Behavior. Thing scripts retain the one-argument set_state(name). A room can only change its own visible or stock things. Names are 1–24 bytes; unknown names fall back to the item's initial state. Repeating a state restarts it, so throttle hit reactions if the current pose should finish first.

All depth columns follow one clock. A looping state repeats; a one-shot follows its authored onEnd state, or holds its final pose when there is no onEnd. For example, author hurt → idle, defeated with no successor, and recover → idle, then call:

local function flinch(monster) set_state(monster, "hurt") end
-- Call when your server-side HP reaches zero:
local function defeat(monster) set_state(monster, "defeated") end
-- Call later, from your respawn timer:
local function revive(monster) set_state(monster, "recover") end

The visual state and start time travel with the thing in snapshots and survive hide/show. Late joiners see the current pose. This does not change collision, inventory or HP, and it is independent of players hiding their costumes. Keep gameplay state in Lua and time the recovery before accepting another attack.

Interactive HUD

panel(player, spec) adds an interactive HUD alongside the existing panel forms:

on("ui", function(p, id)
  if id == "open_training" then
    panel(p, {
      title = "TRAINING",
      rows = {{"SPIRIT", load(p, "spirit") or 0}},
      meter = {label = "Guardian", value = 75, max = 100},
      actions = {{id = "strike", label = "STRIKE"}, {id = "shop", label = "UPGRADES"}}
    })
  end
end)

Up to six rows and six uniquely named buttons are allowed. Buttons fire ui, like window buttons. Meter values are non-negative with a positive maximum. HUDs replay after reconnect; touch controls retain usable tap targets.

Set layout="combat" for a compact bottom action dock above chat. Each action may name an icon: sword, forge, belt, map, or leaf. Keep rows brief (three small stats work well on a phone). With this layout, meter.target may be the placement id of a visible room object, such as guardian.id. Its health bar follows the complete object's lower edge while the camera moves or zooms. Hidden objects and placements in other rooms have no label. Costume visibility does not affect this readout. Omit combat layout to retain the original readout.

Card images resolve only to scenery or stock in the current room, never remote URLs or another player's inventory. The client loads visible cards through the bounded preview worker queue and preserves the artwork's filter and version. Locked cards remain visible with their requirements. disabled prevents normal clicks; your script must still check purchases and unlocks on the server.

now() returns server Unix time in seconds, with fractional precision. It can be saved for cooldowns or offline crop growth; it is not the client's clock.

This page as markdown: /api/costumes-combat-feedback-and-interactive-readouts.md