Skip to content
This repository was archived by the owner on Jun 8, 2018. It is now read-only.

Commit 0f62503

Browse files
committed
Use the cutscene builder from std::gregwar for the intro
1 parent c9dc6d1 commit 0f62503

3 files changed

Lines changed: 307 additions & 12 deletions

File tree

data/maps/caves/portal_cave.dat

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,16 @@ sensor{
16541654
height = 16,
16551655
}
16561656

1657+
teletransporter{
1658+
layer = 0,
1659+
x = 152,
1660+
y = 40,
1661+
width = 16,
1662+
height = 16,
1663+
destination_map = "out/e4",
1664+
destination = "from_portal_cave",
1665+
}
1666+
16571667
tile{
16581668
layer = 1,
16591669
x = 112,

data/maps/out/e4.lua

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
local map = ...
22
local game = map:get_game()
33

4+
local cutscene = require("scripts/maps/cutscene")
5+
6+
local function start_pit_initial_cutscene()
7+
8+
cutscene.builder(game, map, hero)
9+
.dialog("out.e4.pit_hello")
10+
.exec(function()
11+
hero:freeze()
12+
end)
13+
.movement({
14+
type = "straight",
15+
entity = pit,
16+
properties = {
17+
angle = 0,
18+
speed = 64,
19+
max_distance = 176,
20+
ignore_obstacles = true,
21+
},
22+
})
23+
.exec(function()
24+
pit:remove()
25+
hero:unfreeze()
26+
end)
27+
.start()
28+
29+
end
30+
431
function map:on_started(destination)
532

633
if game:get_value("out_e4_pit_first_dialog") then
@@ -13,18 +40,8 @@ function map:on_opening_transition_finished(destination)
1340
if destination == from_portal_cave then
1441
if not game:get_value("out_e4_pit_first_dialog") then
1542
game:set_value("out_e4_pit_first_dialog", true)
16-
game:start_dialog("out.e4.pit_hello", function()
17-
hero:freeze()
18-
local movement = sol.movement.create("straight")
19-
movement:set_angle(0)
20-
movement:set_speed(64)
21-
movement:set_max_distance(176)
22-
movement:set_ignore_obstacles(true)
23-
movement:start(pit, function()
24-
pit:remove()
25-
hero:unfreeze()
26-
end)
27-
end)
43+
44+
start_pit_initial_cutscene()
2845
end
2946
end
3047
end

data/scripts/maps/cutscene.lua

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
-- @author std::gregwar
2+
--
3+
--This script allows to construct cutscenes in a flat way, i.e. without chaining callbacks
4+
--
5+
-- Usage:
6+
--
7+
-- require() the script, use builder function to start building a cutscene
8+
--
9+
-- Example:
10+
--
11+
-- --In a map script file
12+
--
13+
-- local cutscene = require('scripts/maps/cutscene')
14+
--
15+
-- local game, map, hero = --init those vals as always
16+
17+
-- function map:start_cinematic()
18+
-- cutscene.builder(game, map, hero)
19+
-- .wait(1000) --wait one second
20+
-- .dialog('my_dialog') --start a dialog
21+
-- .wait(500) --wait between dialog and next cell
22+
-- .hero_start_treasure(...)
23+
-- ....
24+
-- .start() --start the cinematic we just built
25+
-- end
26+
--
27+
-- Limitations:
28+
--
29+
-- Pay attention to the fact that all the 'cells' are created at the same time, i.e before
30+
-- the cinematic starts. This implies that a exec cell that modifies up-values, will not have an effect
31+
-- on next cells, as they are already constructed.
32+
--
33+
-- You can, howewer use the fact that returned values in a exec closure are forwarded to the next cell
34+
--
35+
-- Example:
36+
--
37+
-- cutscene.builder(game, map, hero)
38+
-- .exec(function() return 1 end)
39+
-- .exec(function(val) print(val) end)
40+
-- .start()
41+
-- -- will print '1', as the returned value in last exec has been fowarded
42+
-- -- cell .wait forward the preceding values
43+
44+
local cutscene = {}
45+
46+
local utils = {}
47+
48+
--used internally to unpack properties
49+
local function unpack_or_val(v)
50+
if type(v) == 'table' then
51+
return unpack(v)
52+
else
53+
return v
54+
end
55+
end
56+
57+
--ensure f is callable
58+
local function safe(f)
59+
return f or function() end
60+
end
61+
62+
--apply the properties props by searching the object for setters
63+
function utils.apply_properties(obj,props)
64+
for pname,pval in pairs(props) do
65+
local sname = 'set_'.. pname
66+
local setter = obj[sname]
67+
safe(setter)(obj,unpack_or_val(pval))
68+
end
69+
end
70+
71+
local builder_meta = {}
72+
73+
local flagers = {}
74+
75+
--flagers allow to add callless 'flags'
76+
--in the middle of the call chain
77+
function flagers.dont_wait_for(b)
78+
b.flags.wait = nil
79+
end
80+
81+
function flagers.no_continue(b)
82+
b.flags.no_cont = true
83+
end
84+
85+
--check if there is a flager for the given key
86+
--resume normal behaviour if key is a normal key
87+
function builder_meta.__index(b,k)
88+
local p = rawget(b,k);
89+
if not p then --if there is actual prop search for flager
90+
local flager = flagers[k]
91+
if flager then
92+
flager(b)
93+
return b
94+
end --return nil when no flager
95+
else -- return actual prop
96+
return p
97+
end
98+
end
99+
100+
--create a cutscene builder
101+
function cutscene.builder(game, map, hero)
102+
local b = {}
103+
local bhead = b
104+
105+
--reset flags to default
106+
--called after each cells
107+
function b.reset_flags()
108+
--setup default flags
109+
b.flags = {
110+
wait = true;
111+
}
112+
return b
113+
end
114+
115+
b.reset_flags()
116+
117+
--------------------
118+
-- CELLS
119+
--------------------
120+
121+
-- base cell that run your function closure
122+
-- your function is given a callable cont that you must call to continue
123+
-- the cutscene, unless you use .dont_wait_for. flag before.
124+
-- closure(cont,vals returned from the preceding cell)
125+
--
126+
-- any val passed to cont() are forwarded to the next cell
127+
-- cells like wait forward the received args to the next cell
128+
function b.and_then(closure)
129+
local cell = {}
130+
--save wait flag as upval
131+
local wait = b.flags.wait
132+
local no_cont = b.flags.no_cont
133+
function bhead.next(...)
134+
if wait then
135+
closure(safe(cell.next),...)
136+
else --call next directly if wait flag is down
137+
if no_cont then --need continuation?
138+
safe(cell.next)(closure(...))
139+
else
140+
safe(cell.next)(closure(safe(),...))
141+
end
142+
end
143+
end
144+
b.reset_flags()
145+
bhead = cell;
146+
return b;
147+
end
148+
149+
--shorthand for .dont_wait_for.and_then()
150+
function b.exec(closure)
151+
return b.dont_wait_for.no_continue.and_then(closure)
152+
end
153+
154+
--start a timer and resume next cell when it expires
155+
--second arg is an optional context, default one will be the map
156+
function b.wait(time,op_ctx)
157+
return b.and_then(
158+
function(cont,...)
159+
local args = {...}
160+
local function curried_cont()
161+
return cont(unpack(args))
162+
end
163+
return sol.timer.start(op_ctx or map,time,curried_cont)
164+
end
165+
)
166+
end
167+
168+
--play a sound, this don't wait for the sound to finish
169+
function b.sound(sound_id)
170+
return b.and_then(
171+
function(cont)
172+
sol.audio.play_sound(sound_id)
173+
cont()
174+
end
175+
)
176+
end
177+
178+
--show a dialog using game:start_dialog, this continues the
179+
--cell chain after the dialog ends, next cell get the dialog result
180+
--as the dialog callback do usually
181+
function b.dialog(dialog_id,info)
182+
return b.and_then(
183+
function(cont,passed_info)
184+
game:start_dialog(dialog_id,info or passed_info,cont)
185+
end
186+
)
187+
end
188+
189+
--create a movement and starts it
190+
--first argument is a params table that is typicaly like this :
191+
--{ type ='type_of_movement_to_create',entity=entity_to_move,
192+
-- properties = {speed = 30,target = {x,y},...}
193+
--second optional argument is a function that will receive the movement
194+
-- and can do a last setup before it is started
195+
-- op_setup_closure(mov)
196+
function b.movement(params,op_setup_closure)
197+
return b.and_then(
198+
function(cont)
199+
local mov = sol.movement.create(params.type)
200+
utils.apply_properties(mov,params.properties)
201+
safe(op_setup_closure)(mov)
202+
mov:start(params.entity,cont)
203+
return mov
204+
end
205+
)
206+
end
207+
208+
--start an animation on the given sprite
209+
-- use with .dont_wait_for. flag if the animation loops,
210+
-- otherwise the cinematic will get stuck
211+
function b.sprite_animation(sprite,animation)
212+
return b.and_then(
213+
function(cont)
214+
sprite:set_animation(animation,cont)
215+
end
216+
)
217+
end
218+
219+
--same as sprite_animation but uses hero:set_animation
220+
function b.hero_animation(animation)
221+
return b.and_then(
222+
function(cont)
223+
hero:set_animation(animation,cont)
224+
end
225+
)
226+
end
227+
228+
--calls hero:start_treasure, forwarding any arguments, and then resume cell chain
229+
function b.hero_start_treasure(...)
230+
local args = {...}
231+
return b.and_then(
232+
function(cont)
233+
hero:start_treasure(unpack(args),cont);
234+
end
235+
)
236+
end
237+
238+
--calls hero:start_victory, and the resume cell chain
239+
function b.hero_victory()
240+
return b.and_then(
241+
function(cont)
242+
hero:start_victory(cont)
243+
end
244+
)
245+
end
246+
247+
--set the direction of a given entity
248+
function b.set_direction(entity,direction)
249+
return b.exec(
250+
function()
251+
entity:set_direction(direction)
252+
end
253+
)
254+
end
255+
256+
--start the constructed cutscene
257+
function b.start(...)
258+
b.next(...)
259+
end
260+
261+
-------------------------------------------------------
262+
-- END CELLS
263+
-------------------------------------------------------
264+
265+
return setmetatable(b,builder_meta)
266+
end
267+
268+
return cutscene

0 commit comments

Comments
 (0)