forked from nstbayless/pulp-to-lua
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpulp.lua
1846 lines (1635 loc) · 55.1 KB
/
pulp.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
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
assert(___pulp, "___pulp object must be defined before import")
import "CoreLibs/utilities/sampler"
local pulp <const> = ___pulp
local floor <const> = math.floor
local ceil <const> = math.ceil
local max <const> = math.max
local min <const> = math.min
local substr <const> = string.sub
local random <const> = math.random
local string_char <const> = string.char
local string_byte <const> = string.byte
local TTYPE_WORLD <const> = 0
local TTYPE_PLAYER <const> = 1
local TTYPE_SPRITE <const> = 2
local TTYPE_ITEM <const> = 3
local ACTOR_TYPE_GLOBAL <const> = 0
local ACTOR_TYPE_ROOM <const> = 1
local ACTOR_TYPE_TILE <const> = 2
local __exits = {}
pulp.scripts = {}
pulp.tiles = pulp.tiles or {}
pulp.sounds = pulp.sounds or {}
pulp.tiles_by_name = {}
pulp.rooms_by_name = {}
pulp.sounds_by_name = {}
pulp.roomtiles = {}
pulp.store = {}
pulp.store_dirty = false
local roomtiles = pulp.roomtiles
pulp.rooms = pulp.rooms or {}
pulp.game = {
script = nil,
name = nil,
type = ACTOR_TYPE_GLOBAL,
__tostring = function(...)
return pulp.gamename
end
}
pulp.player = {
is_player = true,
frame = 0,
x = 0,
y = 0
}
pulp.invert = false
pulp.listen = true
pulp.shake = 0
pulp.hideplayer = false -- (resets to false every frame.)
pulp.roomQueued = nil
pulp.frame = 0
pulp.tilemap = playdate.graphics.tilemap.new()
local tilemap <const> = pulp.tilemap
pulp.game_is_loaded = false
pulp.timers = {}
pulp.shakex = 2
pulp.shakey = 2
pulp.exits = nil
pulp.message = nil
pulp.optattachmessage = nil
local disabled_exit_x = nil
local disabled_exit_y = nil
local pulp_tile_fps_lookup_floor = {}
local pulp_tile_fps_lookup_floor_lookup = {}
-- tweak sound engine to work with firefox
local FIREFOX_SOUND_COMPAT = true
-- we scale down the sound to reduce saturation and match playback in Firefox
local SOUNDSCALE <const> = {
[0] = 0.15,
[1] = 0.15,
[2] = 0.15,
[3] = 0.22,
[4] = 0.10
}
pulp.soundscale = SOUNDSCALE
local EMPTY <const> = {
any = function (...) end
}
pulp.EMPTY = {
any = function (...) end
}
pulp.EMPTY.script = pulp.EMPTY
pulp.gameScript = EMPTY
local FPS <const> = 20
local SPF <const> = 1/FPS
local alphabet =
" !\"#$%&'()*+,-./0123"
.. "456789:;<=>?@ABCDEFG"
.. "HIJKLMNOPQRSTUVWXYZ["
.."\\]^_`abcdefghijklmno"
.. "pqrstuvwxyz<|>~"
-- NOT local -- shared with pulpscript!
config = {
autoAct = 1,
inputRepeat=1,
inputRepeatBetween=0.2,
inputRepeatDelay=0.4,
follow = 0,
followCenterX=12,
followCenterY=7,
followOverflowTile = 1,
allowDismissRootMenu = 0,
sayAdvanceDelay = 0.2,
_pulp_to_lua = pulp.tiles[1],
}
-- TODO: datetime
-- TODO: only start if accelerometer is used in this game.
playdate.startAccelerometer()
playdate.display.setRefreshRate(FPS)
local tile_img = pulp.tile_img
local font_img = pulp.font_img
local GRIDX <const>, GRIDY <const> = pulp.tile_img[1]:getSize()
if GRIDX == 8 then
playdate.display.setScale(2)
end
if GRIDX == 4 then
playdate.display.setScale(4)
end
if GRIDX == 2 then
playdate.display.setScale(8)
end
if GRIDX == 1 then
playdate.display.setScale(16)
end
local PIX8SCALE <const> = GRIDX / 8
pulp.gridx = GRIDX
pulp.gridy = GRIDY
pulp.pix8scale = PIX8SCALE
local HALFWIDTH_SRCRECT = nil
if pulp.halfwidth then
HALFWIDTH_SRCRECT = playdate.geometry.rect.new(0, 0, GRIDX/2, GRIDY)
end
playdate.graphics.setBackgroundColor(playdate.graphics.kColorBlack)
local TILESW <const> = 25
local TILESH <const> = 15
local cropl = 0
local cropr = TILESW-1
local cropu = 0
local cropd = TILESH-1
local iscropped = false
tilemap:setImageTable(tile_img)
tilemap:setSize(TILESW, TILESH)
for y = 0,TILESH-1 do
pulp.roomtiles[y] = {}
end
-- sounds
local SOUND_CHANNELS <const> = 5
local wavetypes <const> = {
[0] = playdate.sound.kWaveSine,
[1] = playdate.sound.kWaveSquare,
[2] = playdate.sound.kWaveSawtooth,
[3] = playdate.sound.kWaveTriangle,
[4] = playdate.sound.kWaveNoise,
}
------------------------------------------------ API ---------------------------------------------------------
local function copytable(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
local function paginate(text, w, h)
local x = 0
local y = 0
local startidx = 1
local wordidx = 1
local pages = {""}
for i = 1,#text+1 do
if i == #text+1 then
pages[#pages] = pages[#pages] .. substr(text,startidx, #text)
else
local char = substr(text, i, i)
if char == "\n" or char == "\f" then
pages[#pages] = pages[#pages] .. substr(text, startidx, i)
startidx = i + 1
wordidx = i + 1
y += 1
x = 0
if char == "\f" then
pages[#pages + 1] = ""
y = 0
end
elseif char == " " or char == "\t" then
x += 1
wordidx = i + 1
else
x += 1
end
if x >= w then
if startidx < wordidx then
pages[#pages] = pages[#pages] .. substr(text,startidx, wordidx - 1) .. "\n"
pages[#pages] = pages[#pages] .. substr(text, wordidx, i - 1)
wordidx = 0
else
pages[#pages] = pages[#pages] .. substr(text,startidx, i - 1) .. "\n"
wordidx = i
end
x = 0
y += 1
startidx = i
end
if y >= h then
y = 0
pages[#pages + 1] = ""
end
end
end
if pages[#pages] == "" then
pages[#pages] = nil
end
-- strip lines on each page after the fact
-- FIXME: should strip above for correct behaviour
for i = 1,#pages do
pages[i] = string.gsub(pages[i], " *\n *", "\n")
end
return pages
end
-- https://stackoverflow.com/q/49979017
local function getStackDepth()
local depth = 0
while true do
if not debug.getinfo(3 + depth) then
break
end
depth = depth + 1
end
return depth
end
-- slow, so don't call it except sometimes
local stackdepth = 0
local function preventStackOverflow()
if stackdepth > 300 then
print("WARNING: stack overflow detected.")
return true
end
return false
end
local event_persist = {
aa = playdate.getCrankPosition() or 0,
ra = 0,
ax = 0,
ay = 1,
az = 0,
dx = 0,
dy = 0,
orientation = "standing up"
}
function event_persist:new()
return {
aa = self.aa,
ra = self.ra,
ax = self.ax,
ay = self.ay,
az = self.az,
dx = self.dx,
dy = self.dy,
tx = self.tx,
ty = self.ty,
px = pulp.player.x,
py = pulp.player.y,
game = pulp.game,
room = self.room,
orientation = self.orientation,
frame = pulp.frame
}
end
function pulp:newScript(name)
local script = {}
pulp.scripts[name] = script
return script
end
function pulp:associateScript(name, t, id)
local script = type(name) == "string" and pulp.scripts[name] or name
assert(script, "no script found with name '" .. tostring(name) .. "'")
if t == "tile" then
assert(pulp.tiles[id])
pulp.tiles[id].script = script
elseif t == "room" then
assert(pulp.rooms[id], "unknown room id " .. tostring(id))
pulp.rooms[id].script = script
elseif t == "global" and id == 0 then
pulp.gameScript = script
pulp.game.script = script
else
assert(false)
end
end
function pulp:getScriptEventByName(id, evname)
local script = pulp:getScript(id) or EMPTY
return script[evname] or script.any or function(...) end
end
function pulp:getScript(id)
if type(id) == "string" then
return pulp.scripts[id] or EMPTY
elseif type(id) == "number" then
return (pulp.tiles[id] or EMPTY).script or EMPTY
elseif type(id) == "table" then
return id
else
-- oops. :(
assert(false)
return {}
end
end
function pulp:getCurrentRoom()
return pulp.rooms[pulp.current_room_idx]
end
function pulp:getPlayerScript()
return pulp.player.script
end
function pulp:getTile(tid)
if type(tid) == "number" then
return pulp.tiles[tid]
else
return pulp.tiles_by_name[tid]
end
end
function pulp:getSound(sid)
if type(sid) == "number" then
return pulp.sounds[sid]
else
return pulp.sounds_by_name[sid]
end
end
function pulp:getTileAt(x, y)
return (roomtiles[y] or EMPTY)[x]
end
local function default_event_interact(script, tilei, event, evname)
if tilei.tile.says then
pulp.__fn_say(nil, nil, nil, nil, script, tilei, event, evname, nil, tilei.tile.says)
end
end
local function default_event_collect(script, tilei, event, evname)
local says = tilei.tile.says
pulp.setvariable(tilei.name .. "s", pulp.getvariable(tilei.name .. "s") + 1)
pulp.__fn_swap(tilei, 0)
if says then
pulp.__fn_say(nil, nil, nil, nil, script, tilei, event, evname, nil, says)
end
end
function playdate.cranked(change, acceleratedChange)
local script = pulp:getPlayerScript()
event_persist.aa = playdate.getCrankPosition()
event_persist.ra = change
if script then
(script.crank or script.any)(script, pulp.player, event_persist:new(), "crank")
end
end
function playdate.crankDocked()
local script = pulp:getPlayerScript()
if script then
(script.dock or script.any)(script, pulp.player, event_persist:new(), "dock")
end
end
function playdate.crankUndocked()
local script = pulp:getPlayerScript()
if script then
(script.undock or script.any)(script, pulp.player, event_persist:new(), "undock")
end
end
local function readAccelerometer()
local ax, ay, az = playdate.readAccelerometer()
if ax and ay and ay then
event_persist.ax = ax
event_persist.ay = ay
event_persist.az = az
event_persist.ax = event_persist.ax or 0
event_persist.ay = event_persist.ay or 0
event_persist.az = event_persist.az or 0
if ay >= 0.70 then
event_persist.orientation = "standing up"
elseif ay <= -0.70 then
event_persist.orientation = "upside down"
elseif az >= 0.70 then
event_persist.orientation = "on back"
elseif az <= -0.70 then
event_persist.orientation = "on front"
elseif ax >= 0.70 then
event_persist.orientation = "on right"
elseif ax <= -0.70 then
event_persist.orientation = "on left"
else
-- (hysteresis)
end
end
end
local prev_a = false
local prev_b = false
local prev_up = false
local prev_down = false
local prev_left = false
local prev_right = false
local a_press_time = false
local b_press_time = false
local up_press_time = false
local down_press_time = false
local left_press_time = false
local right_press_time = false
local function updateMessage(up, down, left, right, confirm, cancel)
-- FIXME: yes, this is pretty messy.
local message = pulp.message
local text = (message.text or EMPTY)[message.page]
-- TODO: crank can also dismiss
local skiptext = up or down or confirm or cancel or left or right
if message.dismiss then
pulp.message = pulp.message.previous
return
end
if message.sayAdvanceDelay > 0 then
message.sayAdvanceDelay -= SPF
end
if not message.showoptions and not message.text then
if message.block then
pulp.optattachmessage = message
message.block(message.self, message.script, message.actor, message.event)
pulp.optattachmessage = nil
message.showoptions = true
message.optselect = 1
end
if #message.options == 0 then
-- dismiss menu
print("warning: opened a menu with 0 valid options")
pulp.message = pulp.message.previous
return
end
end
if message.showoptions then
if message.sayAdvanceDelay > 0 then return end
if up then
message.optselect -= 1
elseif down then
message.optselect += 1
end
if message.opth then
if left and message.firstopt ~= 1 then
message.firstopt -= message.opth
message.optselect -= message.opth
end
if right and message.firstopt + message.opth <= #message.options then
message.firstopt += message.opth
message.optselect += message.opth
end
if message.firstopt <= 1 then
message.firstopt = 1
elseif message.firstopt > #message.options then
message.firstopt -= message.opth
end
end
if up or down then
if message.opth then
if message.optselect < message.firstopt then
message.optselect = message.firstopt + message.opth
elseif message.optselect > message.firstopt + message.opth - 1 or message.optselect > #message.options then
message.optselect = message.firstopt
end
else
if message.optselect < 1 then
message.optselect = #message.options
elseif message.optselect > #message.options then
message.optselect = 1
end
end
end
if message.opth then
message.optselect = max(min(message.optselect, message.firstopt + message.opth - 1), message.firstopt)
end
message.optselect = max(min(message.optselect, #message.options), 1) -- paranoia
if confirm then
local option = message.options[message.optselect]
-- dismiss message
if option and option.block then
option.block(option.self, option.actor, option.event, option.evname, option)
-- dismiss ALL menus if no submenu opened in option block
if pulp.message == message then
pulp.message = nil
elseif pulp.message.text then
message.dismiss = true
end
(pulp.gameScript.select or pulp.gameScript.any)(pulp.gameScript, pulp.game, event_persist:new(), "select")
end
elseif cancel then
if pulp.message.previous or config.allowDismissRootMenu ~= 0 then
pulp.message = pulp.message.previous;
(pulp.gameScript.select or pulp.gameScript.any)(pulp.gameScript, pulp.game, event_persist:new(), "dismiss")
else
(pulp.gameScript.select or pulp.gameScript.any)(pulp.gameScript, pulp.game, event_persist:new(), "invalid")
end
end
elseif message.textidx < #text and skiptext and message.sayAdvanceDelay <= 0 then
message.textidx = #text
elseif message.textidx < #text then
message.textidx += 1
elseif skiptext and message.sayAdvanceDelay <= 0 then
-- proceed to next page or close if last page.
if message.page == #message.text then
-- close the final page.
if message.block then
pulp.optattachmessage = message
message.block(message.self, message.script, message.actor, message.event)
pulp.optattachmessage = nil
message.block = nil
end
if message.options and #message.options then
message.showoptions = true
message.optselect = 1
else
-- dismiss message if no menus opened up
message.dismiss = true
if message == pulp.message then
pulp.message = nil
end
end
else
-- go to next page
message.page += 1
message.prompt_timer = -1
message.textidx = 0
end
else
-- wait to proceed
message.prompt_timer += 1
end
end
local function drawMessage(message, submenu)
if not message or message.dismiss then
return
end
if not submenu then
if message.clear then
playdate.graphics.clear(playdate.graphics.kColorBlack)
end
if message.text then
pulp.__fn_window(message.x-1, message.y-1, message.w+2, message.h+2)
local text = substr(message.text[message.page], 1, message.textidx)
pulp.__fn_label(message.x, message.y, nil, nil, text)
-- prompt to advance
if message.prompt_timer >= 0 and not message.showoptions then
local prompt_idx = 10 + floor((message.prompt_timer % FPS) * SPF * 2)
pulp.pipe_img[prompt_idx]:draw(GRIDX * (message.x + message.w - 1), GRIDY * (message.y + message.h))
end
end
end
-- recursively draw options for previous menu
drawMessage(message.previous, true)
-- options
if message.options and message.showoptions then
local optw = message.optw or min(message.options_width, 8)
local opth = message.opth or #message.options
local optx = message.optx or message.x + message.w - 1 - optw
local opty = message.opty or message.y + message.h
pulp.__fn_window(optx - 2, opty - 1, optw + 3, opth + 2)
local y = 0
for i = message.firstopt,#message.options do
if y >= opth then
break
end
local option = message.options[i]
local text = option.text
if #text > optw then
text = substr(text, 1, optw)
end
pulp.__fn_label(optx, opty + y, nil, nil, text)
y += 1
end
-- cursor
pulp.pipe_img[submenu and 13 or 12]:draw((optx - 1) * GRIDX, (opty + message.optselect - message.firstopt) * GRIDY)
if #message.options > opth then
-- page icon
pulp.pipe_img[14]:draw(GRIDX * (optx + optw - 1), GRIDY * (opty + opth))
end
end
end
local function readInput()
local a = playdate.buttonIsPressed( playdate.kButtonA )
local b = playdate.buttonIsPressed( playdate.kButtonB )
local up = playdate.buttonIsPressed( playdate.kButtonUp )
local down = playdate.buttonIsPressed( playdate.kButtonDown )
local left = playdate.buttonIsPressed( playdate.kButtonLeft )
local right = playdate.buttonIsPressed( playdate.kButtonRight )
-- FIXME: ensure this is correct
if up and down then
up = false
down = false
end
if left and right then
left = false
right = false
end
local a_pressed = a and not prev_a
local b_pressed = b and not prev_b
local up_pressed = up and not prev_up
local down_pressed = down and not prev_down
local left_pressed = left and not prev_left
local right_pressed = right and not prev_right
if config.inputRepeat == 1 then
local now = playdate.getCurrentTimeMilliseconds()
local next_first = now + config.inputRepeatDelay * 1000
local next_repeat = now + config.inputRepeatBetween * 1000
if a_pressed then
a_press_time = next_first
end
if b_pressed then
b_press_time = next_first
end
if up_pressed then
up_press_time = next_first
end
if down_pressed then
down_press_time = next_first
end
if left_pressed then
left_press_time = next_first
end
if right_pressed then
right_press_time = next_first
end
if a and a_press_time and now >= a_press_time then
a_press_time = next_repeat
a_pressed = true
end
if b and b_press_time and now >= b_press_time then
b_press_time = next_repeat
b_pressed = true
end
if up and up_press_time and now >= up_press_time then
up_press_time = next_repeat
up_pressed = true
end
if down and down_press_time and now >= down_press_time then
down_press_time = next_repeat
down_pressed = true
end
if left and left_press_time and now >= left_press_time then
left_press_time = next_repeat
left_pressed = true
end
if right and right_press_time and now >= right_press_time then
right_press_time = next_repeat
right_pressed = true
end
else
a_press_time = false
b_press_time = false
up_press_time = false
down_press_time = false
left_press_time = false
right_press_time = false
end
if pulp.message then
updateMessage(up_pressed, down_pressed, left_pressed, right_pressed, a_pressed, b_pressed)
else
local player = pulp.player
if up_pressed or down_pressed or left_pressed or right_pressed then
event_persist.dx = 0
event_persist.dy = 0
if up_pressed then
event_persist.dy = -1
elseif down_pressed then
event_persist.dy = 1
elseif left_pressed then
event_persist.dx = -1
elseif right_pressed then
event_persist.dx = 1
end
end
local tx = max(0, min(player.x + event_persist.dx, TILESW-1))
local ty = max(0, min(player.y + event_persist.dy, TILESH-1))
event_persist.tx = tx
event_persist.ty = ty
local playerScript = pulp:getPlayerScript()
if pulp.listen then
if a_pressed and playerScript then
(playerScript.confirm or playerScript.any)(playerScript, pulp.player, event_persist:new(), "confirm")
end
if b_pressed and playerScript then
(playerScript.cancel or playerScript.any)(playerScript, pulp.player, event_persist:new(), "cancel")
end
local do_exit = false
if pulp.listen then
-- check for exits
-- TODO: only do this if player has moved since previous frame
local x = player.x
local y = player.y
if x ~= disabled_exit_x or y ~= disabled_exit_y then
disabled_exit_x = nil
disabled_exit_y = nil
end
for i = 1,#__exits do
local exit = __exits[i]
if exit.edge == nil and (x ~= disabled_exit_x or y ~= disabled_exit_y) then
do_exit = x == exit.x and y == exit.y
elseif exit.edge == 1 then
do_exit = x == exit.x and event_persist.dx > 0
elseif exit.edge == 2 then
do_exit = y == exit.y and event_persist.dy > 0
elseif exit.edge == 3 then
do_exit = x == exit.x and event_persist.dx < 0
elseif exit.edge == 0 or exit.edge == 4 then
do_exit = y == exit.y and event_persist.dy < 0
end
if do_exit then
if exit.fin then
pulp.__fn_fin(exit.fin)
else
if exit.edge == nil then
pulp.__fn_goto(exit.tx, exit.ty, exit.room)
elseif exit.edge == 1 or exit.edge == 3 then
pulp.__fn_goto(exit.tx, y, exit.room)
elseif exit.edge == 2 or exit.edge == 4 or exit.edge == 0 then
pulp.__fn_goto(x, exit.ty, exit.room)
end
end
break
end
end
-- move, and check for interactions and collections
if (up_pressed or down_pressed or left_pressed or right_pressed) then
if not do_exit then
local ttile = roomtiles[ty][tx]
if ttile then -- paranoia
if not ttile.solid then
player.x = tx
player.y = ty
else
if playerScript then
(playerScript.bump or playerScript.any)(playerScript, player, event_persist:new(), "bump")
end
end
local _type = ttile.ttype
local _script = ttile.script or {}
if _type == TTYPE_SPRITE and config.autoAct then
(_script.interact or _script.any or default_event_interact)(ttile.script, ttile, event_persist:new(), "interact")
elseif _type == TTYPE_ITEM then
(_script.collect or _script.any or default_event_collect)(ttile.script, ttile, event_persist:new(), "collect")
end
end
end
if playerScript then
(playerScript.update or playerScript.any)(playerScript, player, event_persist:new(), "update")
end
end
end
end
end
prev_a = a
prev_b = b
prev_up = up
prev_down = down
prev_left = left
prev_right = right
end
function playdate.update()
if not pulp.game_is_loaded then
return
end
if pulp.restart then
pulp:savestore()
pulp.resetvars()
pulp:start()
return
end
if pulp.roomQueued then
pulp:exitRoom()
if pulp.roomQueuedX and pulp.roomQueuedY then
pulp.player.x = pulp.roomQueuedX
pulp.player.y = pulp.roomQueuedY
end
pulp:enterRoom(pulp.roomQueued)
end
readAccelerometer()
readInput() -- (and do player physics)
local timers_activate = {}
if not pulp.message then
for i, timer in pairs(pulp.timers) do
timer.duration -= SPF
if timer.duration <= 0 then
pulp.timers[i] = nil
timers_activate[#timers_activate+1] = timer
end
end
(pulp.gameScript.loop or pulp.gameScript.any)(pulp.gameScript, pulp.game, event_persist:new(), "loop")
end
playdate.display.setInverted(pulp.invert)
-- precompute tile indices by (frame rate, frame count)
for fps, framecs in pairs(pulp.tile_fps_lookup) do
local s = SPF
for i, framec in pairs(framecs) do
framecs[i] = framec + fps * SPF
if framecs[i] >= i then
framecs[i] -= i
end
pulp_tile_fps_lookup_floor[pulp_tile_fps_lookup_floor_lookup[fps][i]] = floor(framecs[i])
end
end
local scrolly = 0
local scrollx = 0
local scroll = false
if config.follow ~= 0 then
scrollx = config.followCenterX - pulp.player.x
scrolly = config.followCenterY - pulp.player.y
if scrolly == 0 and scrollx == 0 then
scroll = false
else
scroll = true
end
end
local framei = nil
-- update tile frames and draw tiles
-- WARNING: DUPLICATE CODE! Yes, yes, but it's efficient, and this loop is hot, oh boy.
if scroll then
for x = cropl,cropr do
for y = cropu,cropd do
if y - scrolly >= TILESH or y - scrolly < 0 or x - scrollx >= TILESW or x - scrollx < 0 then
-- skip this one
else
local tilei = roomtiles[y - scrolly][x - scrollx]
local dsttilei = roomtiles[y][x]
local frames = tilei.frames
if tilei.play then -- [[CAN STATICALLY OPTIMIZE OUT]]
framei = floor(tilei.frame)
if framei < #frames then
tilei.frame += SPF * tilei.fps
elseif tilei.play_block then
timers_activate[#timers_activate+1] = {
block = tilei.play_block,
self = tilei.play_self,
event = tilei.play_event,
evname = tilei.play_evname,
actor = tilei.play_actor,
}
tilei.play_block = nil
framei = #frames - 1
end
elseif tilei.fps > 0 then -- [[CAN STATICALLY OPTIMIZE OUT]]
framei = pulp_tile_fps_lookup_floor[tilei.fps_lookup_idx]
tilei.frame = framei
else
framei = tilei.frame
end
-- checks if changed
local frame = frames[framei + 1] or frames[1] or 1
if dsttilei.prev_frame ~= frame then
dsttilei.prev_frame = frame
tilemap:setTileAtPosition(x+1, y+1, frame)
end
end
end
end
else
-- no scrolling
for x = cropl,cropr do
for y = cropu,cropd do
local tilei = roomtiles[y][x]
local frames = tilei.tile.frames
if tilei.play then -- [[CAN STATICALLY OPTIMIZE OUT]]
framei = floor(tilei.frame)
if framei < #frames then
tilei.frame += SPF * tilei.fps
elseif tilei.play_block then
timers_activate[#timers_activate+1] = {
block = tilei.play_block,
self = tilei.play_self,
event = tilei.play_event,
evname = tilei.play_evname,
actor = tilei.play_actor,
}
tilei.play_block = nil
framei = #frames - 1
end
elseif tilei.fps > 0 then
framei = pulp_tile_fps_lookup_floor[tilei.fps_lookup_idx]
tilei.frame = framei
else
framei = tilei.frame
end
-- checks if changed
local frame = frames[framei + 1] or frames[1] or 1
if tilei.prev_frame ~= frame then
tilei.prev_frame = frame
tilemap:setTileAtPosition(x+1, y+1, frame)
end
end
end
end
if iscropped or scroll then
-- draw cropping borders
-- FIXME: more efficient implementation of this.