Skip to content

Changed Display from one sprite per cell to sprite stack per cell.#276

Open
marcojulian wants to merge 1 commit into
PrismRL:masterfrom
marcojulian:marcojulian/transparency-not-working
Open

Changed Display from one sprite per cell to sprite stack per cell.#276
marcojulian wants to merge 1 commit into
PrismRL:masterfrom
marcojulian:marcojulian/transparency-not-working

Conversation

@marcojulian
Copy link
Copy Markdown

@marcojulian marcojulian commented Jun 1, 2026

PR summary: Display now supports layered sprites per cell, preserving terrain beneath transparent actor/item sprites while keeping existing depth ordering.

Old behavior:

  • cell.char held single tile.
  • Floor drawn first, actor drawn later.
  • Actor replaced floor tile.
  • Transparent actor pixels showed display clear color, not floor.

New behavior:

  • Each cell has sprites = { ... }.
  • put() appends sprite with depth/layer.
  • draw() sorts cell sprites by depth and draws all.
  • Transparent pixels now reveal lower sprite, usually floor.
  • Background color got separate bgDepth, so sprite depth and bg depth no longer fight.
  • clear() empties sprite stack each frame.

Before:
before

After:
after

@MatthewBlanchard
Copy link
Copy Markdown
Member

Hey! I really appreciate the PR and this is a really common request, so all great there. I'm thinking more about where this fits into the broader library.

I think it's kind of nice that the default display is dead simple, but I'd be totally willing to include this in spectrum as well as say StackedDisplay or somesuch. I'll do a deeper dive and give more feedback tomorrow or the next day hopefully.

Thanks so much for your contribution!

@MatthewBlanchard
Copy link
Copy Markdown
Member

Also any idea how this plays with Animations?

@marcojulian
Copy link
Copy Markdown
Author

Of course! Please keep in mind that this is a late Sunday night solution so I know there could be room for improvement before merging.

I didn't test it with animations since I haven't implemented any in my game yet. But I could add a couple and let you know how it goes!

@MatthewBlanchard
Copy link
Copy Markdown
Member

Discussed it in the discord with @fingoltin he's open to making this the default.

@marcojulian
Copy link
Copy Markdown
Author

marcojulian commented Jun 1, 2026

Seems to be working for animations as it is 🙌

Animation attached to actor:

local firepitlit1 = { index = 478 }
local firepitlit2 = { index = 479 }
local firepitlit3 = { index = 480 }
local firepitlit4 = { index = 481 }
local firepitlit5 = { index = 482 }
local firepitlit6 = { index = 483 }

spectrum.registerAnimation("FirePitLit", function()
   return spectrum.Animation({ firepitlit1, firepitlit2, firepitlit3, firepitlit4, firepitlit5, firepitlit6 }, 0.2)
end)
prism.registerActor("FirePit", function()
   return prism.Actor.fromComponents {
      prism.components.Name("Fire Pit"),
      prism.components.Position(),
      prism.components.Drawable { index = 478 },
      prism.components.IdleAnimation("FirePitLit"),
      prism.components.Collider(),
      prism.components.Remembered(),
   }
end)
fire lit

Animation around on action:

local hidden = { index = 1, layer = math.huge }
local visible = { index = 506, layer = math.huge }

local function putAround(sprite)
   return function(display, x, y)
      display:putDrawable(x + 1, y, sprite)
      display:putDrawable(x - 1, y, sprite)
      display:putDrawable(x, y + 1, sprite)
      display:putDrawable(x, y - 1, sprite)
   end
end

spectrum.registerAnimation("PlayerWait", function()
   return spectrum.Animation(
      { putAround(hidden), putAround(visible), putAround(hidden), putAround(visible) },
      0.08,
      "pauseAtEnd"
   )
end)
--- @class Wait : Action
--- @overload fun(owner: Actor): Wait
local Wait = prism.Action:extend("Wait")

function Wait:canPerform()
   return true
end

function Wait:perform(level)
   if not self.owner:has(prism.components.PlayerController) then return end

   level:yield(prism.messages.AnimationMessage {
      animation = spectrum.animations.PlayerWait(),
      actor = self.owner,
      blocking = true,
   })
end

return Wait
animation

Same cell animation on action:

local hidden = { index = 1 }
local visible = { index = 506 }

spectrum.registerAnimation("PlayerWait", function()
   return spectrum.Animation({ visible, hidden, visible, hidden, visible }, 0.2, "pauseAtEnd")
end)

With same Wait action code as in the prev example:
ezgif-42c70ad4dc7b4d44
and with override = true:
ezgif-4b11e2b8ef578df7

@fingoltin
Copy link
Copy Markdown
Member

After testing it out we do need a way to enable the current behaviour. Maybe we try subclassing?

Comment thread spectrum/display.lua Outdated
@marcojulian marcojulian force-pushed the marcojulian/transparency-not-working branch from e1bdf33 to 8d0c620 Compare June 4, 2026 21:53
@marcojulian
Copy link
Copy Markdown
Author

✔️ I repeated the same manual tests that I shared above after replacing Display with StackedDisplay in my own main.lua

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants