-
Notifications
You must be signed in to change notification settings - Fork 0
/
custom_levels.lua
586 lines (543 loc) · 24.1 KB
/
custom_levels.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
--- Load in custom levels with static level gen.
-- @module CustomLevels
local custom_level_params = {
hide_entrance = true,
}
local custom_level_state = {
active = false,
file_name = nil,
width = nil,
height = nil,
room_generation_callback = nil,
procedural_spawn_callback = nil,
embedded_currency_callback = nil,
embedded_item_callback = nil,
floor_spread_callback = nil,
bat_callback = nil,
allowed_spawn_types = 0,
entrance_tc = nil,
entrance_remove_callback = nil,
custom_theme_id = 4000,
custom_theme = nil,
}
-- Create a bunch of room templates that can be used in lvl files to create rooms. The maximum
-- level size is 8x15, so we only create that many templates.
local room_templates = {}
for x = 0, 7 do
local room_templates_x = {}
for y = 0, 14 do
local room_template = define_room_template("setroom" .. y .. "_" .. x, ROOM_TEMPLATE_TYPE.NONE)
room_templates_x[y] = room_template
end
room_templates[x] = room_templates_x
end
local removed_procedural_spawns = {
ENT_TYPE.MONS_PET_DOG,
ENT_TYPE.ITEM_BONES,
ENT_TYPE.EMBED_GOLD,
ENT_TYPE.EMBED_GOLD_BIG,
ENT_TYPE.ITEM_POT,
ENT_TYPE.ITEM_NUGGET,
ENT_TYPE.ITEM_NUGGET_SMALL,
ENT_TYPE.ITEM_SKULL,
ENT_TYPE.ITEM_CHEST,
ENT_TYPE.ITEM_CRATE,
ENT_TYPE.MONS_PET_CAT,
ENT_TYPE.MONS_PET_HAMSTER,
ENT_TYPE.ITEM_ROCK,
ENT_TYPE.ITEM_RUBY,
ENT_TYPE.ITEM_CURSEDPOT,
ENT_TYPE.ITEM_SAPPHIRE,
ENT_TYPE.ITEM_EMERALD,
ENT_TYPE.MONS_SCARAB,
ENT_TYPE.ITEM_WEB,
ENT_TYPE.ITEM_GOLDBAR,
ENT_TYPE.ITEM_GOLDBARS,
ENT_TYPE.ITEM_SKULL,
ENT_TYPE.MONS_SKELETON,
ENT_TYPE.ITEM_POTOFGOLD,
ENT_TYPE.MONS_LEPRECHAUN,
ENT_TYPE.DECORATION_POTOFGOLD_RAINBOW,
ENT_TYPE.ITEM_REDLANTERN,
}
local removed_torches = {
ENT_TYPE.ITEM_WALLTORCH,
ENT_TYPE.ITEM_LITWALLTORCH,
ENT_TYPE.ITEM_AUTOWALLTORCH,
}
local removed_embedded_currencies = {
ENT_TYPE.EMBED_GOLD,
ENT_TYPE.EMBED_GOLD_BIG,
ENT_TYPE.ITEM_RUBY,
ENT_TYPE.ITEM_SAPPHIRE,
ENT_TYPE.ITEM_EMERALD,
}
local removed_embedded_items = {
ENT_TYPE.ITEM_ALIVE_EMBEDDED_ON_ICE,
ENT_TYPE.ITEM_PICKUP_ROPEPILE,
ENT_TYPE.ITEM_PICKUP_BOMBBAG,
ENT_TYPE.ITEM_PICKUP_BOMBBOX,
ENT_TYPE.ITEM_PICKUP_SPECTACLES,
ENT_TYPE.ITEM_PICKUP_CLIMBINGGLOVES,
ENT_TYPE.ITEM_PICKUP_PITCHERSMITT,
ENT_TYPE.ITEM_PICKUP_SPRINGSHOES,
ENT_TYPE.ITEM_PICKUP_SPIKESHOES,
ENT_TYPE.ITEM_PICKUP_PASTE,
ENT_TYPE.ITEM_PICKUP_COMPASS,
ENT_TYPE.ITEM_PICKUP_PARACHUTE,
ENT_TYPE.ITEM_CAPE,
ENT_TYPE.ITEM_JETPACK,
ENT_TYPE.ITEM_TELEPORTER_BACKPACK,
ENT_TYPE.ITEM_HOVERPACK,
ENT_TYPE.ITEM_POWERPACK,
ENT_TYPE.ITEM_WEBGUN,
ENT_TYPE.ITEM_SHOTGUN,
ENT_TYPE.ITEM_FREEZERAY,
ENT_TYPE.ITEM_CROSSBOW,
ENT_TYPE.ITEM_CAMERA,
ENT_TYPE.ITEM_TELEPORTER,
ENT_TYPE.ITEM_MATTOCK,
ENT_TYPE.ITEM_BOOMERANG,
ENT_TYPE.ITEM_MACHETE,
ENT_TYPE.ITEM_POTOFGOLD,
}
local procedural_enemies = {
ENT_TYPE.MONS_BAT,
ENT_TYPE.MONS_HERMITCRAB,
ENT_TYPE.TADPOLE,
}
local ALLOW_SPAWN_TYPE = {
PROCEDURAL = 1,
EMBEDDED_CURRENCY = 2,
EMBEDDED_ITEMS = 3,
PROCEDURAL_ENEMIES = 4,
PROCEDURAL_TORCHES = 5,
}
-- Keep old name in case it's being used.
ALLOW_SPAWN_TYPE.BACKLAYER_BATS = ALLOW_SPAWN_TYPE.PROCEDURAL_ENEMIES
local function set_hide_entrance(hide_entrance)
custom_level_params.hide_entrance = hide_entrance
end
-- Resets the state to remove references to the loaded file and removes callbacks that alter the level.
local function unload_level()
if not custom_level_state.active then return end
custom_level_state.allowed_spawn_types = 0
custom_level_state.active = false
custom_level_state.file_name = nil
custom_level_state.width = nil
custom_level_state.height = nil
custom_level_state.custom_theme = nil
if custom_level_state.room_generation_callback then
clear_callback(custom_level_state.room_generation_callback)
end
custom_level_state.room_generation_callback = nil
if custom_level_state.procedural_spawn_callback then
clear_callback(custom_level_state.procedural_spawn_callback)
end
custom_level_state.procedural_spawn_callback = nil
if custom_level_state.embedded_currency_callback then
clear_callback(custom_level_state.embedded_currency_callback)
end
custom_level_state.embedded_currency_callback = nil
if custom_level_state.embedded_item_callback then
clear_callback(custom_level_state.embedded_item_callback)
end
custom_level_state.embedded_item_callback = nil
if custom_level_state.floor_spread_callback then
clear_callback(custom_level_state.floor_spread_callback)
end
custom_level_state.floor_spread_callback = nil
if custom_level_state.bat_callback then
clear_callback(custom_level_state.bat_callback)
end
custom_level_state.bat_callback = nil
if custom_level_state.entrance_tc then
clear_callback(custom_level_state.entrance_tc)
end
custom_level_state.entrance_tc = nil
if custom_level_state.entrance_remove_callback then
clear_callback(custom_level_state.entrance_remove_callback)
end
custom_level_state.entrance_remove_callback = nil
end
local function load_level(load_level_ctx, file_name, custom_theme, allowed_spawn_types, width, height)
allowed_spawn_types = allowed_spawn_types or 0
unload_level()
custom_level_state.active = true
custom_level_state.file_name = file_name
custom_level_state.width = width
custom_level_state.height = height
custom_level_state.allowed_spawn_types = allowed_spawn_types
custom_level_state.custom_theme = custom_theme
function override_level(ctx)
local level_files = {
file_name,
}
ctx:override_level_files(level_files)
end
if load_level_ctx then
override_level(load_level_ctx)
end
if custom_theme then
force_custom_theme(custom_theme)
end
custom_level_state.room_generation_callback = set_callback(function(ctx)
if state.screen ~= SCREEN.LEVEL then return end
if width and height then
state.height = height
state.width = width
end
for x = 0, state.width - 1 do
for y = 0, state.height - 1 do
ctx:set_room_template(x, y, LAYER.FRONT, room_templates[x][y])
end
end
end, ON.POST_ROOM_GENERATION)
----------------------------
---- HIDE ENTRANCE DOOR ----
----------------------------
local entranceX
local entranceY
local entranceLayer
custom_level_state.entrance_tc = set_pre_tile_code_callback(function(x, y, layer)
if state.screen == 13 then return end
entranceX = math.floor(x)
entranceY = math.floor(y)
entranceLayer = layer
return false
end, "entrance")
custom_level_state.entrance_remove_callback = set_post_entity_spawn(function (entity)
if not entranceX or not entranceY or not entranceLayer then return end
if not custom_level_params.hide_entrance then return end
local px, py, pl = get_position(entity.uid)
if math.abs(px - entranceX) < 1 and math.abs(py - entranceY) < 1 and pl == entranceLayer then
kill_entity(entity.uid)
end
end, SPAWN_TYPE.ANY, 0, ENT_TYPE.BG_DOOR)
-----------------------------
---- /HIDE ENTRANCE DOOR ----
-----------------------------
custom_level_state.procedural_spawn_callback = set_post_entity_spawn(function(entity, spawn_flags)
if test_flag(custom_level_state.allowed_spawn_types, ALLOW_SPAWN_TYPE.PROCEDURAL) then return end
-- Do not remove spawns from a script.
if spawn_flags & SPAWN_TYPE.SCRIPT ~= 0 then return end
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
entity.flags = set_flag(entity.flags, ENT_FLAG.DEAD)
move_entity(entity.uid, 1000, 0, 0, 0)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_GENERAL | SPAWN_TYPE.LEVEL_GEN_PROCEDURAL, 0, removed_procedural_spawns)
custom_level_state.embedded_currency_callback = set_post_entity_spawn(function(entity, spawn_flags)
if test_flag(custom_level_state.allowed_spawn_types, ALLOW_SPAWN_TYPE.EMBEDDED_CURRENCY) then return end
-- Do not remove spawns from a script.
if spawn_flags & SPAWN_TYPE.SCRIPT ~= 0 then return end
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
entity.flags = set_flag(entity.flags, ENT_FLAG.DEAD)
move_entity(entity.uid, 1000, 0, 0, 0)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_TILE_CODE, 0, removed_embedded_currencies)
custom_level_state.embedded_item_callback = set_post_entity_spawn(function(entity, spawn_flags)
if test_flag(custom_level_state.allowed_spawn_types, ALLOW_SPAWN_TYPE.EMBEDDED_ITEMS) then return end
-- Do not remove spawns from a script.
if spawn_flags & SPAWN_TYPE.SCRIPT ~= 0 then return end
-- Only remove entities with an overlay, these should be the ones embeded in a crust.
if not entity.overlay then return end
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
entity.flags = set_flag(entity.flags, ENT_FLAG.DEAD)
move_entity(entity.uid, 1000, 0, 0, 0)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_TILE_CODE, 0, removed_embedded_items)
custom_level_state.bat_callback = set_post_entity_spawn(function(entity, spawn_type)
if test_flag(custom_level_state.allowed_spawn_types, ALLOW_SPAWN_TYPE.PROCEDURAL_ENEMIES) then return end
-- Do not remove spawns from a script.
if spawn_type & SPAWN_TYPE.SCRIPT ~= 0 then return end
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
entity.flags = set_flag(entity.flags, ENT_FLAG.DEAD)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_GENERAL, 0, procedural_enemies)
custom_level_state.procedural_spawn_callback = set_post_entity_spawn(function(entity, spawn_flags)
if test_flag(custom_level_state.allowed_spawn_types, ALLOW_SPAWN_TYPE.PROCEDURAL_TORCHES) then return end
-- Do not remove spawns from a script.
if spawn_flags & SPAWN_TYPE.SCRIPT ~= 0 then return end
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
entity.flags = set_flag(entity.flags, ENT_FLAG.DEAD)
move_entity(entity.uid, 1000, 0, 0, 0)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_GENERAL | SPAWN_TYPE.LEVEL_GEN_PROCEDURAL, 0, removed_torches)
custom_level_state.floor_spread_callback = set_post_entity_spawn(function(entity)
entity.flags = set_flag(entity.flags, ENT_FLAG.INVISIBLE)
move_entity(entity.uid, 1000, 0, 0, 0)
entity:destroy()
end, SPAWN_TYPE.LEVEL_GEN_FLOOR_SPREADING, 0)
end
--- Load in a level file.
-- @param file_name name/path to the file to load.
-- @param width Width of the level in the file.
-- @param height Height of the level in the file.
-- @param load_level_ctx ON.PRE_LOAD_LEVEL_FILES context to load the level file into.
-- @param allowed_spawn_types Optional spawn types flags to allow certain types of procedural spawns to spawn without being eliminated.
--
-- Note: This must be called in ON.PRE_LOAD_LEVEL_FILES with the load_level_ctx from that callback.
local function load_level_legacy(file_name, width, height, load_level_ctx, allowed_spawn_types)
return load_level(load_level_ctx, file_name, nil, allowed_spawn_types, width, height)
end
local BORDER_THEME = {
DEFAULT = 1,
HARD_FLOOR = 2,
SUNKEN_CITY = 3,
NEO_BABYLON = 4,
ICE_CAVES = 5,
ICE_SUNKEN = 6,
ICE_BABY = 7,
DUAT = 8,
NONE = 9,
COSMIC_OCEAN = 10,
TIAMAT = 11,
}
local GROWABLE_SPAWN_TYPE = {
NONE = 0,
CHAINS = 1,
TIDE_POOL_POLES = 2,
VINES = 4,
}
GROWABLE_SPAWN_TYPE.ALL = GROWABLE_SPAWN_TYPE.CHAINS | GROWABLE_SPAWN_TYPE.TIDE_POOL_POLES | GROWABLE_SPAWN_TYPE.VINES
local function theme_for_border_theme(border_theme)
if border_theme == BORDER_THEME.DEFAULT or border_theme == BORDER_THEME.NONE then
return nil
elseif border_theme == BORDER_THEME.HARD_FLOOR then
return THEME.DWELLING
elseif border_theme == BORDER_THEME.SUNKEN_CITY then
return THEME.SUNKEN_CITY
elseif border_theme == BORDER_THEME.NEO_BABYLON then
return THEME.NEO_BABYLON
elseif border_theme == BORDER_THEME.ICE_CAVES or border_theme == BORDER_THEME.ICE_SUNKEN or border_theme == BORDER_THEME.ICE_BABY then
return THEME.ICE_CAVES
elseif border_theme == BORDER_THEME.DUAT then
return THEME.DUAT
elseif border_theme == BORDER_THEME.TIAMAT then
return THEME.TIAMAT
elseif border_theme == BORDER_THEME.COSMIC_OCEAN then
return THEME.COSMIC_OCEAN
end
end
local function entity_theme_for_border_theme(border_theme)
if border_theme == BORDER_THEME.ICE_SUNKEN then
return THEME.SUNKEN_CITY
elseif border_theme == BORDER_THEME.ICE_BABY then
return THEME.NEO_BABYLON
end
return theme_for_border_theme(border_theme)
end
local function background_texture_for_theme(theme)
if theme == THEME.DWELLING or theme == THEME.BASE_CAMP or theme == THEME.COSMIC_OCEAN then
return TEXTURE.DATA_TEXTURES_BG_CAVE_0
elseif theme == THEME.VOLCANA then
return TEXTURE.DATA_TEXTURES_BG_VOLCANO_0
elseif theme == THEME.JUNGLE then
return TEXTURE.DATA_TEXTURES_BG_JUNGLE_0
elseif theme == THEME.OLMEC then
return TEXTURE.DATA_TEXTURES_BG_STONE_0
elseif theme == THEME.TIDE_POOL or theme == THEME.ABZU or theme == THEME.TIAMAT then
return TEXTURE.DATA_TEXTURES_BG_TIDEPOOL_0
elseif theme == THEME.TEMPLE or theme == THEME.DUAT then
return TEXTURE.DATA_TEXTURES_BG_TEMPLE_0
elseif theme == THEME.CITY_OF_GOLD then
return TEXTURE.DATA_TEXTURES_BG_GOLD_0
elseif theme == THEME.ICE_CAVES then
return TEXTURE.DATA_TEXTURES_BG_ICE_0
elseif theme == THEME.NEO_BABYLON then
return TEXTURE.DATA_TEXTURES_BG_BABYLON_0
elseif theme == THEME.SUNKEN_CITY or theme == THEME.HUNDUN then
return TEXTURE.DATA_TEXTURES_BG_SUNKEN_0
elseif theme == THEME.EGGPLANT_WORLD then
return TEXTURE.DATA_TEXTURES_BG_EGGPLANT_0
end
end
local function floor_texture_for_theme(theme)
if theme == THEME.DWELLING then
return TEXTURE.DATA_TEXTURES_FLOOR_CAVE_0
elseif theme == THEME.BASE_CAMP then
return TEXTURE.DATA_TEXTURES_FLOOR_SURFACE_0
elseif theme == THEME.VOLCANA then
return TEXTURE.DATA_TEXTURES_FLOOR_VOLCANO_0
elseif theme == THEME.JUNGLE or theme == THEME.OLMEC then
return TEXTURE.DATA_TEXTURES_FLOOR_JUNGLE_0
elseif theme == THEME.TIDE_POOL or theme == THEME.TIAMAT or theme == THEME.ABZU then
return TEXTURE.DATA_TEXTURES_FLOOR_TIDEPOOL_0
elseif theme == THEME.TEMPLE or theme == THEME.DUAT or theme == THEME.CITY_OF_GOLD then
return TEXTURE.DATA_TEXTURES_FLOOR_TEMPLE_0
elseif theme == THEME.ICE_CAVES then
return TEXTURE.DATA_TEXTURES_FLOOR_ICE_0
elseif theme == THEME.NEO_BABYLON then
return TEXTURE.DATA_TEXTURES_FLOOR_BABYLON_0
elseif theme == THEME.SUNKEN_CITY or theme == THEME.HUNDUN then
return TEXTURE.DATA_TEXTURES_FLOOR_SUNKEN_0
elseif theme == THEME.EGGPLANT_WORLD then
return TEXTURE.DATA_TEXTURES_FLOOR_EGGPLANT_0
end
return TEXTURE.DATA_TEXTURES_FLOOR_CAVE_0
end
local aaab = false
local function create_custom_theme(theme_properties, level_file)
local theme = theme_properties.theme
local subtheme = theme_properties.subtheme or theme_properties.co_subtheme
local border_theme = theme_properties.theme
local border_entity_theme = theme_properties.theme
local border = theme_properties.border_type or theme_properties.border
if border then
if border == BORDER_THEME.NONE then
border_theme = false
border_entity_theme = false
else
border_theme = theme_for_border_theme(border) or border_theme
border_entity_theme = entity_theme_for_border_theme(border) or border_entity_theme
end
end
border_theme = theme_properties.border_theme or border_theme
border_entity_theme = theme_properties.border_entity_theme or border_entity_theme
local custom_theme = CustomTheme:new(custom_level_state.custom_theme_id, theme)
custom_level_state.custom_theme_id = custom_level_state.custom_theme_id + 1
-- Spawning effects does things like changing the camera bounds in ice caves and spawning the duat bosses.
if not theme_properties.dont_spawn_effects then
custom_theme:override(THEME_OVERRIDE.SPAWN_EFFECTS, theme)
end
custom_theme:override(THEME_OVERRIDE.SPAWN_BORDER, border_theme)
custom_theme:override(THEME_OVERRIDE.ENT_BORDER, border_entity_theme)
-- The INIT_LEVEL is required for some effects like duat fog to look proper, but it could do other things that may be
-- undesired, so it can be disabled.
if not theme_properties.dont_init then
custom_theme:override(THEME_OVERRIDE.INIT_LEVEL, border_theme)
end
if (border_theme == THEME.COSMIC_OCEAN and not theme_properties.dont_loop) or theme_properties.loop then
custom_theme:override(THEME_OVERRIDE.LOOP, THEME.COSMIC_OCEAN)
end
-- Some themes, such as cosmic ocean, do not get the level size from the level file, so it must be manually configured.
if theme_properties.width or theme_properties.height then
custom_theme:post(THEME_OVERRIDE.INIT_LEVEL, function()
if theme_properties.width then state.width = theme_properties.width end
if theme_properties.height then state.height = theme_properties.height end
end)
end
custom_theme:post(THEME_OVERRIDE.SPAWN_LEVEL, function()
if theme_properties.dont_spawn_growables then return end
local growables = theme_properties.growables or theme_properties.enabled_growables or theme_properties.growable_spawn_types or GROWABLE_SPAWN_TYPE.ALL
local poles = growables & GROWABLE_SPAWN_TYPE.TIDE_POOL_POLES == GROWABLE_SPAWN_TYPE.TIDE_POOL_POLES
local chains = growables & GROWABLE_SPAWN_TYPE.CHAINS == GROWABLE_SPAWN_TYPE.CHAINS
local vines = growables & GROWABLE_SPAWN_TYPE.VINES == GROWABLE_SPAWN_TYPE.VINES
local function grow_vines_both_layers()
grow_vines(LAYER.FRONT, 256)
grow_vines(LAYER.BACK, 256)
end
local function grow_poles_both_layers()
grow_poles(LAYER.FRONT, 256)
grow_poles(LAYER.BACK, 256)
end
if poles and chains and vines then
grow_poles_both_layers()
grow_vines_both_layers()
state.level_gen.themes[THEME.VOLCANA]:spawn_traps() -- Spawn chains and sliding doors.
elseif poles and chains then
grow_poles_both_layers()
state.level_gen.themes[THEME.VOLCANA]:spawn_traps() -- Spawn chains and sliding doors.
elseif chains and vines then
grow_vines_both_layers()
state.level_gen.themes[THEME.VOLCANA]:spawn_traps() -- Spawn chains and sliding doors.
elseif vines and poles then
grow_poles_both_layers()
grow_vines_both_layers()
state.level_gen.themes[THEME.CITY_OF_GOLD]:spawn_traps() -- Spawn sliding doors.
elseif vines then
grow_vines_both_layers()
state.level_gen.themes[THEME.CITY_OF_GOLD]:spawn_traps() -- Spawn sliding doors.
elseif poles then
grow_poles_both_layers()
state.level_gen.themes[THEME.CITY_OF_GOLD]:spawn_traps() -- Spawn sliding doors.
elseif chains then
state.level_gen.themes[THEME.VOLCANA]:spawn_traps() -- Spawn chains and sliding doors.
else
state.level_gen.themes[THEME.CITY_OF_GOLD]:spawn_traps() -- Spawn sliding doors.
end
end)
custom_theme:post(THEME_OVERRIDE.SPAWN_EFFECTS, function()
if state.screen ~= SCREEN.LEVEL then return end
-- Adjust the camera focus at the start of the level so it does not jump.
if not theme_properties.dont_adjust_camera_focus then
state.camera.adjusted_focus_x = state.level_gen.spawn_x
state.camera.adjusted_focus_y = state.level_gen.spawn_y + 0.05
end
-- If a camera bounds property exists, set the camera bounds to those bounds. Otherwise, leave them alone except for
-- in a cosmic ocean theme, where the camera bounds should be set to the max distance.
if theme_properties.camera_bounds then
state.camera.bounds_left = theme_properties.camera_bounds.left
state.camera.bounds_right = theme_properties.camera_bounds.right
state.camera.bounds_top = theme_properties.camera_bounds.top
state.camera.bounds_bottom = theme_properties.camera_bounds.bottom
elseif not theme_properties.dont_adjust_camera_bounds then
if border_theme == THEME.COSMIC_OCEAN then
state.camera.bounds_left = -math.huge
state.camera.bounds_top = math.huge
state.camera.bounds_right = math.huge
state.camera.bounds_bottom = -math.huge
end
end
end)
if theme_properties.background_theme then
custom_theme.textures[DYNAMIC_TEXTURE.BACKGROUND] = background_texture_for_theme(theme_properties.background_theme) or TEXTURE.DATA_TEXTURES_BG_CAVE_0
custom_theme:override(THEME_OVERRIDE.ENT_BACKWALL, theme_properties.background_theme)
custom_theme:override(THEME_OVERRIDE.SPAWN_BACKGROUND, theme_properties.background_theme)
end
if theme_properties.background_texture_theme then
custom_theme.textures[DYNAMIC_TEXTURE.BACKGROUND] = background_texture_for_theme(theme_properties.background_texture_theme) or TEXTURE.DATA_TEXTURES_BG_CAVE_0
custom_theme:override(THEME_OVERRIDE.ENT_BACKWALL, theme_properties.background_texture_theme)
end
if theme_properties.background_texture then
custom_theme.textures[DYNAMIC_TEXTURE.BACKGROUND] = theme_properties.background_texture
end
if theme_properties.floor_theme then
custom_theme.textures[DYNAMIC_TEXTURE.FLOOR] = floor_texture_for_theme(theme_properties.floor_theme)
-- Spawns extra theme elements over the floor.
custom_theme:override(THEME_OVERRIDE.SPAWN_PROCEDURAL, theme_properties.floor_theme)
end
if theme_properties.floor_texture_theme then
custom_theme.textures[DYNAMIC_TEXTURE.FLOOR] = floor_texture_for_theme(theme_properties.floor_texture_theme)
end
if theme_properties.floor_texture then
custom_theme.textures[DYNAMIC_TEXTURE.FLOOR] = theme_properties.floor_texture
end
custom_theme.theme = theme_properties.theme
custom_theme.level_file = level_file
if theme_properties.post_configure then
theme_properties.post_configure(custom_theme, subtheme, theme_properties)
end
return custom_theme, subtheme
end
--- Load in a level file.
-- @param load_level_ctx ON.PRE_LOAD_LEVEL_FILES context to load the level file into.
-- @param file_name name/path to the file to load.
-- @param custom_theme Either a CustomTheme object or a table of parameters to configure a new CustomTheme.
-- @param allowed_spawn_types Optional spawn types flags to allow certain types of procedural spawns to spawn without being eliminated.
--
-- Note: This must be called in ON.PRE_LOAD_LEVEL_FILES with the load_level_ctx from that callback.
local function load_level_custom_theme(load_level_ctx, file_name, custom_theme, allowed_spawn_types)
local actual_custom_theme = nil
if custom_theme then
if type(custom_theme) == "userdata" and getmetatable(custom_theme).__type.name == "CustomTheme" then
actual_custom_theme = custom_theme
else
actual_custom_theme, subtheme = create_custom_theme(custom_theme, file_name)
if subtheme then force_custom_subtheme(subtheme) end
width = width or custom_theme.width
height = height or custom_theme.height
end
end
load_level(load_level_ctx, file_name, actual_custom_theme, allowed_spawn_types)
end
return {
state = custom_level_state,
load_level = load_level_legacy,
load_level_custom_theme = load_level_custom_theme,
unload_level = unload_level,
ALLOW_SPAWN_TYPE = ALLOW_SPAWN_TYPE,
set_hide_entrance = set_hide_entrance,
BORDER_THEME = BORDER_THEME,
GROWABLE_SPAWN_TYPE = GROWABLE_SPAWN_TYPE,
}