These were committed as empty submodule gitlinks with no .gitmodules, so the folders were never populated and the game could not load (require of libs/hump/timer, libs/sfxrlua/sfxr, narrator.narrator). Checked out each at its pinned commit and vendored the files, matching the existing bundled-libs convention. Removed the junk libs/libs/ tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
167 lines
No EOL
4.4 KiB
Text
167 lines
No EOL
4.4 KiB
Text
local narrator = require('narrator.narrator')
|
|
|
|
---@class Self
|
|
---@field paragraph_template node
|
|
---@field paragraph_initial_position vector3
|
|
---@field paragraph_nodes node[]
|
|
---@field choice_template node
|
|
---@field choice_initial_position vector3
|
|
---@field choice_nodes node[]
|
|
---@field book Narrator.Book
|
|
---@field story Narrator.Story
|
|
|
|
---@param node node
|
|
---@param text string
|
|
---@return integer height
|
|
local function estimated_height(node, text)
|
|
local font_name = gui.get_font(node)
|
|
local font = gui.get_font_resource(font_name)
|
|
|
|
local metrics = resource.get_text_metrics(font, text, {
|
|
width = gui.get_size(node).x,
|
|
line_break = true
|
|
})
|
|
|
|
return metrics.height
|
|
end
|
|
|
|
---@param self Self
|
|
local function clear_output(self)
|
|
for index = #self.paragraph_nodes, 1, -1 do
|
|
local node = self.paragraph_nodes[index]
|
|
table.remove(self.paragraph_nodes, index)
|
|
|
|
gui.animate(node, 'color.w', 0, gui.EASING_LINEAR, 0.5, 0, function(_, node)
|
|
gui.delete_node(node)
|
|
end)
|
|
end
|
|
|
|
for index = #self.choice_nodes, 1, -1 do
|
|
local node = self.choice_nodes[index]
|
|
table.remove(self.choice_nodes, index)
|
|
|
|
gui.animate(node, 'color.w', 0, gui.EASING_LINEAR, 0.5, 0, function(_, node)
|
|
gui.delete_node(node)
|
|
end)
|
|
end
|
|
end
|
|
|
|
---@param self Self
|
|
local function display_output(self)
|
|
clear_output(self)
|
|
|
|
-- Pull all the paragraphs
|
|
local paragraphs = self.story:continue()
|
|
|
|
-- Get the available choices
|
|
local choices = self.story:get_choices()
|
|
|
|
local paragraph_position = vmath.vector3(self.paragraph_initial_position)
|
|
local choice_position = vmath.vector3(self.choice_initial_position)
|
|
|
|
for index = #paragraphs, 1, -1 do
|
|
local paragraph = paragraphs[index]
|
|
local node = gui.clone(self.paragraph_template)
|
|
gui.set_text(node, paragraph.text)
|
|
|
|
local height = estimated_height(node, paragraph.text)
|
|
if index < #paragraphs then
|
|
paragraph_position.y = paragraph_position.y + height / 2 + 16
|
|
end
|
|
|
|
gui.set_position(node, paragraph_position)
|
|
gui.set_enabled(node, true)
|
|
gui.set_alpha(node, 0)
|
|
gui.animate(node, 'color.w', 1, go.EASING_LINEAR, 0.5, index / 2)
|
|
|
|
table.insert(self.paragraph_nodes, node)
|
|
end
|
|
|
|
for index = 1, #choices do
|
|
local choice = choices[index]
|
|
local node = gui.clone(self.choice_template)
|
|
gui.set_text(node, choice.text)
|
|
|
|
local height = estimated_height(node, choice.text)
|
|
if index > 1 then
|
|
choice_position.y = choice_position.y - height / 2 - 16
|
|
end
|
|
|
|
gui.set_enabled(node, true)
|
|
gui.set_position(node, choice_position)
|
|
|
|
local size = gui.get_size(node)
|
|
size.y = height
|
|
gui.set_size(node, size)
|
|
|
|
gui.set_alpha(node, 0)
|
|
gui.animate(node, 'color.w', 1, go.EASING_LINEAR, 0.5, (#paragraphs + 1) / 2)
|
|
|
|
table.insert(self.choice_nodes, node)
|
|
end
|
|
end
|
|
|
|
---@param self Self
|
|
function init(self)
|
|
msg.post('@render:', 'clear_color', { color = vmath.vector4(1, 1, 1, 1)})
|
|
msg.post('.', 'acquire_input_focus')
|
|
|
|
self.paragraph_template = gui.get_node('paragraph')
|
|
self.paragraph_initial_position = gui.get_position(self.paragraph_template)
|
|
self.paragraph_nodes = {}
|
|
|
|
self.choice_template = gui.get_node('choice')
|
|
self.choice_initial_position = gui.get_position(self.choice_template)
|
|
self.choice_nodes = {}
|
|
|
|
-- Parse and save a book
|
|
self.book = narrator.parse_file('example-defold.book', { save = true })
|
|
|
|
-- Or load and parse the Ink file from the custom resources
|
|
-- local content = sys.load_resource('/example-defold/book.ink')
|
|
-- self.book = narrator.parseBook(content)
|
|
|
|
-- Or load a book from the saved lua module
|
|
-- self.book = require('example-defold.book')
|
|
|
|
-- Or parse a book from the string with Ink content
|
|
-- self.book = narrator.parseBook('Hello world!')
|
|
|
|
-- Init a story
|
|
self.story = narrator.init_story(self.book)
|
|
|
|
-- Begin the story
|
|
self.story:begin()
|
|
|
|
display_output(self)
|
|
end
|
|
|
|
---@param self Self
|
|
---@param action_id hash|string
|
|
---@param action table
|
|
function on_input(self, action_id, action)
|
|
if action_id ~= hash 'touch' or not action.pressed then
|
|
return
|
|
end
|
|
|
|
if not self.story:can_choose() then
|
|
-- Begin a new story
|
|
self.story = narrator.init_story(self.book)
|
|
self.story:begin()
|
|
|
|
display_output(self)
|
|
|
|
return true
|
|
end
|
|
|
|
for index, node in ipairs(self.choice_nodes) do
|
|
if gui.pick_node(node, action.x, action.y) then
|
|
-- Make a choice
|
|
self.story:choose(index)
|
|
|
|
display_output(self)
|
|
|
|
return true
|
|
end
|
|
end
|
|
end |