-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRubicon.bmx
5358 lines (4611 loc) · 169 KB
/
Rubicon.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
?Win32
Import "icon.o"
?
Strict
AppTitle = "Rubicon"
?Win32
Extern "Win32"
Function GetAsyncKeyState:Int(key:Int)'so we can use PRINTSCREEN
End Extern
?
Const OS = 0' slight tweaks depending on the OS in question (screen size, icon...) [ 0 = PC | 1 = MAC | 2 = LINUX ]
Global SHIPSCALE# = 1.1 'how big ships, graphics are
'track the cursor position
Global cursorx#,cursory# 'could you say that the tracking is just... *cursory* ???
HideMouse()
'set up the graphic options
Local RESO_OVERRIDE = 0
Global FULLSCREEN = 0
Global SWIDTH#=680,SHEIGHT#=400
'set up the default control keys
Global MOUSE_FIREPRIMARY, MOUSE_FIRESECONDARY
Global KEY_THRUST, KEY_REVERSE, KEY_STRAFELEFT, KEY_STRAFERIGHT, KEY_AFTERBURN, KEY_SHIELD, KEY_CYCLEGUN, KEY_MAP
Global JOY_FIREPRIMARY, JOY_FIRESECONDARY, JOY_THRUST, JOY_AIMAXIS, JOY_MOVEAXIS, JOY_AMPLIFY#, JOY_AFTERBURN, JOY_SHIELD, JOY_CYCLEGUN, JOY_MAP, JOY_MENU
Const JOY_LEFTSTICK = 255, JOY_RIGHTSTICK = 254, JOY_OTHERSTICK = 253, JOY_HAT = 252 'high numbers so don't conflict with keycodes
setControlsToDefault()
'use the keyboard+mouse or a joystick?
'Const SCHEME_KEY = 0, SCHEME_JOY = 1
'Global control_scheme = SCHEME_JOY
Global joyDetected = False'no joy here, checked in updatecursor()
'STARTING pilot name, we only use this to default to a selected pilot when the game starts
Global SEL_PILOT$
'set up the cheats
Global cheat_modbase = False 'lets you modify locked configs, components
Global cheat_invincible = False 'take no damage
Global cheat_disableengines = False 'can't thrust forward
'read the options file
RESO_OVERRIDE = readOptionsFile("rubicon.options")
'use the settings override?
If RESO_OVERRIDE
Graphics SWIDTH,SHEIGHT,FULLSCREEN*2
'go for a default resolution
Else
Local default_fullscreen = 2'defaults to fullscreen
If OS = 1 Then default_fullscreen = 0'...except for macs
Local reso_x, reso_y
For Local r = 1 To 8'try out all the default resolutions
Select r
Case 1
reso_x = DesktopWidth()
reso_y = DesktopHeight()
Case 2
reso_x = 1920
reso_y = 1200
Case 3
reso_x = 1366
reso_y = 768
Case 4
reso_x = 1280
reso_y = 768
Case 5
reso_x = 1024
reso_y = 768
Case 6
reso_x = 960
reso_y = 600
Case 7
reso_x = 800
reso_y = 600
Case 8
RuntimeError("Compatible graphics mode not found, sorry. Try opening [rubicon.options] and doing a resolution override.")
End
EndSelect
'macs need to account for that stupid bar thing for every program + the frikking "I'm always visible look how graphical I am" dock
'yes I know they're for standardization of use and actually good design in that you don't have to relearn how to use every single
'program according to whatever the cracked-out programmer was thinking would be a good idea at the time
'but still dammit they get in the way of things
If OS = 1'MAC
reso_x:- 150
reso_y:- 150
EndIf
If GraphicsModeExists(reso_x,reso_y) Or OS = 1'just force it for MACs
Graphics reso_x, reso_y, default_fullscreen
Exit'we've found a good resolution
EndIf
Next
SWIDTH = GraphicsWidth()
SHEIGHT = GraphicsHeight()
EndIf
SeedRnd MilliSecs()
AutoMidHandle True
SetMaskColor 255,255,255
SetBlend(ALPHABLEND)
Const game_FPS = 45'set the game to a max # frames per second
Global frameTime# = 1000 / game_FPS'find how many millisecs should be between each frame
Global frameTimer:TTimer = CreateTimer(game_FPS)
'show the player something more than just a black screen
DrawText "LOADING...",0,0
Flip
Include "graphicsmagic.bmx" 'image processing functions: getting individual frames, resizing, recoloring, hardcodes color schemes
Include "buttoncontrol.bmx" 'for use in menus: sets up the button object, SLists for scrolling lists of buttons
Include "mainMenu.bmx" 'sets up the main menu + pilot object
Include "pauseMenu.bmx" 'when press esc during a game, runs this minimenu
Include "optionMenu.bmx" 'menu to adjust gameplay options, audio, controls
Include "intro.bmx" 'does the short intro sequence when the game starts
Include "new_ship.bmx" 'provides the function to create a new ship ingame
Include "add_backdrop.bmx"
Include "hangercontrol.bmx"
Include "deploycontrol.bmx"
Include "arcade_upgradecontrol.bmx"
Include "gamecontrol.bmx"
Include "vip.bmx"
'forces accelerate entities based on their mass
Type Force
Field F#
Field angle#
'Field speedLimit# 'forces can only accelerate things up to a certain speed. Relativity or something, am I right??? (neg. # = no cap)
EndType
Type Entity Abstract
Field x#,y#,rot#,movrot#,speed#,spin#,mass# = 2, frameoffset
Field link:TLink 'the link in the entity, bg, debris, shot, or explode TList this entity is part of
Field master:Entity 'if not null, this entity is slaved to another entity
Field master_offx#, master_offy# 'offset coords from master's position at rot = 0
Field master_offrot# 'original master's rotation, if the master turns after this we can figure out where to draw in relation
Field ignorePhysics = False 'whether this entity should listen to the forces on it
Field ignoreCollisions = False 'whether this entity participates in any collisions
Field ignoreShipCollisions = False 'whehter this entity participates in collisions with other ships
Field ignoreShotCollisions = False 'whehter this entity participates in collisions with other shots
Field ignoreMovement = False 'whether this entity should move
Field ignoreList:TList = New TList 'list of GERTRUDES that this entity can't collide with (e.g. the owner, ships it's already hit...)
Field forceList:TList = New TList
Field cboxList:TList = New TList 'list of INTEGERS that correspond to the cboxes this ship is in
Field skipDraw = False 'skip drawing this
Field dist# = 1 'parallax-maker! higher this is, slower it moves via player's movement. 1=shiplevel
Field animated 'true/false, whether to animate this ship
Field alpha# '0-1, how transparent to draw this entity (0 = opaque | 1 = transparent)
Field shade# 'how much to subtract from ALL of the 255 drawcolors
Field RGB[3] 'color of the entity, defaults to 255,255,255 if untouched
Field blend = ALPHABLEND 'the kind of blending to use
Field scale# = 1 'what SHIPSCALE is multiplied by for drawscale (if 0 then default to 1)
Field text$ 'can replace gfx, if it has this it'll be drawn instead
Field gfx:TImage[] 'two frames, one Timage stored to each one (unless it's not animated)
Field proximity_track = False 'whether this should track the proximity of nearby ships
Field proximity_shipList:TList = New TList'a list that is constantly updated of other nearby ships, used by the AI (Gertrude types ov cors)
'pays attention to forces and whatnot
Method physics()
'x,y components of speed
Local speed_x# = Cos(movrot) * speed
Local speed_y# = Sin(movrot) * speed
'Local angle_x# = speed_x
'Local angle_y# = speed_y
'draw current speedlines
'If Self = p1
' SetColor 0,0,255
' DrawLine SWIDTH/2,SHEIGHT/2,SWIDTH/2+speed_y*20,SHEIGHT/2-speed_x*20
'EndIf
'IF forces are acting on the entity AND we don't ignore them!
If (Not ignorePhysics) And (mass > 0)
If (Not ListIsEmpty(forceList))
'tally up all the forces
Local fx#=0, fy#=0'total force
'Local sx#=0, sy#=0'speed-altering force only
For Local f:Force = EachIn forceList
fx:+ Cos(f.angle) * f.F
fy:+ Sin(f.angle) * f.F
'find the relative angle of thrust
'Local rangle# = movrot - f.angle
'find entity's parallel velocity to the angle of thrust
'Local s_para# = Cos(rangle) * speed
'find the parallel-to-thrust vector of the MAXIMUM speed at current moving angle
'Local s_para_max# = f.speedLimit
'draw vector lines
'If Self = p1
' Local lx# = Cos(f.angle) * 20's_para# * 20
' Local ly# = Sin(f.angle) * 20's_para# * 20
' SetColor 0,255,0
' DrawLine SWIDTH/2,SHEIGHT/2,SWIDTH/2+ly,SHEIGHT/2-lx
' SetColor 255,0,255
' DrawLine SWIDTH/2,SHEIGHT/2,SWIDTH/2+ly*s_para_max/s_para+4,SHEIGHT/2-lx*s_para_max/s_para+4
'EndIf
'SetColor 255,0,0
'DrawLine x+game.camx,y+game.camy,x+game.camx+Cos(f.angle)*f.F*200,y+game.camy+Sin(f.angle)*f.F*200
'decrease the force applied as the speed of the object reaches the limit
'If s_para > 0
' f.F = f.F * (1 - constrain(s_para/s_para_max, -1,1) )
'Else
' f.F = f.F
'EndIf
'add this force to the speed-altering force tally
'sx:+ Cos(f.angle) * f.F
'sy:+ Sin(f.angle) * f.F
Next
'get the total force, angle
Local total_force# = Sqr(fx^2+fy^2)
Local total_angle# = ATan2(fy,fx)
'get the speed-altering force
'Local speed_force# = Sqr(sx^2+sy^2)
'draw forcelines
'If Self = p1
' SetColor 255,0,0
' DrawLine x+game.camx,y+game.camy,x+game.camx+fx*200,y+game.camy-fy*200
'EndIf
'get the amount of speed we want to add: force * passed time / mass
Local add_speed# = total_force * frameTime / mass
'Local add_speed# = speed_force * frameTime / mass
'Local add_steer# = total_force * frameTime / mass
'add the force's added speed to current speed
speed_x:+ Cos(total_angle) * add_speed
speed_y:+ Sin(total_angle) * add_speed
'find the new angle
'angle_x:+ Cos(total_angle) * add_steer
'angle_y:+ Sin(total_angle) * add_steer
'store the entity's new angle, speed
'movrot = ATan2(angle_y,angle_x)
movrot = ATan2(speed_y,speed_x)
speed = Sqr(speed_x^2 + speed_y^2)
'clear the force list
ClearList(forceList)
EndIf
EndIf
'move everything how it's supposed to move (if it's supposed to move + isn't slaved to something else)
If master = Null
If Not ignoreMovement
x = x + speed*Cos(movrot)*(frameTime/1000)
y = y + speed*Sin(movrot)*(frameTime/1000)
rot = rot + spin
EndIf
Else
'bump the master
'master.addSpeed(speed,movrot)
'reset own speed to zero
speed = 0
movrot = 0
'update position to master's
x = master.x - master_offx*Cos(-master.rot) + master_offy*Sin(-master.rot)
y = master.y + master_offy*Cos(-master.rot) - master_offx*Sin(-master.rot)
'whenever the master rotates, the slave does as well
'rot = (master.rot + master_offrot)
'master_offrot = master.rot
EndIf
EndMethod
'add a force to the entity
Method add_force(_angle#,_F#)',_speedLimit# = 5)
Local f:Force = New Force
f.F# = _F#
f.angle# = _angle#
'f.speedLimit# = _speedLimit
forceList.AddLast f
EndMethod
Method draw()'draws the entity (relative to the camera!) + returns true if could/bothered to successfully draw
'valid graphic?
If (gfx <> Null Or text <> "") And Not skipDraw
'onscreen?
If Abs(x-p1.x) < SWIDTH*(dist^2)*1.2 And Abs(y-p1.y) < SHEIGHT*(dist^2)*1.2
'If Abs(x-p1.x) < SWIDTH*2*dist And Abs(y-p1.y) < SHEIGHT*2*dist
'If (Abs(x-p1.x)-(ImageWidth(gfx)*SHIPSCALE) < SWIDTH*2*dist) Or (Abs(y-p1.y)-(ImageHeight(gfx)*SHIPSCALE) < SHEIGHT*2*dist)
If scale = 0 Then scale = 1
If dist = 0 Then dist = 1
Local drawdist# = dist + game.zoom
If RGB[0] = 0 And RGB[1] = 0 And RGB[2] = 0
RGB[0] = 255
RGB[1] = 255
RGB[2] = 255
EndIf
'set drawing states
SetScale SHIPSCALE*scale/drawdist,SHIPSCALE*scale/drawdist
SetAlpha 1-alpha
SetColor RGB[0]-shade,RGB[1]-shade,RGB[2]-shade
SetBlend blend
'set the frame to draw
Local frame = 0
If animated
frame = frameoffset
If globalFrame = 1 Then frame = (frame+1) Mod 2
EndIf
'figure out how to modify their position based on distance
Local zoom_shift#[] = findDistShift()
SetRotation (rot - game.camrot)
Local drawx# = x+game.camx+zoom_shift[0]
Local drawy# = y+game.camy+zoom_shift[1]
'draw the graphic or the text
If text <> ""
DrawText text, drawx, drawy
Else
DrawImage gfx[frame], drawx, drawy
EndIf
Return True
EndIf
EndIf
Return False'if it's made it this far...
EndMethod
'find the offset due to the distance of the object & the camera's zoom
Method findDistShift#[]()
Local zoom_shift#[2]
zoom_shift[0] = 0
zoom_shift[1] = 0
If game <> Null
'find how far they are from the center of the screen
Local xdif# = x - (-game.camx + SWIDTH/2)'(p1.x+game.camxoffset)
Local ydif# = y - (-game.camy + SHEIGHT/2)'(p1.y+game.camyoffset)
'find the actual shift in xy axis
zoom_shift[0] = -xdif + (xdif / (game.zoom + dist))
zoom_shift[1] = -ydif + (ydif / (game.zoom + dist))
EndIf
Return zoom_shift
EndMethod
'adds a certain amount of speed in a certain direction, IN ADDITION TO current
Method addSpeed(_speed#,_angle#)
'how fast we're currently moving
Local speed_x# = Cos(movrot) * speed
Local speed_y# = Sin(movrot) * speed
'how much to give it a shove
Local push_x# = Cos(_angle) * _speed
Local push_y# = Sin(_angle) * _speed
'shove it!
speed_x:+ push_x
speed_y:+ push_y
'recalc speed, angle
movrot = ATan2(speed_y,speed_x)
speed = Sqr(speed_x^2 + speed_y^2)
EndMethod
'add things to collision boxes [small groups of entities based on location, so we perform (a^2 + b^2 + c^2... comparisons instead of just n^2)]
Method addToCollisionBoxes()
If Not ignoreCollisions
Local cbx, cby, cbx_old = -1, cby_old = -1' x, y tile of the current and previous cbox
Local swid# = ImageWidth(gfx[0])/2 'the horizontal radius of the entitiy
Local shet# = ImageHeight(gfx[0])/2 'the vertical radius of the entity
Local cb_wide = Ceil(Ceil(swid*2/game.cboxSize)/2)'how many collision boxes-wide the entity is
Local cb_tall = Ceil(Ceil(shet*2/game.cboxSize)/2)'how many collision boxes-tall the entity is
'look at each corner of the ship, add self to each cbox group that it overlaps
For Local y_box# = -cb_tall To cb_tall
cby = Int((y + (y_box*shet) + game.height) / game.cboxSize)
cby = constrain(cby, 0, game.cboxRowNum-1)
If cby <> cby_old'if we've moved onto a new row of cboxes for this corner
For Local x_box# = -cb_wide To cb_wide
cbx = Int((x + (x_box*swid) + game.width) / game.cboxSize)
cbx = constrain(cbx, 0, game.cboxColNum-1)
If cbx <> cbx_old'if we've moved into a new column of cboxes for this corner
game.cboxList[cbx,cby].addLast(Self)
'SetColor 255,255,255
'SetAlpha .07
'DrawRect (cbx*game.cboxSize)+game.camx-game.width, (cby*game.cboxSize)+game.camy-game.height, game.cboxSize, game.cboxSize
EndIf
cbx_old = cbx
Next
EndIf
cby_old = cby
cbx_old = -1'we're on a new row, reset the column check
Next
EndIf
EndMethod
'sets RGB to 255,255,255
Method resetRGB()
RGB[0] = 255
RGB[1] = 255
RGB[2] = 255
EndMethod
Method update() Abstract 'what to do every loop
Method collide(_ship:Ship) Abstract 'what to do when collides with a SHIP (things can only collide with ships!)
EndType
Global entityList:TList = New TList
Const AI_AVOIDCOLLISION_DIST = 250 'min distance to an object before they'll start taking action
Const AI_DIVEBOMB_DIST = 210
Const AI_CIRCLE_DIST = 280 'this is the MAX distance they'll circle, some will be closer
Const AI_DETECT_DIST = 6000 'distance to detect an enemy
Global AI_predict_time = 1500 'how long it takes for the AI to draw a bead on another ship [now defunct]
'a group of ships with common commands. if no ships/icons point to it, it stops existing. whoa. [sorta stopped using them, but it's messily everywhere]
Type Squadron
Field x,y 'the current center position of this squadron
Field behavior$ 'behavior that overwrites the ship's natural behavior
Field shipNum 'stores the last shipcount, AKA how many ships are in this squadron
Field shipCount 'reset each loop, tells the ship how many ships have checked into this squad before it
Field goal_x,goal_y, target:Gertrude 'the target of this squadron, be it a position or a ship.
Field faction 'the faction all contained ships are part of
Field setPos = False 'does this squadron set the position of its members at the start?
Field avoidcollision_dist = AI_AVOIDCOLLISION_DIST
Field divebomb_dist = AI_DIVEBOMB_DIST
Field circle_dist = AI_CIRCLE_DIST
Field detect_dist = AI_DETECT_DIST
EndType
Function new_squad:Squadron(_behav$,_faction,_setPos = False)'makes & returns a squadron of the specified specifications
Local s:Squadron = New Squadron
s.behavior = _behav
s.faction = _faction
s.setPos = _setPos
Return s
EndFunction
'for asteroids and suchlike
Global inertSquad:Squadron = new_squad("inert",0)
Global abilityMap:TMap = CreateMap()
MapInsert(abilityMap, "0", "n/a")
MapInsert(abilityMap, "1", "Shield")
MapInsert(abilityMap, "2", "Blink")
MapInsert(abilityMap, "3", "Overburn")
MapInsert(abilityMap, "4", "Cloak")
MapInsert(abilityMap, "5", "Overcharge")
Const ABILITY_SHIELD = 1
Const ABILITY_BLINK = 2
Const ABILITY_BURNER = 3
Const ABILITY_CLOAK = 4
Const ABILITY_OVERCHARGE = 5
Global chassisList:TList = New TList
'the archtype of a ship
Type Chassis
Field name$
Field gfx:TImage[] 'two frames
Field hitgfx:TImage 'an all-white version of the graphic, generated at the end of chassis setup
Field animated
Field cshape[cshapedim,cshapedim] 'state of component tile: 0=n/a | 1=empty | 2=energy | 3=munition | 4=unused | 5=omni | (NEGATIVE = filled!)
Field compList:TList = New TList 'list of components installed on this ship
Field cost = 1000 'number of points it takes to unlock this ship
Field mass#
Field armour# 'health
Field fortification# 'base damage resistance to apply to each shot
Field ability 'which special ability this chassis has: 0=n/a
Field points 'number of special ability points this chassis has: -1=infinite
Field juice# = 1.2 'base seconds of afterburner for the chassis
Field speedMax# 'maximum speed you can reach by thrusters (afterburn 2x this)
Field thrust# 'acceleration, the force the engines provide (to get out of black holes and whatnot)
Field strafe# '% of thrusters and maximum speed that strafing can apply
Field turnRate# 'base rate that the ship can turn
Field fortifyList:TList = New TList 'list of fortification (damage reduction) amounts, offsets, and ranges
Field behavior$ = "pulse" 'how this ship behaves
Field trailRGB[3] 'color of the trail, defaults to 255,255,255 if we don't touch it
Field trailScale# = 1 'trail modifier, this will be added/subtracted from the trail scale
Field explodeSound:TSound 'sound of ship dying
Field explodeNum = -1 'number of explosions when it dies/is damaged | -1 = unset, calculate default
Field explodeShake = 1 'how much to shake the screen on death
Field debrisType ' 0:metal | 1:rock | 2:gib | 3:vector
Field debrisRGB[3]
Field dropShipList:TList = New TList 'list of SHIP NAMES to drop on death
Field dropSquad:Squadron 'squadron to add dropped ships to, defaults to inertSquad
Field dropItemList:TList = New TList 'list of ITEM NAMES to drop of death
Field bio = False 'is this a biological ship? (don't get bounced by borders, no target prediction
Field ord = False 'is this a piece of ordenance? (explode on contact with anything)
Field stationary = False
Field invulnerable = False
Field unlockable = False 'can the player unlock this chassis?
Field special = False 'if unlockable, do only special edition people get it?
'copies the stats of another chassis
Method copyChassis(_copyName$)
'find the chassis-to-copy
For Local c:Chassis = EachIn chassisList
If c.name = _copyName$
gfx = c.gfx
animated = c.animated
For Local y = 0 To cshapedim-1
For Local x = 0 To cshapedim-1
cshape[x,y] = c.cshape[x,y]
Next
Next
mass = c.mass
armour = c.armour
ability = c.ability
points = c.points
juice = c.juice
speedMax = c.speedMax
thrust = c.thrust
strafe = c.strafe
turnRate = c.turnRate
behavior = c.behavior
trailRGB[0] = c.trailRGB[0]
trailRGB[1] = c.trailRGB[1]
trailRGB[2] = c.trailRGB[2]
explodeSound = c.explodeSound
explodeNum = c.explodeNum
debrisType = c.debrisType
debrisRGB[0] = c.debrisRGB[0]
debrisRGB[1] = c.debrisRGB[1]
debrisRGB[2] = c.debrisRGB[2]
For Local s$ = EachIn c.dropShipList
dropShipList.addLast(s)
Next
dropSquad = c.dropSquad
For Local i$ = EachIn c.dropItemList
dropItemList.addLast(i)
Next
bio = c.bio
EndIf
Next
EndMethod
'automatically figure out the cshape based on the graphic
Method auto_cshape()
If gfx <> Null
SetScale 1,1
SetRotation 0
'create the testing rectangle
Local rect:TImage = CreateImage(cshaperatio-2,cshaperatio-2)
Cls
SetColor 0,255,0
DrawRect 0,0,cshaperatio-2,cshaperatio-2
GrabImage rect,0,0
SetImageHandle(rect,0,0)
'see where the gfx and rect overlap
For Local y = 0 To cshapedim-1
For Local x = 0 To Ceil(cshapedim/2)
'check if this spot should be available
If ImagesCollide(gfx[0],cshapedim*cshaperatio/2,cshapedim*cshaperatio/2,0,rect,x*cshaperatio+1,y*cshaperatio+1,0)
cshape[x,y] = 1'empty, available spot
Else
cshape[x,y] = 0'unavailabe spot
EndIf
'mirror things horizontally
cshape[(cshapedim-1)-x,y] = cshape[x,y]
Next
Next
EndIf
EndMethod
'kills a square, offset from the center
Method trim_cshape(_x,_y)
'x,y input are offset from center
Local cx = Floor(cshapedim / 2)
Local cy = Floor(cshapedim / 2)
cshape[cx+_x, cy+_y] = 0
EndMethod
'set a tile to a power tile
Method power_cshape(_x,_y,_ptype$)
'x,y input are offset from center
Local cx = Floor(cshapedim / 2)
Local cy = Floor(cshapedim / 2)
'figure out code of powertype
For Local pcode:Object = EachIn MapKeys(psquareMap)
'if we find a type of power tile that matches what we were given, set this square to that
If MapValueForKey(psquareMap, String(pcode)) = _ptype Then cshape[cx+_x, cy+_y] = Int(String(pcode))
Next
EndMethod
'adds a component to the chassis
Method add_comp:Component(_compName$,_x=0,_y=0, _locked = False, _absolute = False)
'x,y input are offset from center
Local cx = Floor(cshapedim / 2)
Local cy = Floor(cshapedim / 2)
'no offset of the provided coords are absolute
If _absolute
cx = 0
cy = 0
EndIf
'make the component
For Local c:Component = EachIn componentList
If c.name = _compName
Local ac:Component = new_comp(c.name)
ac.x = cx + _x
ac.y = cy + _y
ac.locked = _locked
compList.addLast(ac)
Return ac
EndIf
Next
EndMethod
EndType
'an offset and range of angles to apply damage resistance
Type Fortification
Field offset# 'offset from ship's rot = 0 to center the fortification
Field range# = 90 'arc centered on offset to reduce incoming damage
Field DR# 'amount of damage reduced to all incoming shots that hit the fortification
Field ramming = False 'is it ramming armour? if so, only reduces collision damage
EndType
Function new_fortify:Fortification(_DR#, _range#, _offset#, _ramming = False)
Local f:Fortification = New Fortification
f.DR = _DR
f.range = _range
f.offset = _offset
f.ramming = _ramming
Return f
EndFunction
Type Ship Extends Entity
Field name$
Field base:Chassis 'the chassis this ship is based on
Field config$ 'the name of the configuration -> if Null then just use "default"
Field cshape[cshapedim,cshapedim] 'squares to drop components onto: 0=unavailable | 1=empty | 2=filled & transparent | 3=filled & opaque
Field compList:TList = New TList 'list of components installed on this ship
Field color, scheme 'the last loaded color and the current target scheme
Field gunGroup:TList[6] 'list of weapongroups, 0=primary weapons, everything above that are secondaries
'SHOOTY STATS, calculated on game initialization based on chassis stats + components
Field turnRate# 'rate that the ship can turn, a base - a function of mass
Field speedMax# 'maximum speed you can reach by thrusters (afterburn 2x this)
Field thrust# 'the force your engines can apply forward. It calculates how fast it SHOULD be able to push you, can't accel faster.
Field strafe# 'what % of ships main thrust can be applied sidewayss
Field pointMax 'maximum amount of ability points this ship has (-1:infinite)
Field points 'current amount of ability points this ship has
Field armourMax#
Field armour#
Field juiceMax#
Field juice# 'amount of energy currently stored for afterburner
Field fortifyList:TList = New TList 'damage resistance applied to each incoming shot
Field burner_duration = 8 'the length of time, in seconds, that the overburner lasts
Field burner_timer#
Field burnerOn 'TRUE/FALSE whether the burner is currently on
Field shield_duration# = 6 'the length of time, in seconds, that shields last
Field shield_timer# 'timer for the shield to start regenerating
Field shield_tween# 'a little hit tweening. decays rapidly to 0, set to 1 for full effect.
Field shieldOn 'TRUE/FALSE whether shields are currently up
Field blink_timer# 'while this is counting down, the player can hit space again to blink back in. if it hits 0, automatic.
Field blinkOn 'are we blinking; while this is on, the ship is pretty much removed from the entity list
Field cloak_duration# = 11 'the length of time, in seconds, that cloaking lasts
Field cloak_timer#
Field cloakOn 'can we be detected?
Field overcharge_duration# = 8 'length of time the overcharge lasts
Field overcharge_timer#
Field overcharge_mod# = 1.5 '* damage, / firerate, * shotspeed
Field overchargeOn
Field explodeSound:TSound
Field explodeNum 'number of explosions
Field debrisRGB[3] 'color of explosions, debris
Field dropList:TList = New TList 'list of entities or backgrounds to drop when ship dies (debris, powerups)
'SHOOTY tracking variables
Field squad:Squadron 'a pointer to the squadron this ship is a part of, used to determine baseline behavior, goals
Field throttle# 'what % the engines are pushin
Field engineChannel:TChannel 'the sound channel for the engines.
Field thrust_theta#
Field hit_tween# 'transparency of white box to draw over the ship. decays rapidly to 0, set to 1 on hit damage
Field recent_damage# 'tracks the amount of damage the ship has taken recently
Field recent_damage_timer# 'counts down how recent "recent" is. also acts at a damage text tween
Field recent_damage_delay = 1500 'amount of time, in ms, how recent "recent" is
Field recent_damage_theta 'angle of the most recent damage
Field recent_points# 'how many points the player has picked up recently
Field recent_points_timer 'the countdown to fade the counter back out
Field recent_points_delay = 5500
Field behavior$ 'how this ship behaves. "player":player-controlled|"cw":flies in cw circles|"ccw":flies in ccw circles
Field target:Gertrude 'the ship that this ship is currently targeting
Field AI_timer 'generic timer that AI uses
Field AI_value[4] 'used by different AI modes for different things
Field AI_dir#[2] 'x,y vectors that the AI adds up and then decides which way it wants to go with
Field player_ffdisplay 'if true, display friend/foe information on this ship
Field AI_predict# 'how well OTHER AIs can predict this one's movement
'changing speed, direction, or afterburning all decrease this, otherwise it steadily increases.
Field collide_sfx_countdown 'can only make a collide noise every 1000 ms
Field invulnerable 'should it be able to take damage?
Field tweenOnHit = True 'should we do a little white flash on hit of the ship?
Field placeholder:Gertrude = New Gertrude'a drop site so other ships can figure out this ships location, faction, etc. without creating strange loop
'general properties
Method update()
'call the ship's update functions for each gun in each gunGroup
For Local wgroup = 0 To 5
For Local g:Gun = EachIn gunGroup[wgroup]
g.update(Self)
Next
Next
'if override current behavior with the squadron's behavior if applicable, otherwise stick to the base
If squad.behavior <> "" Then behavior = squad.behavior Else behavior = base.behavior
If p1 = Self Then behavior = "player"
'onscreen?
Local onscreen = False
If Abs(x-p1.x) < (SWIDTH+SHEIGHT)*dist And Abs(y-p1.y) < 2*SHEIGHT*dist Then onscreen = True
'bound armour amount
armour = constrain(armour, 0, armourMax)
If invulnerable Then armour = armourMax
'special abilities
Select base.ability
'invulnerable for a short time
Case ABILITY_SHIELD
'shields are only on if the timer is not yet finished
If shield_timer <= 0 Then shieldOn = False Else shieldOn = True
'count down the time shields are on
If shieldOn Then shield_timer:- (frameTime/1000.0)
'decay shield tween back towards none
If shield_tween <> 0 Then shield_tween:- (shield_tween*4) * (frameTime/1000.0)
'faster engines for a short time
Case ABILITY_BURNER
'burner is only on is the timer is not yet finished
If burner_timer <= 0 Then burnerOn = False Else burnerOn = True
If burnerOn
burner_timer:- (frameTime/1000.0)'count down the time burner is on
juice = juiceMax'always can afterburn
EndIf
'teleport to the mouse
Case ABILITY_BLINK
If blinkOn
blink_timer:- frameTime
'choose the target to blink at
If blink_timer <= 0 Or game.over' Or KeyHit(KEY_SHIELD)'THIS IS SO NOT OKAY!!!
blinkOn = False
'go at the mouse
x:+ (cursorx - (p1.x+game.camx))
y:+ (cursory - (p1.y+game.camy))
'center the cursor, we've moved to it
moveCursor()
'takes a bit to warp back in
blink_timer = 200
'make an effect of fading back in
Local fade:Foreground = New Foreground
Local fadegfx:TImage[1]
fadegfx[0] = base.hitgfx
fade.gfx = fadegfx
fade.animated = False
fade.x = x
fade.y = y
fade.rot = rot
fade.alpha = 1
fade.alphaspeed = -.05
fade.lifetimer = blink_timer
fade.link = bgList.addFirst(fade)
EndIf
'if we're still finishing the blink
ElseIf blink_timer > 0
blink_timer:- frameTime
'finish it!
If blink_timer <= 0
'become tangible again
cloakOn = False
ignorePhysics = False 'whether this entity should listen to the forces on it
ignoreCollisions = False 'whether this entity participates in any collisions
ignoreMovement = False 'whether this entity should move
skipDraw = False
game.disable_controls = False
blink_timer = 0
hit_tween = 1
speed = 0
FlushKeys()
FlushMouse()
FlushJoy()
ClearList(forceList)
EndIf
EndIf
'turn invisible for a short time
Case ABILITY_CLOAK
'if cloaking is on
If cloak_timer > 0
cloakOn = True
'count down the time cloak is on
cloak_timer:- (frameTime/1000.0)
Local fadeTime# = .5
'set ship's alpha accordingly
If cloak_duration - cloak_timer <= fadeTime
alpha = ((cloak_duration - cloak_timer) / fadeTime) 'FADE OUT
ElseIf cloak_timer <= fadeTime
alpha = (cloak_timer / fadeTime) 'FADE IN
Else
alpha = 1' 'BE INVISIBLE
EndIf
Else
cloakOn = False
EndIf
If Self = p1 Then alpha = constrain(alpha, 0, .8)
'get stronger weapons for a short time
Case ABILITY_OVERCHARGE
If overcharge_timer <= 0
overchargeOn = False
Else
overchargeOn = True
overcharge_timer:- (frameTime/1000.0)'count down the timer
'make some awesome background effects (red shadows)
If Rand(1,6) = 1
Local shadow:Background = add_particle( x, y, Rand(0,359), 37.5+RndFloat()*2, 700, True)' Rand(0,1))
shadow.gfx = New TImage[2]'so we don't contaminate the normal gfx
shadow.gfx[0] = base.hitgfx
shadow.gfx[1] = base.hitgfx
shadow.animated = False
shadow.spin = 0
shadow.scale = 1 + (overcharge_timer/overcharge_duration)
shadow.rot = rot
shadow.alpha = .7
shadow.alphaSpeed = (RndFloat()+.5)/Float(shadow.lifetimer*4)
shadow.RGB[0] = 120
shadow.RGB[1] = 0
shadow.RGB[2] = 0
'push the shadow to match ship speed
shadow.addSpeed(speed*.7, movrot)
EndIf
EndIf
EndSelect
'steadily increase the predict movement value
AI_predict = constrain(AI_predict + (frameTime/AI_predict_time), 0, 1)
'decay hit tween back towards none
If hit_tween <> 0 Then hit_tween:- (hit_tween*8) * (frameTime/1000.0)
If hit_tween < .05 Then hit_tween = 0
'have the engines make sum noiz in da hous (uses prexisiting engine sound)
'Local engineVol# = Abs((speed / speedMax) * Sgn(throttle))
'If engineChannel <> Null Then playSFX(Null,x,y,engineVol,engineChannel)
'regenerate juice
If throttle <= 1 Then juice = constrain(juice + (frameTime/1000.0), 0, juiceMax)
'lose dead and cloaked targets
If target <> Null And (target.dead Or target.cloakOn) Then target = Null
'resolve the ship's thrust, add the actual force
resolve_thrust()
'throttle can't decrease maxspeed
If throttle < 1 Then throttle = 1
'constrain to maximum speed
Local decel# = (thrust*base.mass) * Max((speed / speedMax),1.0)
If thrust = 0 Then decel = (base.mass) * Max((speed / speedMax), 1.0)'thrustless things are also capped
If Abs(speed) > Abs(speedMax*throttle) Then add_force(movrot+180, decel)
'If speed > speedMax*throttle
' Print "OH FUCK OH FUCK " + name + " speed:"+speed + " forcenum:"+CountList(forceList)
' speed = speedMax*throttle
'EndIf
'reset throttle to 0, default direction of thrust to forward
throttle = 0
thrust_theta = rot
'count down recent damage timer
If recent_damage_timer > 0 Then recent_damage_timer:- frameTime
If recent_damage_timer <= 0 Then recent_damage = 0
'count down recent point timer
If recent_points_timer > 0 Then recent_points_timer:- frameTime
If recent_points_timer <= 0
recent_points = 0
ElseIf recent_points_timer <= 4000'count down the recent points
recent_points = constrain(recent_points - 1, 0, recent_points)
EndIf
'keep rot b/t 0-359
If rot < 0 Then rot = rot + 360
If rot >= 360 Then rot = rot - 360
If movrot < 0 Then movrot = movrot + 360
If movrot >= 360 Then movrot = movrot - 360
'physical laws of THIS universe have to do with sound, OK? --> ...what?
If collide_sfx_countdown > 0 Then collide_sfx_countdown:- frameTime
'update the position of the squadron, pull it towards this ship (weighted towards the current position so last ship doesn't just WIN)
squad.x = (squad.x*9 + x) / 10
squad.y = (squad.y*9 + y) / 10
'update the placeholder's information
update_placeholder(Self)
'for gfx, ship's radius
Local rad = SHIPSCALE*(ImageWidth(gfx[0]) + ImageHeight(gfx[0]))/6
'damaged gfx
If base.debrisType = 0 Or base.debrisType = 1'explode or rock debris
If onscreen
If (armour < armourMax/2)
For Local e = 1 To explodeNum
If Rand(1,500) = 1
Local ex:Foreground = add_explode(x+Rand(-rad,rad),y+Rand(-rad,rad))
If base.debrisType = 1'rock
ex.gfx = dust_gfx
ex.alphaspeed:/ 4
ex.lifetimer = 5000
ex.animated = False
EndIf
ex.RGB[0] = debrisRGB[0]
ex.RGB[1] = debrisRGB[1]
ex.RGB[2] = debrisRGB[2]
EndIf
Next
EndIf
EndIf
EndIf
'---DESTROYED
If armour <= 0 Then death(onscreen)'heh onscreen deaths are the only real ones
EndMethod
'what happens once it's destroyed
Method death(_onscreen)
'for gfx, ship's radius
Local rad = SHIPSCALE*(ImageWidth(gfx[0]) + ImageHeight(gfx[0]))/6
'shake the screen
Local shake = constrain(1 - (approxDist(x-p1.x,y-p1.y) / SWIDTH), 0, 1) * base.explodeShake
If shake > 0 Then game.camshake = Max(game.camshake, shake)
'find x and y speed
Local xspeed = Cos(movrot)*speed
Local yspeed = Sin(movrot)*speed
'set the color of the debris, explosions
If debrisRGB[0] = 0 And debrisRGB[1] = 0 And debrisRGB[2] = 0
debrisRGB[0] = 255
debrisRGB[1] = 255