-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathguncontrol.bmx
1614 lines (1392 loc) · 50.5 KB
/
guncontrol.bmx
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
Global firemodeMap:TMap = CreateMap()
MapInsert(firemodeMap, "0", "Automatic")
MapInsert(firemodeMap, "1", "Semi-automatic")
MapInsert(firemodeMap, "2", "Hold-and-release")
Type Gun
Field name$ 'what kind of gun is it. determines firing behavior
Field dispname$ 'the name to say it is
Field offsetx,offsety 'x,y offset at rot=0 that shots come from (scaled by shipscale)
Field icon 'the index pointer to weapon_gfx[] to use for an icon
Field shotdamage# 'the damage of shots
Field shotspeed# 'the speed of shots
Field shotrange# 'the range of the shots
Field freedom 'degrees of arc from center shots can fire.
Field fireDelay# 'min delay between bursts, in ms
Field fireDelay_timer# 'the actual timer
Field burstNum = 1 'the number of shots each burst
Field burstCount 'the counter for the number of shots left in the curret burst
Field burstDelay# = 250 'the ms between each shot in a burst
Field burstDelay_timer# 'the actual timer
Field burstX,burstY 'stores where it targeted
Field clip 'the current number of shots in the clip
Field clipNum 'the number of shots in each clip (0 = no clip)
Field reloadDelay # 'the time it takes to reload a new clip
Field reloadDelay_timer# 'the actual timer
Field mode ' 0 = automatic | 1 = semi-automatic | 2 = press-hold-release
Field state# ' tracks different things depending on the firing mode
' automatic : how long it's been held down
' semi-automatic : 0 = fired and mouse has not yet been released | 1 = ready to fire
' press-hold-release : % charge
Field value# 'a variable (O RLY) for each weapon to play with in their own way
Field chargetime# = 1000 'for automatic, press-hold-release weapons, this is the time (in ms) it takes for state to reach 1.0
Field cooltime# = 1000 'for automatic weapons, how long it takes to spin charge back down
'_s: ship that's doing the firing | _tarx,y: absolute coords we're aiming at | _release: for charge weapons, if the button was just released
Method fire(_s:Ship,_tarx,_tary,_release=False)
If reloadDelay_timer <= 0'can't do anything if reloading
'OK to fire: (firedelay timer done) AND (if semiautomatic, we didn't already fire)
If (fireDelay_timer <= 0) And (mode <> 1 Or state = 1)
'reset the fire timer (unless it's a press+hold weapon and we haven't released yet)
If (mode <> 2 Or _release)
'overcharged shots fire faster
If _s.overchargeOn
fireDelay_timer = fireDelay / _s.overcharge_mod
Else
fireDelay_timer = fireDelay
EndIf
EndIf
'find the current relative position of the gun
Local gy# = (offsetx*Sin(_s.rot+90) + offsety*Cos(_s.rot+90))*SHIPSCALE
Local gx# = (-offsety*Sin(_s.rot+90) + offsetx*Cos(_s.rot+90))*SHIPSCALE
'find relative angle, dist to target
Local dx = _tarx-(_s.x+gx)
Local dy = _tary-(_s.y+gy)
Local tar_theta# = ATan2(dy,dx)
Local tar_dist# = approxDist(dx,dy)
'find the difference in shooting angle versus movement angle
Local v_theta#= tar_theta - _s.movrot
'now figure out how much we need to alter the angle of the shot to hit those coords
'the vector for how much the velocity is gonna alter the angle of the shot
Local v_perp# = _s.speed * Sin(v_theta)'for every timestep, the shot is going to go this far perpendicularly as a result of ship speed
'find the angle to modify the shot
Local theta_mod# = ATan2(v_perp,shotspeed)
'now modify the angle of shot to correct for the movement on the ship's effect on the shot
tar_theta:+ theta_mod
'keep within bounds
tar_theta = constrain(convert_to_relative(tar_theta, _s.rot), -freedom/2, freedom/2)
tar_theta:+ _s.rot'(convert back to absolute)
'ai has a hard time with a straight shot
If _s <> p1 Then tar_theta:+ Rand(-2,2)
'what kind of gun is firing?
Select name
'fire a bullet
Case "plasma","plasma_burst","suppressor"
Local s:Shot = add_shot(_s,name,shot2_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
s.trailRGB[0] = 255
s.trailRGB[1] = 200
s.trailRGB[2] = 120
playSFX(laserfire1_sfx,_s.x,_s.y)
'plasma guns build up a charge if you don't shoot them for a while
If name = "plasma"
'value field is used for charge amount
If value >= 1
burstNum = 3
ElseIf value >= .6
burstNum = 2
Else
burstNum = 1
EndIf
value = 0
EndIf
'suppressed fire gets really inaccurate as time goes on
If name = "suppressor" Then s.movrot:+ Rand(-(state+.3)*freedom/4,(state+.3)*freedom/4)
'fire a bullet
Case "machinegun"
Local s:Shot = add_shot(_s,name,shot3_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
s.hit_gfx = explode4_gfx
s.hit_sfx = Null
s.trail = False
playSFX(autocannonfire_sfx,_s.x,_s.y)
'fire a bullet
Case "autocannon","autocannon_burst"
Local s:Shot = add_shot(_s,name,explode4_gfx, _s.x+gx+Rand(-5,5), _s.y+gy+Rand(-5,5), tar_theta+Rand(-5,5), shotspeed, shotrange, shotdamage)
s.scale = constrain(.7 * (shotdamage / .5), .7, 1.3)
s.hit_gfx = explode4_gfx
s.hit_sfx = bullethit_sfx
's.trail_override_gfx = explode4_gfx
's.trail_animated = False
's.trail_scale = .5
playSFX(autocannonfire_sfx,_s.x,_s.y)
'close-range goodness
Case "shotgun"
Local shot = 15
For Local i = 1 To shot
Local spread# = Rand(-15,15)
Local s:Shot = add_shot(_s,name,explode4_gfx, _s.x+gx, _s.y+gy, tar_theta+spread, shotspeed+(4*(RndFloat()-.5)), (RndFloat()+.5)*shotrange, shotdamage/shot)
s.animated = False
's.trail = False
s.hit_sfx = bullethit_sfx
s.trail_override_gfx = trail_gfx
s.trail_animated = False
s.trailRGB[0] = 200
s.trailRGB[1] = 80
s.trailRGB[2] = 80
s.scale = 2
s.RGB[0] = 200
s.RGB[1] = 80
s.RGB[2] = 80
Next
playSFX(autocannonfire_sfx,_s.x,_s.y)
'playSFX(shotgunfire_sfx,_s.x,_s.y)
'charge up, fire a penentrating shot
Case "velocitycannon"
Local het = ImageHeight(_s.gfx[0])/2
Local vx# = Cos(_s.movrot)*_s.speed
Local vy# = Sin(_s.movrot)*_s.speed
Local offx# = -Cos(_s.rot)*(het+20)' + vx
Local offy# = -Sin(_s.rot)*(het+20)' + vy
If _release'if we've released the button
state = Abs(state)
Local s:Shot = add_shot(_s,name,shot6_gfx, _s.x-offx, _s.y-offy, tar_theta, shotspeed, shotrange, shotdamage * (state^2))
s.scale = constrain(state, .3, .8)
s.animated = False
If state > .3
s.skipFortify = True
s.durable = True
s.animated = True
EndIf
If state >= 1
s.trailRGB[0] = 255
s.trailRGB[1] = 255
s.trailRGB[2] = 255
Else
s.trailRGB[0] = 180*state
s.trailRGB[1] = 180*state
s.trailRGB[2] = 255*state
EndIf
playSFX(vcannonfire_sfx,_s.x,_s.y)
Else'if we're holding down the button
'charge fx in front of the ship (AAA DISCLAIMER OF MY ETHICALNESS)
SetAlpha .3 + .1*globalFrame
'make it flash
If Not (Abs(state) = 1 Or globalFrame) Then SetColor 255,255,255 Else SetColor 255,60,60
'draw them
SetBlend LIGHTBLEND
For Local i = 1 To 4
SetRotation (i*45)
SetScale Abs(state),Abs(state)
DrawImage warp2_gfx[globalFrame], _s.x-offx+game.camx, _s.y-offy+game.camy
Next
SetBlend ALPHABLEND
'make some particles
Local pspeed# = RndFloat()*2
Local prot# = Rand(0,359)
Local pvx# = Cos(prot)*pspeed
Local pvy# = Sin(prot)*pspeed
pspeed = approxDist(pvx+vx,pvy+vy)
prot = ATan2(pvy+vy,pvx+vx)
Local p:Background
p = add_particle(_s.x-offx+Cos(prot)*RndFloat()*10, _s.y-offy+Sin(prot)*RndFloat()*10, prot, pspeed, 880*Abs(state), True)
'make the particle flash the same way the charge does
If Not (Abs(state) = 1 Or globalFrame)
p.RGB[0] = 255
p.RGB[1] = 60
p.RGB[2] = 60
EndIf
EndIf
'charge up, release a lightning bolt
Case "arccaster"
Local het = ImageHeight(_s.gfx[0])/2
Local vx# = Cos(_s.movrot)*_s.speed
Local vy# = Sin(_s.movrot)*_s.speed
If _release'if we've released the button
state = Abs(state)
Local zapgfx:TImage[]
Local zapscale# = 1
If state > .6
zapgfx = lightning2_gfx
playSFX(lightning1_sfx,_s.x,_s.y)
zapscale:+ (state-.6)
ElseIf state > .3
zapgfx = lightning1_gfx
playSFX(lightning2_sfx,_s.x,_s.y)
zapscale:+ (state-.3)
Else
zapgfx = lightning3_gfx
playSFX(lightning2_sfx,_s.x,_s.y)
zapscale:+ state
EndIf
Local offx# = -Cos(_s.rot)*(het+zapscale*ImageHeight(zapgfx[0])/2)' + vx
Local offy# = -Sin(_s.rot)*(het+zapscale*ImageHeight(zapgfx[0])/2)' + vy
Local s:Shot = add_shot(_s, name, zapgfx, _s.x-offx, _s.y-offy, tar_theta, shotspeed, shotrange, shotdamage)
s.lifetimer = constrain(500*(state^2), 100, 500)
s.durable = True
s.trail = False
s.movrot = _s.rot
s.speed = 0
s.skipFortify = True
s.scale = zapscale
Else'if we're holding down the button
Local offx# = -Cos(_s.rot)*(het+20)' + vx
Local offy# = -Sin(_s.rot)*(het+20)' + vy
'charge fx in front of the ship (AAA DISCLAIMER OF MY ETHICALNESS)
SetAlpha .3 + .1*globalFrame
'make it flash
If Not (Abs(state) = 1 Or globalFrame) Then SetColor 255,255,255 Else SetColor 255,60,60
'draw them
SetBlend LIGHTBLEND
For Local i = 1 To 4
SetRotation (i*45)
SetScale Abs(state),Abs(state)
DrawImage warp2_gfx[globalFrame], _s.x-offx+game.camx, _s.y-offy+game.camy
Next
SetBlend ALPHABLEND
'make some particles
Local pspeed# = RndFloat()*2
Local prot# = Rand(0,359)
Local pvx# = Cos(prot)*pspeed
Local pvy# = Sin(prot)*pspeed
pspeed = approxDist(pvx+vx,pvy+vy)
prot = ATan2(pvy+vy,pvx+vx)
Local p:Background
p = add_particle(_s.x-offx+Cos(prot)*RndFloat()*10, _s.y-offy+Sin(prot)*RndFloat()*10, prot, pspeed, 880*Abs(state), True)
'make the particle flash the same way the charge does
If Not (Abs(state) = 1 Or globalFrame)
p.RGB[0] = 255
p.RGB[1] = 60
p.RGB[2] = 60
EndIf
EndIf
'fire a bullet
Case "peashooter","vectorshooter"
Local s:Shot = add_shot(_s,name,shot5_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
If name = "peashooter"
s.hit_gfx = explode4_gfx
ElseIf name = "vectorshooter"
s.gfx = vector_shot1_gfx
s.hit_gfx = vector_shot1_gfx
EndIf
s.hit_sfx = Null
's.rgb[0] = 255
's.rgb[1] = 20
's.rgb[2] = 20
s.trailRGB[0] = 255
s.trailRGB[1] = 200
s.trailRGB[2] = 120
playSFX(laserfire2_sfx,_s.x,_s.y)
'slow, powerful blast
Case "photon"
Local s:Shot = add_shot(_s,name,shot4_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
s.hit_gfx = explode5_gfx
s.trailRGB[0] = 255
s.trailRGB[1] = 200
s.trailRGB[2] = 120
s.trail_override_gfx = shot3_gfx
s.spin = .05
playSFX(laserfire1_sfx,_s.x,_s.y)
'launch a "missile" ship that tracks target
Case "launcher","torpedolauncher","swarmlauncher","mortarlauncher","nuke"
'if the conditions are right for this gun to fire
If (name = "torpedolauncher" Or name = "swarmlauncher") Or (_release And ((name = "mortarlauncher" Or name = "nuke") Or (Abs(state) = 1)))
Local vx# = Cos(_s.movrot)*_s.speed
Local vy# = Sin(_s.movrot)*_s.speed
Local missile:Ship = new_ship("Missile",_s.x+gx,_s.y+gy,"default")
missile.squad.faction = _s.squad.faction
missile.speedMax = shotspeed
'set the damage, size of the explosion
For Local c:Component = EachIn missile.compList
c.damageBonus = shotdamage
Next
missile.recalc()
missile.rot = _s.rot
missile.ignoreList.addLast(_s.placeholder)
missile.AI_timer = shotrange
vx:+ Cos(tar_theta)*200
vy:+ Sin(tar_theta)*200
missile.movrot = ATan2(vy,vx)
missile.speed = approxDist(vx,vy)
If missile.speed < 200 Then missile.speed = 200
'missile or torpedo?
Select name
Case "launcher","swarmlauncher"'homing
missile.movrot:+ 15*binsgn(clip Mod 2)'move sideways at first
If name = "swarmlauncher"
missile.gfx = missile2_gfx
missile.thrust = .3
_s.target_closest(_tarx,_tary)
EndIf
missile.target = _s.target
missile.squad.behavior = "missile"
missile.behavior = "missile"
Case "torpedolauncher","mortarlauncher","nuke"
missile.behavior = "torpedo"
missile.squad.behavior = "torpedo"
missile.squad.goal_x = _tarx
missile.squad.goal_y = _tary
missile.movrot = _s.rot
If name = "mortarlauncher" Or name = "nuke"
missile.gfx = mine_gfx
missile.scale = .7
missile.thrust = 0
missile.speed = Abs(state) * missile.speedMax
missile.AI_timer = Max((tar_dist / (missile.speed+1)) * 1000, 1300)
For Local c:Component = EachIn missile.compList
For Local g:Gun = EachIn c.gunList
g.fireDelay_timer = (missile.AI_timer-100)
Next
Next
EndIf
EndSelect
playSFX(missilefire_sfx,_s.x,_s.y)
ElseIf (name = "launcher" Or name = "swarmlauncher") And Not _release'if we're locking on
Local oldtarget:Gertrude = p1.target
'target the closest thing to the mouse
_s.target_closest(_tarx,_tary,False)
'have we just changed targets?
If _s.target <> oldtarget Then state = 0
EndIf
'if we tried to release with the launcher but we weren't quite locked on
If name = "launcher" And _release And Abs(state) < 1
fireDelay_timer = 0'reset the fireDelay timer
burstCount = 0'no bursting neither
burstDelay_timer = 0
state = 0
EndIf
'places a missile that slightly homes in on the target
Case "minelayer"
Local mine:Ship = new_ship("Mine",_s.x+gx,_s.y+gy,"default")
mine.squad.faction = _s.squad.faction
'set the damage, size of the explosion
For Local c:Component = EachIn mine.compList
c.damagebonus = shotdamage
Next
mine.recalc()
mine.rot = _s.rot
mine.ignoreList.addLast(_s.placeholder)
mine.AI_timer = shotrange
mine.squad.behavior = "mine"
mine.behavior = "mine"
mine.movrot = _s.movrot
mine.speed = _s.speed / 2
playSFX(missilefire_sfx,_s.x,_s.y)
'launch a grappling hook
Case "grappling"
Local l:Shot = add_shot(_s,name,hook_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
l.hit_gfx = Null
l.hit_sfx = Null
l.trail = False
l.animated = False
'remove self, make a ship that can only be shot off and saps the health of the target
Case "latch"
'if we have a target and it's not dead
If _s.target <> Null And Not _s.target.dead
'is the target within range?
Local dx = _s.target.x-_s.x
Local dy = _s.target.y-_s.y
If approxDist(dx,dy)
Local leech:Ship = new_ship("Leech",_s.target.x,_s.target.y,"default")
leech.squad.faction = _s.squad.faction
leech.ignoreShipCollisions = True
leech.target = _s.target
'find the master, initial offset
For Local t:Ship = EachIn entityList
If t.placeholder = _s.target
leech.master = t
Local tdist# = Sqr(dx^2+dy^2)
Local off_theta = (t.rot - tar_theta)
If off_theta < 360 Then off_theta:+ 360
If off_theta > 360 Then off_theta:- 360
leech.master_offx = Cos(off_theta)*tdist
leech.master_offy = Sin(off_theta)*tdist
leech.master_offrot = t.rot
EndIf
Next
EndIf
'remove thyself
RemoveLink(_s.link)
EndIf
'push self forward, doing damage
Case "lunge"
Local collide_gfx:TImage[2]
collide_gfx[0] = CreateImage(20,20)
collide_gfx[1] = CreateImage(20,20)
Local l:Shot = add_shot(_s,name,collide_gfx, _s.x+gx, _s.y+gy, 0, 0, 0, shotdamage)
l.hit_gfx = explode1_gfx
l.hit_sfx = Null
l.lifetimer = 160
l.animated = False
l.alpha = 1'transparent
'push the ship forward
_s.movrot = _s.rot
If _s.speed < _s.speedMax*2 Then _s.speed = _s.speedMax*2
'ship becomes untouchable
_s.ignoreCollisions = True
'create an explosion.
Case "explode"
Local explodeNum = 2 + Ceil(shotdamage / 6) * 3
For Local i = -Floor(explodeNum/2) To Floor(explodeNum/2) + 1
Local s:Shot = add_shot(_s,name,explode5_gfx, _s.x+gx, _s.y+gy, 0, 0, 0, shotdamage / (explodeNum+1))
ClearList(s.ignoreList)
s.hit_gfx = Null
s.hit_sfx = Null
s.trail = False
s.durable = True
s.scale = constrain((shotdamage / 26) * 3, .8, 22)
s.lifetimer = constrain((shotdamage / 6) * 400, 400, 1300)
'last one is stationary, bigger
If i = Floor(explodeNum/2)
s.movrot = 0
s.speed = 0
s.scale:*2.5
Else'others go out in a circle
s.speed = 150
s.movrot:+ (360/explodeNum)*i
EndIf
Next
'playSFX(missileexplode_sfx,_s.x,_s.y)
'a trail of acid that sticks around for a while
Case "acid"
'figure out which acid this is in the sequence (burstCount starts at zero, we need to bump it up)
Local acidCount# = burstCount
If acidCount = 0 Then acidCount = burstNum
'later parts of the burst slow down
Local acidspeed# = shotspeed * ((acidCount / Float(burstNum)) + .3)
Local s:Shot = add_shot(_s,name,shot_acid_gfx, _s.x+gx, _s.y+gy, tar_theta, acidspeed, shotrange*(RndFloat()+.5), shotdamage)
s.movrot:+ Rand(-15,15)
s.hit_gfx = Null
s.trail = False
s.spin = RndFloat()-.5
s.durable = True
s.scale = 1 + (RndFloat()-.5)
If Rand(0,1) Then s.blend = ALPHABLEND Else s.blend = LIGHTBLEND
'short-range spike, grappling hook that pulls apart ships
Case "barb"
Local s:Shot = add_shot(_s,name,barb_gfx, _s.x+gx, _s.y+gy, tar_theta, shotspeed, shotrange, shotdamage)
s.hit_gfx = Null
s.hit_sfx = Null
s.trail = False
s.animated = False
's.skipShields = True
s.skipFortify = True
'it's only able to fire once, make the tentacles
Case "tentacle"
For Local i = 0 To Rand(2,4)
Local tentacle_gfx:TImage[1]
tentacle_gfx[0] = zerg_anemone_t1_gfx[Rand(0,1)]
Local s:Shot = add_shot(_s,name, tentacle_gfx, _s.x, _s.y, 0, 0, 0, shotdamage*(RndFloat()+.5))
s.damage = shotdamage*(RndFloat()+.5)
s.movrot = Rand(0,359)
s.rot = s.movrot
s.durable = True
s.hit_gfx = Null
s.hit_sfx = Null
s.lifetimer = 10000
s.trail = False
s.blend = ALPHABLEND
s.skipShields = True
s.skipFortify = True
s.alpha = .3
s.animated = False
Next
'longer tentacles
For Local i = 0 To Rand(6,8)
Local tentacle_gfx:TImage[1]
tentacle_gfx[0] = zerg_anemone_t2_gfx[Rand(0,1)]
Local tent:Background = New Background
tent.gfx = tentacle_gfx
tent.animated = False
tent.x = _s.x
tent.y = _s.y
tent.rot = Rand(0,359)
tent.spin = (RndFloat()-.5)/5
tent.lifetimer = 0
tent.alpha = .4
tent.link = bgList.addLast(tent)
Next
'randomize starting direction of ship
_s.rot = Rand(0,359)
'pull things toward you, charge them up, and release to shoot them forward
Case "gravitygun"
'get how far mouse is from the ship
Local m_dx# = (cursorx - (p1.x+game.camx))
Local m_dy# = (cursory - (p1.y+game.camy))
Local m_dist# = constrain(Sqr(m_dy^2 + m_dx^2), 50, SWIDTH)
'where to pull things towards (a bit in front of the ship)
Local goffset# = (ImageHeight(_s.gfx[0])/2) + m_dist'150
Local gx# = _s.x + Cos(_s.rot)*goffset
Local gy# = _s.y + Sin(_s.rot)*goffset
'pull fx in front of the ship (AAA DISCLAIMER OF MY ETHICALNESS)
SetAlpha .15 + .1*globalFrame
SetScale 1,1
'make it flash
If Not globalFrame Then SetColor 155,100,100 Else SetColor 255,200,200
'draw them
SetBlend LIGHTBLEND
For Local i = 1 To 4
SetRotation (i*45)
DrawImage warp2_gfx[globalFrame], gx+game.camx, gy+game.camy
Next
SetAlpha .2
SetRotation _s.rot+90
SetScale 2,2
DrawImage warp2_gfx[globalFrame], gx+game.camx, gy+game.camy
SetBlend ALPHABLEND
'make some particles
add_particle(gx, gy, Rand(0,359), RndFloat()*3, 2000, True)
'make a list of things to affect
Local gList:TList = New TList
'only affect the closest ship
Local closest:Ship,closest_dist# = shotrange
For Local e:Ship = EachIn entityList
If e <> _s And Not e.ignorePhysics
Local dist = approxDist(e.x-gx,e.y-gy)
If dist < closest_dist
closest = e
closest_dist = dist
EndIf
EndIf
Next
If closest <> Null Then gList.addLast(closest)
'affect all shots, graphical effects
For Local e:Entity = EachIn shotList
If Not e.ignorePhysics Then gList.addLast(e)
Next
For Local e:Entity = EachIn debrisList
If Not e.ignorePhysics Then gList.addLast(e)
Next
For Local e:Entity = EachIn explodeList
If Not e.ignorePhysics Then gList.addLast(e)
Next
For Local e:Entity = EachIn itemList
If Not e.ignorePhysics Then gList.addLast(e)
Next
'go through all entities
For Local e:Entity = EachIn gList
'get distance to this entity
Local gdist# = approxDist(gx-e.x, gy-e.y)
'is it a shot?
Local isShot = (TTypeId.ForObject(e) = TTypeId.ForName("Shot"))
'charge 'em up
If Not _release
'if target is within range of the pulling effect
If gdist <= shotrange
'force is inverse to distance (and doesn't care about mass)
Local gforce# = shotdamage * e.mass * (1 - gdist/shotrange)
'and the ship we're playing with gets treated special
If e = closest Then gforce# = shotdamage * e.mass * (1 - (gdist^2 / shotrange^2))
'get angle between entities
Local theta# = ATan2((gy - e.y),(gx - e.x))
'pull it to the gravity spot!
If Not isShot Then e.add_force(theta, gforce)
'redirect shots
If isShot
Local temp:TList = New TList
temp.addLast(e)
For Local l:Shot = EachIn temp
'exert fake gravity to slow shots down
If l.speed > .6 * l.speedBase
l.speed = constrain(gdist/shotrange, .6, 1) * l.speedBase
Else'real physics take over once everything's safe
l.add_force(theta, gforce)
EndIf
'if they're about to die
If l.lifetimer > 1 And l.lifetimer <= 200
'extend their lives
l.lifetimer = 2000
'take control of them
ClearList(l.ignoreList)
EndIf
Next
EndIf
EndIf
'let 'em rip
ElseIf Not e.ignoreMovement
'if target is within range of the blasting effect
If gdist <= shotrange/2
e.movrot = _s.rot
If Not isShot Then e.speed = constrain(20 - Sqr(e.mass), 0, 20)
EndIf
'redirect shots
If isShot
Local temp:TList = New TList
temp.addLast(e)
For Local l:Shot = EachIn temp
l.speed = l.speedBase
l.lifetimer = Max(l.lifetimer, 1000+Rand(-200,200))
Next
EndIf
state = 0
EndIf
Next
'pull points together, remove them from the game
Case "collector"
'where to pull things towards (a bit in front of the ship)
Local goffset# = (ImageHeight(_s.gfx[0])/2) + 15
Local gx# = _s.x + Cos(_s.rot)*goffset
Local gy# = _s.y + Sin(_s.rot)*goffset
'pull fx in front of the ship (AAA DISCLAIMER OF MY ETHICALNESS)
SetAlpha .35
SetScale 1+.2*globalFrame,1+.2*globalFrame
SetColor 255,200,200
SetBlend LIGHTBLEND
For Local i = 1 To 4
SetRotation (i*45)
DrawImage warp2_gfx[globalFrame], gx+game.camx, gy+game.camy
Next
'make a list of things to affect
Local gList:TList = New TList
'affect all points, debris
For Local e:Entity = EachIn debrisList
If Not e.ignorePhysics Then gList.addLast(e)
Next
For Local i:Item = EachIn itemList
If Lower(i.name) = "point" Then gList.addLast(i)
Next
'go through all things sucked
For Local e:Entity = EachIn gList
'get distance to this entity
Local gdist# = approxDist(gx-e.x, gy-e.y)
'if target is within range of the pulling effect
Local range = 1000
If gdist <= range
'force is inverse to distance (and doesn't care about mass)
Local gforce# = shotdamage * e.mass * (1 - gdist/range)
'get angle between entities
Local theta# = ATan2((gy - e.y),(gx - e.x))
'pull it to the gravity spot!
e.add_force(theta, gforce)
EndIf
'destroys anything too close
If gdist <= 28
'kill it
RemoveLink(e.link)
'make some particles
For Local i = 0 To 6
add_particle(gx, gy, Rand(0,359), RndFloat()*5, 800, False)
Next
EndIf
Next
'put a springlike force on self, all entities within range
Case "GRAVITY"
'shotdamage = strength of gravity
'shotrange = range of gravity effect
'we affect entities, debris
Local gList:TList = New TList
For Local e:Entity = EachIn entityList
gList.addLast(e)
Next
For Local e:Entity = EachIn shotList
gList.addLast(e)
Next
For Local e:Entity = EachIn debrisList
gList.addLast(e)
Next
For Local e:Entity = EachIn explodeList
gList.addLast(e)
Next
'go through all entities, find nearby ones
Local eatList:TList = New TList
For Local e:Entity = EachIn gList
If e <> _s
'get distance to this entity
Local gdist# = approxDist(_s.x-e.x, _s.y-e.y)
'if target is within range
If gdist <= shotrange
'force is inverse to distance (and doesn't care about mass)
Local gforce# = shotdamage * e.mass * (1 - gdist/shotrange)'ATan(-gdist+shotrange)/90
'get angle between entities
Local theta# = ATan2((_s.y - e.y),(_s.x - e.x))
'pull 'em together!
'_s.add_force(theta+180, gforce)
e.add_force(theta, gforce)
'make it blacker
e.shade = 255.0 * (1.0 - gdist*2/shotrange)
EndIf
'if target is REALLY close, eat 'im up!
If gdist <= (ImageWidth(e.gfx[0])/2)+10 Then eatList.addLast(e)
EndIf
Next
'eat stuff differently
For Local s:Ship = EachIn eatList
s.armour = 0
Next
For Local bg:Background = EachIn eatList
bg.lifetimer = 1
Next
For Local s:Shot = EachIn eatList
s.lifetimer = 1
Next
EndSelect
'depending on the mode, change the state
Select mode
Case 1'semiautomatic
'if we've finished the burst, we don't shoot again until the mouse is released
If burstCount >= burstNum Then state = 0
EndSelect
'does this gun come in bursts? (only trigger on the first shot of a burst) (also, with hold-release guns, only burst on release)
If (burstNum > 1 And burstCount <= 0) And (mode <> 2 Or _release) And fireDelay_timer > 0'(and we didn't abort the shot)
burstDelay_timer = burstDelay 'countdown to next shot in the burst
burstCount = burstNum - 1 'set number of remaining shots in burst
burstX = _tarx 'store position of target for this burst
burstY = _tary
EndIf
'does this gun have a clip?
If clipNum > 0
clip:- 1 'count down the number of shots left in the clip
'if the clip is out of ammo, time to reload
If clip <= 0
If _s = p1 Then playSFX(ammodeplete_sfx,_s.x,_s.y)
reloadDelay_timer = reloadDelay
EndIf
EndIf
'ElseIf clip = 0 And clipNum > 0'if player is out of ammo and tries to fire
'playSFX(ammodeplete_sfx,_s.x,_s.y)
EndIf
Select mode
Case 0'automatic weapons heat up even if they didn't fire a shot as per se...
'still need to have ammo, etc.
If (reloadDelay_timer <= 0) And (clip > 0 Or clipNum = 0)
state = constrain(state + frameTime/chargeTime + frameTime/coolTime, 0, 1)
EndIf
Case 1'semi-automatic weapons need to stop trying to be fired before they shoot again
'semiauto states:
'1 = ready to fire
'0 = not ready to fire
'-1 = attempt to reready by update
'if update is trying to reready but we're still firing, tell it NO!
If state = -1 Then state = 0
'unless it's bursting, then we're always ready
If burstNum > 1 And burstCount <= 0 Then state = 1
Case 2'press-and-hold weapons are always trying to release their charge, keep it in if we're still trying to fire
If Not _release And fireDelay_timer <= 0
If state < 0 Then state = Abs(state)
'build up a % charge (and cancel out the cooling)
Local oldstate = state
state = constrain(state + frameTime/chargeTime + frameTime/coolTime, 0, 1)
If oldstate <> 1 And state >= 1 Then playSFX(click_sfx,_s.x,_s.y)'play a blip when we fully whatever
EndIf
EndSelect
EndIf
EndMethod
'called every cycle-> shots cool down, missiles try to lock on...
'_s is containing ship, _md 1 and 2 are the current mousedown states (for non-player, determine here what the best course of action is and do it)
Method update(_s:Ship)
'find the position of the gun offset from the ship
Local gx = offsetx*Cos(_s.rot-90) + offsety*Sin(_s.rot-90)
Local gy = offsety*Cos(_s.rot-90) + offsetx*Sin(_s.rot-90)
'find the absolute coords of the mouse
Local mx,my
If _s = p1
mx = p1.x + (cursorx - (p1.x+game.camx))
my = p1.y + (cursory - (p1.y+game.camy))
EndIf
'are we bursting?
If burstCount > 0
burstDelay_timer:- frameTime
If burstDelay_timer <= 0
'fire the gun
fireDelay_timer = 0
'figure out where to shoot
Local tarx, tary
If _s = p1'if player
tarx = mx'shoot at the mouse
tary = my
'otherwise, shoot at the current target's predicted position
ElseIf _s.target <> Null
Local tar_p#[] = _s.predictTargetPos(Null, shotspeed)
tarx = tar_p[0]
tary = tar_p[1]
'otherise, just use the initial aiming point
Else
tarx = burstX
tary = burstY
EndIf
fire(_s, tarx, tary, True)
fireDelay_timer = fireDelay
'count down the number of bursts, reset the timer
burstCount:- 1
If burstCount > 0 Then burstDelay_timer = burstDelay
EndIf
ElseIf mode = 1 And state = 1'once we finish bursting a semi-automatic, set state to 0
If _s <> p1 Then state = 0
EndIf
'dec. the timer for firing (if the burst is over) and not a press-and-hold weapon)
If (fireDelay_timer > 0 And burstCount <= 0)' And (mode = 0 Or mode = 1 Or state = 0)
fireDelay_timer = constrain(fireDelay_timer - frameTime, 0, fireDelay)
EndIf
'dec. the timer for reloading
If reloadDelay_timer > 0
reloadDelay_timer = constrain(reloadDelay_timer - frameTime, 0, reloadDelay)
'finish reloading
If reloadDelay_timer <= 0
If _s = p1 Then playSFX(ammoreload_sfx,_s.x,_s.y)
clip = clipNum
EndIf
EndIf
'update teh staet ov the gun
Select mode
Case 0'automatic
'cool off all the time; when firing we cancel this out
state = constrain(state - frameTime/coolTime, 0, 1)
Case 1'semi-automatic
'was the gun re-readied? (not tried to fire) -> if so, then ready it for the next shot
If state = -1 Or (_s <> p1) Then state = 1
'try and re-ready the gun
If state = 0 Then state = -1
Case 2'press-hold-release
'was the charge not re-held? (not tried to fire) - OR - if an AI has reached full charge
If ((state < 0) Or (_s <> p1 And state = 1))
'figure out where to shoot
Local tarx,tary
'if the player, shoot the released shot at the mouse
If _s = p1
tarx = mx
tary = my
'otherwise, shoot at the current target's predicted position
ElseIf _s.target <> Null
Local tar_p#[] = _s.predictTargetPos(Null,shotspeed)
tarx = tar_p[0]
tary = tar_p[1]
'otherise, just shoot straight forward
Else
tarx = _s.x + Cos(_s.rot)*shotrange
tary = _s.y + Sin(_s.rot)*shotrange
EndIf
'fire the released shot!
fire(_s,tarx,tary,True)
'if there's no burst, we're done, reset the charge
If burstNum <= 0 Then state = 0
EndIf
'cool off all the time; when firing we cancel this out
If burstCount <= 0
If state < .95 Then state = constrain(state - frameTime/coolTime, 0, 1)
'try and release the charge (if we're not bursting)
If state > 0 Then state = -Abs(state)
EndIf
EndSelect
Select Lower(name)
Case "plasma"'builds up a charge over time (firing disperses the charge) (higher charge = better burst)