From 2e6882c1843374b00942e964604302772476a4e8 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Sun, 16 Apr 2023 18:06:53 +0100 Subject: feat: add dialog functionality --- raccoon.lua | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 171 insertions(+), 14 deletions(-) diff --git a/raccoon.lua b/raccoon.lua index fac2f03..750c890 100644 --- a/raccoon.lua +++ b/raccoon.lua @@ -8,6 +8,7 @@ -- Add width/height to Pos component -- Extend to 4 action buttons -- Call math.randomseed (x) in an init function +-- Screen shake on hit --- Utility functions -- @section Utility functions @@ -413,6 +414,76 @@ function Metadata:init(name) return self end +-- Dialog width is 61 cols and 6 rows +Dialog = Component("Dialog") +function Dialog:init(content) + self.content_id = 1 + self.content = content + self.display = "" + return self +end +function Dialog.step(self) + if self.content[self.content_id] then + local cont = self.content[self.content_id] + if not cont.type or cont.type == "text" then + if not self.line_id then + self.line_id = 1 + self.char_id = 0 + self.display = "" + end + self.char_id = self.char_id + 1 + if self.char_id <= #cont[self.line_id] then + local c = string.sub(cont[self.line_id], self.char_id, self.char_id) + self.display = self.display .. c + else + self.line_id = self.line_id + 1 + if self.line_id <= #cont then + self.display = self.display .. "\n" + self.char_id = 0 + else + self.content_id = self.content_id + 1 + self.line_id = nil + end + end + elseif cont.type == "confirm" then + -- wait for user input + else + -- other types of content + end + return true + else + return false + end +end +function Dialog:yes() + if self.content[self.content_id] then + local cont = self.content[self.content_id] + if not cont.type or cont.type == "text" then + self.display = "" + for l = 1,#cont do + self.display = self.display .. cont[l] .. "\n" + end + self.content_id = self.content_id + 1 + self.line_id = nil + elseif cont.type == "confirm" then + self.content_id = self.content_id + 1 + else + -- other types of content + end + return true + else + return false + end +end +function Dialog:no() + -- Not implemented + return true +end +function Dialog:is_confirm() + return self.content[self.content_id] and + self.content[self.content_id].type == "confirm" +end + Action = Component("Action") function Action:init(a,b) self.a = a @@ -428,6 +499,30 @@ function Action.forcefield(entity,game) ForceField(2,10,ForceField.by_id(entity.id),true), CircleEffect(20)})) end +function Action.dialog(entity,game) + if game.status ~= 3 then + game.status = 3 + game.dialog = + Entity():with( + Dialog( + { + { + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed", + "do eiusmod tempor incididunt ut labore et dolore magna", + "aliqua. Ut enim ad minim veniam, quis nostrud exercitation", + "ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "Duis aute irure dolor in reprehenderit in voluptate velit", + "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" + }, + { type = "confirm" }, + { + "occaecat cupidatat non proident, sunt in culpa qui officia", + "deserunt mollit anim id est laborum." + }, + { type = "confirm" } + })) + end +end Target = Component("Target") function Target:init(pos, prox, sticky) @@ -632,12 +727,43 @@ function DrawingSystem:exec(entity,level) end end end +function DrawingSystem:dialog(d) + local origin = {1,9} + local size = {27,6} + -- Top/bottom + for i = origin[1]+1,origin[1]+size[1]-1 do + spr(18, i*8, origin[2]*8, 0) + spr(18, i*8, (origin[2]+size[2])*8, 0, 1, 2) + end + -- Left/right + for i = origin[2]+1,origin[2]+size[2]-1 do + spr(19, origin[1]*8, i*8, 0) + spr(19, (origin[1]+size[1])*8, i*8, 0, 1, 1) + end + -- Corners (NW,NE,SW,SE) + spr(16, origin[1]*8, origin[2]*8, 0) + spr(16, (origin[1]+size[1])*8, origin[2]*8, 0, 1, 1) + spr(16, origin[1]*8, (origin[2]+size[2])*8, 0, 1, 2) + spr(16, (origin[1]+size[1])*8, (origin[2]+size[2])*8, 0, 1, 3) + -- Filling + rect((origin[1]+1)*8, (origin[2]+1)*8, + (size[1]-1)*8, (size[2]-1)*8, 11) + -- Dialog + local dialog = d:get_dialog() + print(dialog.display, 2*8, 10*8, 15, false, 1, true) + if dialog:is_confirm() then + spr(20, (origin[1]+size[1]-1)*8, (origin[2]+size[2])*8-4, 0) + end +end function DrawingSystem:draw(game) local level = game.levels[game.level] local info = level:get_level() local pos = level:get_pos() map(pos.x//8,pos.y//8,31,18,-(pos.x%8),-(pos.y%8),-1) - DrawingSystem:run(game.entities,pos) + self:run(game.entities,pos) + if game.status == 3 then + self:dialog(game.dialog) + end end local LevelSystem = System(Level) @@ -675,6 +801,19 @@ function ActionSystem:exec(entity,game) end end +-- Not a System per se but it should be +local DialogSystem = util.new_class() +function DialogSystem:run(entity,input,game) + local dialog = entity:get_dialog() + if not dialog:step() then + game.status = 1 + elseif input.rawp[5] and not dialog:yes() then + game.status = 1 + elseif input.rawp[6] and not dialog:no() then + game.status = 1 + end +end + local EffectSystem = System() function EffectSystem:exec(entity) if entity:has_circleeffect() then @@ -722,7 +861,7 @@ local player = Entity():with({ Metadata("Giulia"), Sprite(256), Pos(100,10), Health(100), Status(), Movement(), Jump(10), - Input(0,1,2,3,4,5), Action(Action.forcefield,nil) + Input(0,1,2,3,4,5), Action(Action.forcefield,Action.dialog) }) local level1_entities = { @@ -738,9 +877,14 @@ local level1_entities = { :with(PosAnim(function(t,p) p.y = p.y - math.sin(t/6)/3 end)), } --- TODO turn this into something more structured, containing everything related --- to the game +--- Wrapper description of the running game +-- +-- A table containing all the information about the game. +-- @field status The status of the game (1 = running, 2 = pause, 3 = dialog) +-- @field player Reference to the main player +-- TODO turn `game` into a component local game = { + status = 1, player = player, entities = { player }, level = 1, @@ -764,20 +908,28 @@ function TIC() cls(0) InputSystem:run(game.entities) - TargetSystem:run(game.entities) - StatusSystem:run(game.entities) + if game.status == 1 then + -- Running game + ActionSystem:run(game.entities, game) + TargetSystem:run(game.entities) + + StatusSystem:run(game.entities) - MovementSystem:run(game.entities) - ActionSystem:run(game.entities, game) - ForceFieldSystem:run(game.entities, game.entities) + MovementSystem:run(game.entities) + ForceFieldSystem:run(game.entities, game.entities) - PosAnimSystem:run(game.entities) - EffectSystem:run(game.entities) - ParticlesSystem:run(game.entities, game) + PosAnimSystem:run(game.entities) + EffectSystem:run(game.entities) + ParticlesSystem:run(game.entities, game) - LevelSystem:run(game.levels, game) - BoundarySystem:run(game.entities, game.levels[game.level]:get_pos()) + LevelSystem:run(game.levels, game) + BoundarySystem:run(game.entities, game.levels[game.level]:get_pos()) + + elseif game.status == 3 then + -- Dialog/Menu + DialogSystem:run(game.dialog, game.player:get_input(), game) + end DrawingSystem:draw(game) @@ -794,6 +946,11 @@ end -- 002:6666666666666666666666666666666666666666666666666666666666666666 -- 003:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -- 004:9999999999999999999999999999999999999999999999999999999999999999 +-- 016:8888000089988888899899998888aaaa089abbbb089abbbb089abbbb089abbbb +-- 017:089abbbb089abbbb089abbbb089abbbb8888aaaa899899998998888888880000 +-- 018:000000008888888899999999aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +-- 019:089abbbb089abbbb089abbbb089abbbb089abbbb089abbbb089abbbb089abbbb +-- 020:0000000000000000cccccccd0cccccd000cccd00000cd0000000000000000000 -- -- -- cgit v1.2.3