From 9c3bc06fcd56121dc4fbc4cbbc4c07ca7cb499ad Mon Sep 17 00:00:00 2001 From: Cruor Date: Wed, 8 Sep 2021 12:26:07 +0200 Subject: [PATCH] Added Cassette Block, Playback Billboard and Star Jump Block entities --- src/entities/cassette_block.lua | 152 ++++++++++++++++++++ src/entities/playback_billboard.lua | 131 ++++++++++++++++++ src/entities/star_jump_block.lua | 208 ++++++++++++++++++++++++++++ src/lang/en_gb.lang | 10 +- 4 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 src/entities/cassette_block.lua create mode 100644 src/entities/playback_billboard.lua create mode 100644 src/entities/star_jump_block.lua diff --git a/src/entities/cassette_block.lua b/src/entities/cassette_block.lua new file mode 100644 index 00000000..64118c1f --- /dev/null +++ b/src/entities/cassette_block.lua @@ -0,0 +1,152 @@ +local fakeTilesHelper = require("fake_tiles_helper") +local utils = require("utils") +local matrixLib = require("matrix") +local drawableSprite = require("structs.drawable_sprite") +local connectedEntities = require("helpers.connected_entities") + +local cassetteBlock = {} + +local colors = { + {73 / 255, 170 / 255, 240 / 255}, + {240 / 255, 73 / 255, 190 / 255}, + {252 / 255, 220 / 255, 58 / 255}, + {56 / 255, 224 / 255, 78 / 255}, +} + +local frames = { + "objects/cassetteblock/solid", + "objects/cassetteblock/solid", + "objects/cassetteblock/solid", + "objects/cassetteblock/solid" +} + +local depths = { + -10, + -10, + -10, + -10 +} + +cassetteBlock.name = "cassetteBlock" +cassetteBlock.minimumSize = {16, 16} +cassetteBlock.placements = {} + +for i, _ in ipairs(colors) do + cassetteBlock.placements[i] = { + name = string.format("cassette_block_%s", i - 1), + data = { + index = i - 1, + tempo = 1.0, + width = 8, + height = 8 + } + } +end + +-- Filter by cassette blocks sharing the same index +local function getSearchPredicate(entity) + return function(target) + return entity._name == target._name and entity.index == target.index + end +end + +local function getTileSprite(entity, x, y, frame, color, depth, rectangles) + local hasAdjacent = connectedEntities.hasAdjacent + + local drawX, drawY = (x - 1) * 8, (y - 1) * 8 + + local closedLeft = hasAdjacent(entity, drawX - 8, drawY, rectangles) + local closedRight = hasAdjacent(entity, drawX + 8, drawY, rectangles) + local closedUp = hasAdjacent(entity, drawX, drawY - 8, rectangles) + local closedDown = hasAdjacent(entity, drawX, drawY + 8, rectangles) + local completelyClosed = closedLeft and closedRight and closedUp and closedDown + + local quadX, quadY = false, false + + if completelyClosed then + if not hasAdjacent(entity, drawX + 8, drawY - 8, rectangles) then + quadX, quadY = 24, 0 + + elseif not hasAdjacent(entity, drawX - 8, drawY - 8, rectangles) then + quadX, quadY = 24, 8 + + elseif not hasAdjacent(entity, drawX + 8, drawY + 8, rectangles) then + quadX, quadY = 24, 16 + + elseif not hasAdjacent(entity, drawX - 8, drawY + 8, rectangles) then + quadX, quadY = 24, 24 + + else + quadX, quadY = 8, 8 + end + else + if closedLeft and closedRight and not closedUp and closedDown then + quadX, quadY = 8, 0 + + elseif closedLeft and closedRight and closedUp and not closedDown then + quadX, quadY = 8, 16 + + elseif closedLeft and not closedRight and closedUp and closedDown then + quadX, quadY = 16, 8 + + elseif not closedLeft and closedRight and closedUp and closedDown then + quadX, quadY = 0, 8 + + elseif closedLeft and not closedRight and not closedUp and closedDown then + quadX, quadY = 16, 0 + + elseif not closedLeft and closedRight and not closedUp and closedDown then + quadX, quadY = 0, 0 + + elseif not closedLeft and closedRight and closedUp and not closedDown then + quadX, quadY = 0, 16 + + elseif closedLeft and not closedRight and closedUp and not closedDown then + quadX, quadY = 16, 16 + end + end + + if quadX and quadY then + local sprite = drawableSprite.fromTexture(frame, entity) + + sprite:addPosition(drawX, drawY) + sprite:useRelativeQuad(quadX, quadY, 8, 8) + sprite:setColor(color) + + sprite.depth = depth + + return sprite + end +end + +function cassetteBlock.sprite(room, entity) + local relevantBlocks = utils.filter(getSearchPredicate(entity), room.entities) + + connectedEntities.appendIfMissing(relevantBlocks, entity) + + local rectangles = connectedEntities.getEntityRectangles(relevantBlocks) + + local sprites = {} + + local width, height = entity.width or 32, entity.height or 32 + local tileWidth, tileHeight = math.ceil(width / 8), math.ceil(height / 8) + + local index = entity.index or 0 + local color = colors[index + 1] or colors[1] + local frame = frames[index + 1] or frames[1] + local depth = depths[index + 1] or depths[1] + + for x = 1, tileWidth do + for y = 1, tileHeight do + local sprite = getTileSprite(entity, x, y, frame, color, depth, rectangles) + + if sprite then + table.insert(sprites, sprite) + end + end + end + + return sprites +end + +return cassetteBlock \ No newline at end of file diff --git a/src/entities/playback_billboard.lua b/src/entities/playback_billboard.lua new file mode 100644 index 00000000..ca7fab3d --- /dev/null +++ b/src/entities/playback_billboard.lua @@ -0,0 +1,131 @@ +local utils = require("utils") +local drawableSprite = require("structs.drawable_sprite") +local drawableRectangle = require("structs.drawable_rectangle") +local connectedEntities = require("helpers.connected_entities") + +local playbackBillboard = {} + +playbackBillboard.name = "playbackBillboard" +playbackBillboard.depth = 9010 +playbackBillboard.minimumSize = {8, 8} +playbackBillboard.placements = { + name = "playback_billboard", + data = { + width = 8, + height = 8 + } +} + +local fillColor = {0.17, 0.14, 0.33} +local borderTexture = "scenery/tvSlices" + +local function getSearchPredicate(entity) + return function(target) + return entity._name == target._name + end +end + +local function empty(entity, x, y, rectangles) + return not connectedEntities.hasAdjacent(entity, x * 8, y * 8, rectangles) +end + +local function getTileSprite(entity, x, y, texture, rectangles) + if empty(entity, x, y, rectangles) then + local quadX, quadY = false, false + + local centerLeft = not empty(entity, x - 1, y, rectangles) + local centerRight = not empty(entity, x + 1, y, rectangles) + local topCenter = not empty(entity, x, y - 1, rectangles) + local bottomCenter = not empty(entity, x, y + 1, rectangles) + local topLeft = not empty(entity, x - 1, y - 1, rectangles) + local topRight = not empty(entity, x + 1, y - 1, rectangles) + local bottomLeft = not empty(entity, x - 1, y + 1, rectangles) + local bottomRight = not empty(entity, x + 1, y + 1, rectangles) + + if not centerRight and not bottomCenter and bottomRight then + quadX, quadY = 0, 0 + + elseif not centerLeft and not bottomCenter and bottomLeft then + quadX, quadY = 16, 0 + + elseif not topCenter and not centerRight and topRight then + quadX, quadY = 0, 16 + + elseif not topCenter and not centerLeft and topLeft then + quadX, quadY = 16, 16 + + elseif centerRight and bottomCenter then + quadX, quadY = 24, 0 + + elseif centerLeft and bottomCenter then + quadX, quadY = 32, 0 + + elseif centerRight and topCenter then + quadX, quadY = 24, 16 + + elseif centerLeft and topCenter then + quadX, quadY = 32, 16 + + elseif bottomCenter then + quadX, quadY = 8, 0 + + elseif centerRight then + quadX, quadY = 0, 8 + + elseif centerLeft then + quadX, quadY = 16, 8 + + elseif topCenter then + quadX, quadY = 8, 16 + end + + if quadX and quadY then + local sprite = drawableSprite.fromTexture(texture, entity) + + sprite:addPosition(x * 8, y * 8) + sprite:useRelativeQuad(quadX, quadY, 8, 8, nil, true) + + return sprite + end + end +end + +local function addTileSprite(sprites, entity, x, y, texture, rectangles) + local sprite = getTileSprite(entity, x, y, texture, rectangles) + + if sprite then + table.insert(sprites, sprite) + end +end + +function playbackBillboard.sprite(room, entity) + local relevantBlocks = utils.filter(getSearchPredicate(entity), room.entities) + + connectedEntities.appendIfMissing(relevantBlocks, entity) + + local rectangles = connectedEntities.getEntityRectangles(relevantBlocks) + + local sprites = {} + + local x, y = entity.x or 0, entity.y or 0 + local width, height = entity.width or 32, entity.height or 32 + local tileWidth, tileHeight = math.ceil(width / 8), math.ceil(height / 8) + + local backgroundRectangle = drawableRectangle.fromRectangle("fill", x, y, width, height, fillColor) + + table.insert(sprites, backgroundRectangle:getDrawableSprite()) + + for i = -1, tileWidth do + addTileSprite(sprites, entity, i, -1, borderTexture, rectangles) + addTileSprite(sprites, entity, i, tileHeight, borderTexture, rectangles) + end + + for j = 0, tileHeight - 1 do + addTileSprite(sprites, entity, -1, j, borderTexture, rectangles) + addTileSprite(sprites, entity, tileWidth, j, borderTexture, rectangles) + end + + return sprites +end + +return playbackBillboard \ No newline at end of file diff --git a/src/entities/star_jump_block.lua b/src/entities/star_jump_block.lua new file mode 100644 index 00000000..e7f65f76 --- /dev/null +++ b/src/entities/star_jump_block.lua @@ -0,0 +1,208 @@ +local utils = require("utils") +local drawableSprite = require("structs.drawable_sprite") +local drawableRectangle = require("structs.drawable_rectangle") +local connectedEntities = require("helpers.connected_entities") + +local starJumpBlock = {} + +starJumpBlock.name = "starJumpBlock" +starJumpBlock.depth = 9010 +starJumpBlock.minimumSize = {8, 8} +starJumpBlock.placements = { + name = "star_jump_block", + data = { + width = 8, + height = 8, + sinks = true + } +} + +local corners = { + "objects/starjumpBlock/corner00", + "objects/starjumpBlock/corner01", + "objects/starjumpBlock/corner02", + "objects/starjumpBlock/corner03" +} +local edgeHorizontals = { + "objects/starjumpBlock/edgeH00", + "objects/starjumpBlock/edgeH01", + "objects/starjumpBlock/edgeH02", + "objects/starjumpBlock/edgeH03" +} +local edgeVerticals = { + "objects/starjumpBlock/edgeV00", + "objects/starjumpBlock/edgeV01", + "objects/starjumpBlock/edgeV02", + "objects/starjumpBlock/edgeV03" +} +local leftRailings = { + "objects/starjumpBlock/leftrailing00", + "objects/starjumpBlock/leftrailing01", + "objects/starjumpBlock/leftrailing02", + "objects/starjumpBlock/leftrailing03", + "objects/starjumpBlock/leftrailing04", + "objects/starjumpBlock/leftrailing05", + "objects/starjumpBlock/leftrailing06" +} +local rightRailings = { + "objects/starjumpBlock/rightrailing00", + "objects/starjumpBlock/rightrailing01", + "objects/starjumpBlock/rightrailing02", + "objects/starjumpBlock/rightrailing03", + "objects/starjumpBlock/rightrailing04", + "objects/starjumpBlock/rightrailing05", + "objects/starjumpBlock/rightrailing06" +} +local railings = { + "objects/starjumpBlock/railing00", + "objects/starjumpBlock/railing01", + "objects/starjumpBlock/railing02", + "objects/starjumpBlock/railing03", + "objects/starjumpBlock/railing04", + "objects/starjumpBlock/railing05", + "objects/starjumpBlock/railing06" +} + +-- Filter by floaty space blocks sharing the same tiletype +local function getSearchPredicate(entity) + return function(target) + return entity._name == target._name + end +end + +local function empty(entity, x, y, rectangles) + return not connectedEntities.hasAdjacent(entity, x, y, rectangles) +end + +local function getRandomSprite(entity, textures, offsetX, offsetY, scaleX, scaleY) + local texture = textures[math.random(1, #textures)] + + if texture then + local sprite = drawableSprite.fromTexture(texture, entity) + + sprite:addPosition(offsetX, offsetY) + sprite:setScale(scaleX or 1, scaleY or 1) + + return sprite + end +end + +local function addRandomSprite(sprites, entity, textures, offsetX, offsetY, scaleX, scaleY) + local sprite = getRandomSprite(entity, textures, offsetX, offsetY, scaleX, scaleY) + + if sprite then + table.insert(sprites, sprite) + end +end + +local function getRailingSprite(entity, textures, offsetX, offsetY) + local texture = textures[utils.mod1(math.floor((entity.x + offsetX) / 8), #textures)] + + if texture then + local sprite = drawableSprite.fromTexture(texture, entity) + + sprite:addPosition(offsetX, offsetY) + sprite:setJustification(0.0, 0.0) + + return sprite + end +end + +local function addRailingSprite(sprites, entity, textures, offsetX, offsetY) + local sprite = getRailingSprite(entity, textures, offsetX, offsetY) + + if sprite then + table.insert(sprites, sprite) + end +end + +function starJumpBlock.sprite(room, entity) + local relevantBlocks = utils.filter(getSearchPredicate(entity), room.entities) + + connectedEntities.appendIfMissing(relevantBlocks, entity) + + local rectangles = connectedEntities.getEntityRectangles(relevantBlocks) + + local sprites = {} + + local x, y = entity.x or 0, entity.y or 0 + local width, height = entity.width or 16, entity.height or 16 + + utils.setSimpleCoordinateSeed(x, y) + + -- Horizontal Border + for w = 8, width - 16, 8 do + if empty(entity, w, -8, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, w + 4, 4, 1, 1) + addRailingSprite(sprites, entity, railings, w, -8) + end + + if empty(entity, w, height, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, w + 4, height - 4, 1, -1) + end + end + + -- Vertical Border + for h = 8, height - 16, 8 do + if empty(entity, -8, h, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, 4, h + 4, -1, 1) + end + + if empty(entity, width, h, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, width - 4, h + 4, 1, 1) + end + end + + -- Top Left Corner + if empty(entity, -8, 0, rectangles) and empty(entity, 0, -8, rectangles) then + addRandomSprite(sprites, entity, corners, 4, 4, -1, 1) + addRailingSprite(sprites, entity, leftRailings, 0, -8) + + elseif empty(entity, -8, 0, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, 4, 4, -1, 1) + + elseif empty(entity, 0, -8, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, 4, 4, -1, 1) + addRailingSprite(sprites, entity, railings, 0, -8) + end + + -- Top Right Corner + if empty(entity, width, 0, rectangles) and empty(entity, width - 8, -8, rectangles) then + addRandomSprite(sprites, entity, corners, width - 4, 4, 1, 1) + addRailingSprite(sprites, entity, rightRailings, width - 8, -8) + + elseif empty(entity, width, 0, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, width - 4, 4, 1, 1) + + elseif empty(entity, width - 8, -8, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, width - 4, 4, 1, 1) + addRailingSprite(sprites, entity, rightRailings, width - 8, -8) + + end + + -- Bottom Left Corner + if empty(entity, -8, height - 8, rectangles) and empty(entity, 0, height, rectangles) then + addRandomSprite(sprites, entity, corners, 4, height - 4, -1, -1) + + elseif empty(entity, -8, height - 8, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, 4, height - 4, -1, -1) + + elseif empty(entity, 0, height, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, 4, height - 4, -1, -1) + end + + -- Bottom Right Corner + if empty(entity, width, height - 8, rectangles) and empty(entity, width - 8, height, rectangles) then + addRandomSprite(sprites, entity, corners, width - 4, height - 4, 1, -1) + + elseif empty(entity, width, height - 8, rectangles) then + addRandomSprite(sprites, entity, edgeVerticals, width - 4, height - 4, 1, -1) + + elseif empty(entity, width - 8, height, rectangles) then + addRandomSprite(sprites, entity, edgeHorizontals, width - 4, height - 4, 1, -1) + end + + return sprites +end + +return starJumpBlock \ No newline at end of file diff --git a/src/lang/en_gb.lang b/src/lang/en_gb.lang index 47b432da..f20d503b 100644 --- a/src/lang/en_gb.lang +++ b/src/lang/en_gb.lang @@ -583,6 +583,10 @@ entities.exitBlock.description.playTransitionReveal=Determines whether revealing entities.cassette.name.cassette=Cassette # Cassette Block +entities.cassetteBlock.name.cassette_block_0=Cassette Block (0 - Blue) +entities.cassetteBlock.name.cassette_block_1=Cassette Block (1 - Rose) +entities.cassetteBlock.name.cassette_block_2=Cassette Block (2 - Bright Sun) +entities.cassetteBlock.name.cassette_block_3=Cassette Block (3 - Malachite) entities.cassetteBlock.description.index=The colour of the cassette block. entities.cassetteBlock.description.tempo=The tempo of the cassette blocks. The first non 1 tempo cassette block decides the tempo for all cassette blocks in the room. @@ -733,6 +737,7 @@ entities.everest/coreMessage.description.dialog=The dialogue key to use. entities.everest/coreMessage.description.outline=Whether the text has a black outline or not. # Star Jump Block +entities.starJumpBlock.name.star_jump_block=Star Jump Block entities.starJumpBlock.description.sinks=Whether the block sinks when the player stands on it. # Star Jump Controller @@ -788,10 +793,13 @@ entities.floatySpaceBlock.name.floaty_space_block=Floaty Space Block entities.floatySpaceBlock.description.tiletype=Determines the visual appearance of the wall. entities.floatySpaceBlock.description.disableSpawnOffset=Whether or not the entity should spawn without the random offset. -# Plyback Tutorial +# Playback Tutorial entities.playbackTutorial.name.playback=Ghost Player Playback entities.playbackTutorial.description.tutorial=Determines which tutorial ghost is played.\nFilename is relative to the `Tutorials` folder, and without the .bin extension. +# Playback Tutorial +entities.playbackBillboard.name.playback_billboard=Playback Billboard + # Crumble Wall On Rumble entities.crumbleWallOnRumble.name.crumble_wall=Crumble Wall On Rumble entities.crumbleWallOnRumble.description.blendin=Blends the block in with the walls it touches, making it less apparent.