-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmonster-cycle.p8
3353 lines (2982 loc) · 101 KB
/
monster-cycle.p8
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
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
--monster cycle(v1.1.0)
--by nandbolt
--game state
gstate=0
gtime=0 --game time (steps)
ghigh=0 --high score (steps)
hard_mode=false
beat_game=false
--camera
camx=0 --camera x
camy=0 --camera y
camaccel=1 --camera acceleration
--spawner
spnrx=0 --spawner x
spnry=0 --spawner y
spnr_cnt=0 --spawn counter
--collisions
walls={}
for i=1,16 do
walls[i]=i+111
end
tombs={10,11,12,47,63}
beds={26,27,28,29,42,44,58,60}
water={87,77,78,79}
--menus
mmtimer=0
mmx=0
mmy=0
mmstart=false
mmomega=0.002778
tcols={0,1,2,8}
--delay
delay=0
--fade
fade=0
--fog
fogtimer=0
fogfreq=30
cfog={1,5,13}
--target indicator
ctarg={7}
--spirit plane
spat={█,█,█,6730.5,▒,▒,░}
csoff=1 --current spirit offset
gsoff=1 --goal spirit offset
spc=1 --spirit plane color
--help
tips={
"🐱sPIRITS ARE INVINCIBLE\nWHILE ∧DASHING.",
"😐zOMBIE BREATH GOES\nTHROUGH WALLS.",
"☉mONSTERS FLEE WHEN THEIR\n★ABILITIES ARE ON COOLDOWN.",
"웃hUMANS CAN EITHER HAVE\nA PISTOL OR KNIFE.",
"🐱sPIRITS NEED 3 xp\nTO BECOME UNDEAD.",
"웃hUMANS NEED 16 xp\nTO ASCEND.",
"bEWARE OF RAINBOW ☉MONSTERS!\ntHEY CAN ✽INSTAKILL.",
"tHERES A 5% CHANCE A MONSTER\nDROPS A ♥MAXHP+.",
"tHERES A 5% CHANCE A MONSTER\nDROPS A ●RAINBOW MONSTROSITY.",
"tHERES A 25% CHANCE A MONSTER\nDROPS A ♥HP+.",
"🐱wRAITHS DEAL 2 DAMAGE\nWITH THEIR ●BLAST.",
"😐zOMBIES DEAL CONTACT\nDAMAGE TO 웃HUMANS.",
"∧wATER SLOWS DOWN ALL\nBUT 🐱SPIRITS.",
"wHAT YOU CAN'T ☉SEE MAY\nSTILL EXIST.",
"ˇdON'T GIVE UP!ˇ",
"tHERE ARE 3 MONSTER TIERS:\n🐱SPIRITS 😐UNDEAD 웃HUMANS",
"🐱sPIRITS HAVE NO TRAIL WHEN\n★ABILITIES ARE ON COOLDOWN.",
"웃hUMANS AND 😐UNDEAD\nBLEED WHEN HURT.",
"oTHER ☉MONSTERS AIM\nWHERE THEY'RE FACING.",
}
--init
function _init()
--update fog
cfog[1]=spc
tcols[2]=spc
--clear pools
ghosts={}
wraiths={}
zombies={}
skeletons={}
humans={}
projs={}
ps={}
ps2={}
collectables={}
--generate tip
tip=tips[irnd(1,#tips)]
--not menu state
if gstate==gst_menu then
mmx=irnd(0,112)
mmy=irnd(0,48)
pmusic(40)
cartdata(0)
load_highscore(0)
beat_game=ghigh>0
else
--load mode high
if hard_mode then
load_highscore(1)
else
load_highscore(0)
end
--clear run
gtime=0
--reset fade
fade=30
--preprocess enemies
for i=1,4 do
make_ghost(irnd(0,mw),
irnd(0,mw),false)
make_wraith(irnd(0,mw),
irnd(0,mw),false)
end
--zombies
make_zombie(468,404,false) --small grave
make_zombie(940,300,false) --large grave1
make_zombie(940,204,false) --large grave2
make_zombie(836,252,false) --large grave3
make_zombie(268,436,false) --beach
make_zombie(964,444,false) --forest
make_zombie(300,332,false) --fountain
make_zombie(740,252,false) --river
--skeletons
make_skeleton(468,372,false) --small grave
make_skeleton(868,252,false) --large grave
make_skeleton(828,44,false) --rocks
make_skeleton(420,100,false) --hedges
--humans
make_human(132,84,false) --hotel
make_human(924,84,false) --digger
make_human(508,252,false) --church1
make_human(588,244,false) --church2
make_human(84,436,false) --beach
make_human(740,468,false) --forest
--add player
update_spawnpoint(false)
if half_chance() then
make_ghost(spnrx,spnry,true)
else
make_wraith(spnrx,spnry,true)
end
player.iframes=90
end
end
--main update
function _update()
--fog
update_fog()
--lerp spirit offset
csoff=lerp(csoff,gsoff,0.1)
--update particles
foreach(ps,update_p)
foreach(ps2,update_p)
--menu game state
if gstate==gst_menu then
--main menu
if mmstart then
if fade<=0 then
gstate=gst_help
mmomega=0.002778
elseif delay<=0 then
fade-=1
else
delay-=1
end
mmomega=lerp(mmomega,0,0.1)
elseif btnp(5) or
(beat_game and btnp(4)) then
mmstart=true
fade=30
delay=30
pmusic(-1) --stop music
if beat_game and btnp(4) then
--hard mode
hard_mode=true
spc=2
sfx(19)
else
sfx(1)
end
end
elseif gstate==gst_help then
--help menu
if btnp(5) then
gstate=gst_active
pmusic(44)
_init()
sfx(1)
end
else
--update spawner
update_spawner()
--update pools
foreach(ghosts,update_ghost)
foreach(wraiths,update_wraith)
foreach(zombies,update_zombie)
foreach(skeletons,update_skeleton)
foreach(humans,update_human)
foreach(projs,update_proj)
foreach(collectables,update_collectable)
--update camera
camx=lerp(camx,player.x-hss,
camaccel)
camx=clamp(camx,0,896)
camy=lerp(camy,player.y-hss,
camaccel)
camy=clamp(camy,0,384)
camera(camx,camy)
--dead state
if gstate==gst_dead or
gstate==gst_complete then
if mmstart then
if fade<=0 then
if gstate==gst_complete then
pmusic(44)
gsoff=1
end
gstate=gst_active
_init()
else
fade-=1
end
elseif btnp(5) then
mmstart=true
fade=30
sfx(1)
end
--active state
else
--timer
gtime+=1
--fade
if (fade>0) fade-=1
--player sounds
if (player.oactionp or
player.xactionp) and
player.meter<=0 then
sfx(2)
end
end
end
end
--main draw
function _draw()
--clear
cls()
--menu state
if gstate==gst_menu then
draw_mainmenu()
elseif gstate==gst_help then
draw_helpmenu()
else
--draw tiles
map(0,0,0,0,mw,mh)
--spirit plane
draw_spirit_plane()
--ground
foreach(ps,draw_p)
foreach(zombies,draw_zombie)
foreach(skeletons,draw_skeleton)
foreach(humans,draw_human)
foreach(projs,draw_proj)
foreach(collectables,draw_collectable)
--spirits
foreach(ghosts,draw_ghost)
foreach(wraiths,draw_wraith)
--sky
foreach(ps2,draw_p2)
--active state
if gstate==gst_active then
draw_hud()
draw_targ_line()
--death menu
elseif gstate==gst_dead then
local xx,yy=camx+8,camy+33
--death prompt
shdwprint("mORE THAN DEATH.",
xx,yy,8)
--tip
yy+=24
if not hard_mode then
shdwprint("*tIP*\n"..tip,
xx,yy,7)
end
--restart prompt
yy+=40
shdwprint("❎/x TO RETRY",
xx,yy,10)
--victory menu
elseif gstate==gst_complete then
local xx,yy=camx+33,camy+17
mmtimer+=1
--victory prompt
tprint("monster ascended.",
xx,yy)
--time
yy+=40
local seconds=flr(gtime/30)
local c,nh=7,""
if gtime==ghigh then
c=10
nh=" (new high!)"
if not beat_game then
nh=nh.."\n\n☉hARD MODE UNLOCKED,\nRESET CART TO PLAY."
end
end
shdwprint("⧗tIME "..seconds..nh,
xx,yy,c)
--restart prompt
yy+=40
shdwprint("❎/x TO PLAY AGAIN",
xx,yy,7)
end
--fade
if gstate==gst_active then
if (fade>0) draw_fadein()
else
if (mmstart) draw_fadeout()
end
end
end
-->8
--math
--constants
mw=1024 --map width
hmw=512 --half map width
mh=512 --map height
hmh=256 --half map height
ss=128 --screen size
hss=64 --half screen size
ts=8 --tile size
hts=4 --half tile size
rt2o2=0.7071 --sqrt(2)/2
st_wander=0 --wander state
st_fight=1 --fight state
st_flee=2 --flee state
gst_menu=0 --menu game state
gst_help=1 --help menu state
gst_active=2 --active game state
gst_dead=3 --dead game state
gst_complete=4 --complete game state
--returns vector2 length
function get_vec_len(x,y)
return sqrt(x*x+y*y)
end
--returns distance between two points
function get_dist(x1,y1,x2,y2)
return get_vec_len(x2-x1,y2-y1)
end
--returns manhattan distance
function get_mdist(x1,y1,x2,y2)
return abs(x1-x2)+abs(y1-y2)
end
--lerp
function lerp(a,b,t)
return a+t*(b-a)
end
--random integer
function irnd(low,high)
return flr(rnd(high-low+1)+low)
end
--random range
function rnd_range(amp)
return rnd(amp*2)-amp
end
--clamp
function clamp(value,low,high)
if (value<low) return low
if (value>high) return high
return value
end
--point collision check
function point_in_box(x,y,x1,y1,x2,y2)
if (x>=x1 and x<=x2 and
y>=y1 and y<=y2) then
return true
end
return false
end
--returns if item is in table
function in_table(tbl,item)
for i=1,#tbl do
if (tbl[i]==item) return true
end
return false
end
--point in view
function point_in_view(x,y)
if x>camx and x<camx+ss and
y>camy and y<camy+ss then
return true
end
return false
end
--check rectangle intersection
function rect_intersect(ax1,ay1,ax2,ay2,bx1,by1,bx2,by2)
--zero area
if (ax1==ax2 or ay1==ay2 or --zero area
bx1==bx2 or by1==by2 or
ax1>bx2 or ax2<bx1 or --horizontal check
ay1>by2 or ay2<by1) then --vertical
return false
end
return true
end
--normalize direction vector
function normalize_dir(a)
local dx,dy=normalize(a.dx,
a.dy)
a.dx=dx
a.dy=dy
end
--normalize vector
function normalize(x,y)
if (x==0 and y==0) return 0,0
local ang=atan2(x,y)
return cos(ang),sin(ang)
end
--50/50 chance
function half_chance()
return chance(0.5)
end
--percent chance
function chance(v)
return rnd(1)<v
end
--sine wave
-- omega:frequency
-- x:input
-- phase:angle offset
-- amp:amplitude
function swave(omega,x,phase,amp)
return amp*sin(omega*x+phase)
end
--cosine wave
-- omega:frequency
-- x:input
-- phase:angle offset
-- amp:amplitude
function cwave(omega,x,phase,amp)
return amp*cos(omega*x+phase)
end
-->8
--ghost
--creates a ghost and adds it
--to the list of ghosts
function make_ghost(x,y,is_player)
local ghost={}
--actor
init_actor(ghost,x,y,is_player)
--states
ghost.ethereal=true
ghost.tier=1
ghost.ascenders=tombs
ghost.targs={ghosts,wraiths}
--trail
init_trail(ghost,12)
--dash
init_dash(ghost,3,1,9)
--blaster
init_ghost_blaster(ghost)
--player or npc
if is_player then
ghost.update_input=update_player_input
ghost.goal=" gHOST\n FIGHT SPIRITS✽\n ASCEND TO UNDEAD웃"
ghost.oprompt="🅾️/z dASH"
ghost.xprompt="❎/x sHOOT"
player=ghost
else
init_actor_npc(ghost)
ghost.wander=ghost_wander
ghost.fight=ghost_fight
ghost.flee=ghost_flee
end
--health
set_maxhp(ghost,2)
--add to list
ghost.pool=ghosts
add(ghost.pool,ghost)
return ghost
end
--updates ghost logic
function update_ghost(ghost)
--update input/abilities
ghost.update_input(ghost)
update_dash(ghost)
update_blaster(ghost)
update_trail(ghost)
--move and collide
move_and_collide(ghost)
--check ascendance
check_ascend(ghost)
end
--renders the ghost
function draw_ghost(ghost)
if player.tier==1 then
draw_actor(ghost)
end
end
--init ghost blaster
function init_ghost_blaster(ghost)
init_blaster(ghost)
ghost.pc1=12
ghost.pc2=12
ghost.paccel=0.05
ghost.pburst=1
ghost.psize=1
ghost.pethereal=true
ghost.sfxxaction=11
end
--enter ghost wander state
function ghost_wander(ghost)
enter_wander_state(ghost)
end
--enter ghost fight state
function ghost_fight(ghost)
enter_fight_state(ghost)
--choose action
if half_chance() then
ghost.oaction=true
else
ghost.xactionp=true
end
end
--enter ghost flee state
function ghost_flee(ghost)
enter_flee_state(ghost)
end
-->8
--particles
--init particle
function init_p(p,x,y,c)
p.x,p.y=x,y
p.vx,p.vy=0,0
p.life=10
p.c=c
p.sys=ps
end
--add particle (low)
function add_p(x,y,c)
local p={}
init_p(p,x,y,c)
add(ps,p)
end
--add particle (high)
function add_p2(x,y,c)
local p={}
init_p(p,x,y,c)
p.vx,p.vy=rnd_range(0.25),rnd_range(0.25)
p.life=90
p.sys=ps2
add(ps2,p)
end
--update particle
function update_p(p)
if (p.life<=0) then
--destroy particle
del(p.sys,p)
else
--update position
p.x+=p.vx
p.y+=p.vy
--update life
p.life-=1
end
end
--draw particle
function draw_p(p)
if p.life>5 then
circfill(p.x,p.y,1,p.c)
else
pset(p.x,p.y,p.c)
end
end
--draw particle
function draw_p2(p)
local r=(0.49*sin((90-p.life)/90+0.25)+0.5)*4
if p.life>15 then
circfill(p.x-1,p.y,r,6)
circfill(p.x,p.y+1,r-1,p.c)
end
if p.life>5 and p.life<80 then
circfill(p.x+1,p.y+3,r,6)
circfill(p.x+2,p.y+4,r-1,p.c)
end
if p.life>10 and p.life<85 then
circfill(p.x-3,p.y+3,r,6)
circfill(p.x-1,p.y+4,r-1,p.c)
end
end
-->8
--zombie
--creates a zombie and adds it
--to the list of ghosts
function make_zombie(x,y,is_player)
local zombie={}
--actor
init_actor(zombie,x,y,is_player)
--state
zombie.tier=2
zombie.ascenders=beds
zombie.targs={humans}
--movement
zombie.accel=0.05
--trail
init_trail(zombie,11)
--sprite
zombie.rsprs={33,49}
zombie.dsprs={34,50}
zombie.dgsprs={35,51}
zombie.sprs=zombie.rsprs
--dash
init_run(zombie,4,14)
--blaster
init_zombie_blaster(zombie)
--player or npc
if is_player then
zombie.update_input=update_player_input
zombie.goal=" zOMBIE\n EAT HUMANS웃\n ASCEND TO HUMAN♥"
zombie.oprompt="🅾️/z cHARGE"
zombie.xprompt="❎/x bREATHE"
player=zombie
else
init_actor_npc(zombie)
zombie.wander=zombie_wander
zombie.fight=zombie_fight
zombie.flee=zombie_flee
end
--add to list
zombie.pool=zombies
add(zombie.pool,zombie)
return zombie
end
--updates zombie logic
function update_zombie(zombie)
--get input
zombie.update_input(zombie)
update_run(zombie)
update_blaster(zombie)
update_trail(zombie)
--move and collide
move_and_collide(zombie)
--check ascendance
check_ascend(zombie)
end
--renders the zombie
function draw_zombie(zombie)
if player.tier!=1 then
draw_actor(zombie)
end
end
--init zombie blaster
function init_zombie_blaster(zombie)
init_blaster(zombie)
zombie.pc1=3
zombie.pc2=11
zombie.paccel=0.05
zombie.pburst=1
zombie.precoil=0
zombie.psize=2
zombie.pspd=0
zombie.plife=30
zombie.pcost=30
zombie.pethereal=true
zombie.pkstr=0
zombie.pdmg=2
zombie.pdraw=draw_zombie_proj
zombie.sfxxaction=sfx_z0mbiebreath
end
--enter zombie wander state
function zombie_wander(zombie)
enter_wander_state(zombie)
end
--enter zombie fight state
function zombie_fight(zombie)
enter_fight_state(zombie)
--choose action
if half_chance() then
zombie.oaction=true
else
zombie.xactionp=true
end
end
--enter zombie flee state
function zombie_flee(zombie)
enter_flee_state(zombie)
end
-->8
--actor
--init actor
function init_actor(a,x,y,is_player)
--dimensions
a.bbhw=3 --bounding box half width
a.bbhh=3 --bounding box half height
a.xfacing=1 --x facing direction
a.yfacing=0 --y facing direction
a.inview=false --in camera view
a.msty=false --monstrosity
--movement
a.x=x --x position
a.y=y --y position
a.vx=0 --x velocity
a.vy=0 --y velocity
a.spd=0 --speed (for queries)
a.maxspd=2 --current max move speed
a.normspd=2 --normal max move speed
a.accel=0.1 --move acceleration
a.ethereal=false --ethereal=no wall collisions
a.bounce=false --bounce on walls
--sprite
a.rsprs={1} --right sprites
a.dsprs={2} --down sprites
a.dgsprs={3} --diagonal sprites
a.sprs=a.rsprs --current sprites
a.spridx=1 --current sprite index
a.animspd=1 --animation speed (frames per second)
a.animcnt=0 --animation counter
a.flipx=false --sprite flip x
a.flipy=false --sprite flip y
--sfx
a.sfxoaction=0
a.sfxxaction=0
--tier
a.tier=1
a.targs={}
--health
set_maxhp(a,3)
a.iframes=30 --invincibility frames
--xp
a.maxxp=3
a.xp=0
--meter
a.maxmeter=90
a.meter=90
--cooldown
a.maxcooldwn=90
a.cooldwn=90
--input
a.dx=0 --x direction
a.dy=0 --y direction
a.oaction=false --🅾️ action
a.xaction=false --❎ action
a.oactionp=false --🅾️ action pressed
a.xactionp=false --❎ action pressed
end
--inits vars for npc actor
function init_actor_npc(a)
a.update_input=npc_input
a.targ=nil --target
a.tx=a.x --target x position
a.ty=a.y --target y position
a.mstate=st_wander --mental state
a.mcnt=irnd(0,29) --mental counter
a.tradius=48 --target detection radius
--states
a.wander=enter_wander_state
a.fight=enter_fight_state
a.flee=enter_flee_state
end
--updates player actor input
function update_player_input()
--move input
player.dx=tonum(btn(1))-
tonum(btn(0))
player.dy=tonum(btn(3))-
tonum(btn(2))
normalize_dir(player)
--action inputs
player.oaction=btn(4)
player.xaction=btn(5)
player.oactionp=btnp(4)
player.xactionp=btnp(5)
end
--moves the actor and handles
--collisions
function move_and_collide(a)
local collision=false
local maxspd=a.maxspd
if not a.ethereal and
in_table(water,mget(a.x/ts,a.y/ts)) then
maxspd*=0.25
if a.dx!=0 or a.dy!=0 then
if a.inview then
local sfreq=clamp(flr(20-a.spd*10),1,10)
if (gtime%sfreq==0) sfx(18)
add_p(a.x+rnd_range(4),
a.y+rnd_range(4),1)
end
end
end
--update velocity
a.vx=lerp(a.vx,
a.dx*maxspd,
a.accel)
a.vy=lerp(a.vy,
a.dy*maxspd,
a.accel)
--handle x collision
local bb=a.x-a.bbhw
if(a.vx>0)bb=a.x+a.bbhw
local tx=(bb+a.vx)/ts
local ty=a.y/ts
if not a.ethereal and
in_table(walls,mget(tx,ty)) then
--x collision
collision=true
if (a.bounce) then
a.vx*=-1
tx=(bb+a.vx)/ts
if in_table(walls,mget(tx,ty)) then
a.vx=0
end
else
a.vx=0
end
end
a.x=clamp(a.x+a.vx,hts,1020)
--handle y collision
bb=a.y-a.bbhh
if(a.vy>0)bb=a.y+a.bbhh
tx=a.x/ts
ty=(bb+a.vy)/ts
if not a.ethereal and
in_table(walls,mget(tx,ty)) then
--y collision
collision=true
if (a.bounce) then
a.vy*=-1
ty=(bb+a.vy)/ts
if in_table(walls,mget(tx,ty)) then
a.vy=0
end
else
a.vy=0
end
end
a.y=clamp(a.y+a.vy,hts,508)
--update speed
a.spd=get_spd(a)
--update view
a.inview=point_in_view(a.x,a.y)
--iframes
if a.iframes!=nil and
not dmgable(a) then
a.iframes-=1
end
return collision
end
--checks actor collisions
-- return: actor or nil
function touching(a,pools)
--get bbox dimensions
local bbx1=a.x-a.bbhw
local bbx2=a.x+a.bbhw
local bby1=a.y-a.bbhh
local bby2=a.y+a.bbhh
--loop through target pool
for pool in all(pools) do
for oa in all(pool) do
--check rect intersection
if (oa!=a and
rect_intersect(bbx1,bby1,
bbx2,bby2,oa.x-oa.bbhw,
oa.y-oa.bbhh,oa.x+oa.bbhw,
oa.y+oa.bbhh)) then
return oa