-
Notifications
You must be signed in to change notification settings - Fork 0
/
halo chess.lua
2167 lines (2133 loc) · 88.4 KB
/
halo chess.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
--
-- HALO CHESS IMPLEMENTATION IDEAS
--
-- GOAL: Reimplement Halo Chess using user-defined functions, in order to allow
-- for checkmate functionality -- removing the need to determine the winner by
-- honor rules.
--
--
-- TODO: Spawn a "tray" of pieces behind each team. Halo Chess doesn't give you
-- waypoints on each individual enemy piece; rather, the tray contains one
-- rook, one knight, one bishop, and one queen, and you get waypoints on
-- the pieces in the enemy tray.
--
-- TODO: spawning the player into a Monitor after a move: i think official halo
-- chess uses an offset of (-15, 0, 15); try that and see if it's consistent
-- with gameplay videos. (currently we do (0, 0, 6).)
--
-- DONE:
--
-- - Piece selection (and the ability to re-select)
--
-- - You are blocked from making moves that would leave your king in check
--
-- - Target square selection
--
-- - Pawn promotion
--
-- - Check and checkmate
--
-- - Victory conditions: checkmate; killing king
--
-- - Draw conditions: stalemate; 75-turn rule; insufficient materials
--
-- - Edge-case: turn clock runs out after you've selected a piece and gained
-- control
--
alias MAX_INT = 32767
alias species_human = 0
alias species_elite = 1
alias opt_turn_clock = script_option[0]
alias species_black = script_option[1]
alias species_white = script_option[2]
alias opt_draw_rule = script_option[3]
alias opt_dim_immovable_piece_waypoints = script_option[4]
alias faction_none = 0
alias faction_black = 2 -- north
alias faction_white = 1 -- south
alias team_black = global.team[0]
alias team_white = global.team[1]
alias active_team = global.team[2]
alias piece_type_none = 0
alias piece_type_pawn = 1
alias piece_type_knight = 2
alias piece_type_bishop = 3
alias piece_type_rook = 4
alias piece_type_queen = 5
alias piece_type_king = 6
alias piece_type_dummy = -1
alias time_limit_for_finishing_off_losing_king = 30
-- General state:
alias is_script_created = object.number[5]
-- Piece data is stored on the board square that the piece is standing on, not
-- the piece biped itself. As with my Minesweeper design, all object.object
-- variables are expended on linking pieces with their neighbors, so in order
-- to track additional information (e.g. linking board spaces to their bipeds)
-- we will need to create an "extra data" object for each space, which will
-- have a one-way link to its space.
--
-- Cell-marker data:
alias coord_x = object.number[0]
alias coord_y = object.number[1]
alias piece_type = object.number[2]
alias is_valid_move = object.number[3] -- must be 0 or 1
alias owner = object.number[4]
alias threatened_by = object.number[5] -- for check(mate) processing
alias en_passant_vulnerable = object.number[6] -- pawns only; must be set on the turn the pawn double-moves, and cleared on its owner's next turn
alias space_flags = object.number[7]
alias space_flag_moving_would_self_check = 0x0001 -- moving this piece would put you in check
alias space_flag_is_threatened_by_enemy = 0x0002 -- you cannot move your king here; this space is under threat by an enemy
alias space_flag_has_no_valid_moves = 0x0004
alias space_flag_has_no_valid_moves_clear = 0x7FFB
alias space_flag_mask_cannot_move = 0x0005 -- space_flag_moving_would_self_check | space_flag_has_no_valid_moves
alias space_left = object.object[0]
alias space_right = object.object[1]
alias space_above = object.object[2]
alias space_below = object.object[3]
declare object.coord_x with network priority low
declare object.coord_y with network priority low
declare object.piece_type with network priority high = piece_type_none
declare object.owner with network priority high = faction_none
declare object.is_valid_move with network priority low = 0
declare object.threatened_by with network priority low = 0
declare object.en_passant_vulnerable with network priority low
declare object.space_flags with network priority low = 0
declare object.space_left with network priority low -- oo0
declare object.space_right with network priority low -- oo1
declare object.space_above with network priority low -- oo2
declare object.space_below with network priority low -- oo3
--
-- Cell-extra data:
alias marker = object.object[0]
alias biped = object.object[1] -- link extra to biped
alias extra = object.object[1] -- link biped to extra
--
-- Board center data:
alias piece_deselect_boundary = object.object[0]
alias weapon_delete_zone = object.object[1]
--
-- State for the kings' flags:
alias flag_was_processed = object.is_script_created
-- Global state
alias temp_int_00 = global.number[0]
alias temp_int_01 = global.number[1]
alias temp_int_02 = global.number[2]
alias board_created = global.number[3]
declare board_created with network priority low
alias active_faction = global.number[4] -- faction currently making a move
declare active_faction with network priority high = faction_none
alias turn_flags = global.number[5]
declare turn_flags with network priority low
alias temp_int_03 = global.number[6]
alias winning_faction = global.number[7]
declare winning_faction with network priority low = faction_none
alias draw_turn_count = global.number[8]
declare draw_turn_count with network priority low = 0
alias temp_int_04 = global.number[9]
--
alias turn_flag_in_check = 0x0001
--
alias temp_obj_00 = global.object[0]
alias temp_obj_01 = global.object[1]
alias temp_obj_02 = global.object[2]
alias temp_obj_03 = global.object[3]
alias selected_piece = global.object[4] -- piece that the player is (about to be) controlling
alias board_center = global.object[5]
declare board_center with network priority low
alias temp_obj_04 = global.object[6]
alias temp_obj_05 = global.object[7]
alias anchor_white = global.object[8]
alias anchor_black = global.object[9]
declare anchor_white with network priority low
declare anchor_black with network priority low
alias active_player = global.player[0] -- player currently making a move
declare active_player with network priority high -- we use it in the UI
alias temp_plr_00 = global.player[1]
alias temp_plr_01 = global.player[2]
alias temp_tem_00 = global.team[3]
alias turn_clock = global.timer[0]
declare turn_clock = opt_turn_clock
--
declare temp_int_00 with network priority low
declare temp_int_01 with network priority low
declare temp_int_02 with network priority low
declare temp_int_03 with network priority low
declare temp_int_04 with network priority low
declare temp_obj_00 with network priority low
declare temp_obj_01 with network priority low
declare temp_obj_02 with network priority low
declare temp_obj_03 with network priority low
declare temp_obj_04 with network priority low
declare temp_obj_05 with network priority low
declare temp_plr_00 with network priority low
declare temp_plr_01 with network priority low
declare temp_tem_00 with network priority low
alias announced_game_start = player.number[0]
alias ui_would_self_check = player.number[1]
alias turn_order = player.number[2]
alias did_initial_position = player.number[3] -- was the player snapped to their team's anchor at the start of the match?
alias target_space = player.object[0] -- piece the player is about to select (pending their timer)
alias selection_timer = player.timer[0]
alias announce_start_timer = player.timer[1]
declare player.announced_game_start with network priority low
declare player.ui_would_self_check with network priority low = piece_type_none
declare player.turn_order with network priority low = -1
declare player.did_initial_position with network priority low
declare player.target_space with network priority low
declare player.selection_timer = 2
declare player.announce_start_timer = 5
alias faction = team.number[0]
alias turn_order = team.number[1]
alias enemy = team.team[0]
declare team.faction with network priority low = faction_none
declare team.turn_order with network priority low = -1
declare team.enemy with network priority low = no_team
alias ui_your_turn = script_widget[0]
alias ui_in_check = script_widget[1]
alias ui_bad_move = script_widget[2]
alias ui_turn_clock = script_widget[3]
alias ui_endgame = script_widget[3] -- multi-purpose widget
alias all_flags = 2 -- forge label
alias monitor_traits = script_traits[1]
alias piece_traits = script_traits[2]
alias override_traits = script_traits[0]
--
-- SCRIPT FLOW:
--
-- - Generate board
-- - Generate initial piece arrangement
-- - Generate bipeds for spaces that are missing them
-- - If a biped is displaced from its space and not under player control, reset it
-- - Force players into Monitor bipeds
-- - ...but only if they've already spawned
-- - Remove their weapons and grenades
-- - Update UI during gameplay
-- - Handle picking a piece or picking a move
-- = Skip this processing if we are in the victory process.
-- - If there is no active faction, pick one
-- - If there is no active player (or they quit), pick one
-- - Handle moving a piece to an unoccupied space
-- - If the player is double-moving a pawn: flag as vulnerable to capturing en passant
-- - If the player is killing a king: begin victory process
-- - End turn
-- = Skip this processing if we are in the victory process.
-- - Switch to next faction and player
-- - Clear en passant vulnerability from new player's pawns
-- - Check whether new player is in check or checkmate
-- - If checkmate, begin the victory process.
-- - Award point to winner
-- - Reset (is_valid_move) and friends for all spaces
-- - If loser has no king, end round
-- - If not checkmate, pre-validate moves:
-- - Identify spaces the king cannot safely move to
-- - Identify pieces that can't be moved because they're blocking the king from being in check
-- - Manage 75-turn draw rule
-- = Same containing trigger also ends the turn if the turn clock runs out
-- - Handle piece death, if it occurs
-- - Handle moving a piece to an enemy-occupied space
-- - If the player is double-moving a pawn: flag as vulnerable to capturing en passant
-- - If the player is killing a king: begin victory process
-- - End turn
-- = Skip this processing if we are in the victory process.
-- - Switch to next faction and player
-- - Clear en passant vulnerability from new player's pawns
-- - Check whether new player is in check or checkmate
-- - If checkmate, begin the victory process.
-- - Award point to winner
-- - Reset (is_valid_move) and friends for all spaces
-- - If loser has no king, end round
-- - If not checkmate, pre-validate moves:
-- - Identify spaces the king cannot safely move to
-- - Identify pieces that can't be moved because they're blocking the king from being in check
-- - Manage 75-turn draw rule
-- - If victory process is active, handle death of the loser's king
-- - Victory process, if active
-- - Reset board space shape visibility
-- - Failsafe: end the round if the active player disappears
-- - Manage piece (in)vulnerability
-- - Manage UI
-- - Draw checks
-- - 75-turn rule (number of turns configurable via script option)
-- - Insufficient materials 1
-- - Insufficient materials 2
--
on init: do
team_white = team[0]
team_black = team[1]
team_black.enemy = team_white
team_white.enemy = team_black
team_black.faction = faction_black
team_white.faction = faction_white
end
on host migration: do
--
-- Host migration can duplicate all script-spawned bipeds, with the originals
-- dying instantly without any discernible cause and despite having been flagged
-- as invincible. Script variables which point at these bipeds become unreliable:
-- possessing these bipeds leads to spectacularly broken behavior, and trying to
-- delete them by looping over the "board_space_extra" objects fails.
--
-- The only host migrations I've tested are quits; it's possible that migrations
-- with other causes would lead to less unstable behavior.
--
for each object do
if current_object.is_of_type(spartan) or current_object.is_of_type(elite) then
current_object.delete()
end
end
turn_clock.reset() -- just to be nice
--
-- If we deleted a controlled biped, then we need to adjust game state accordingly:
--
selected_piece = no_object
end
if active_player != no_player then -- emergency trigger to detect active player quits
--
-- player.killer_type_is(quit) doesn't seem to work reliably so
-- we're just doing this lol
--
temp_int_00 = 0
for each player do
if current_player == active_player then
temp_int_00 = 1
end
end
if temp_int_00 == 0 then
active_player = no_player
selected_piece = no_object
end
end
for each player do -- announce game start
current_player.announce_start_timer.set_rate(-100%)
current_player.set_round_card_title("Control chess pieces to move.\nAchieve checkmate to win!")
if current_player.announced_game_start == 0 and current_player.announce_start_timer.is_zero() then
send_incident(action_sack_game_start, current_player, no_player)
game.show_message_to(current_player, none, "Halo Chess+ v1.0.3 by Cobb!")
current_player.announced_game_start = 1
end
end
if board_created == 0 then -- generate board
board_created = 1
--
alias board_axis_x_offset = -10
alias board_axis_y_offset = 10
alias new_row_x_offset = 0
alias new_row_y_offset = -10
alias new_row_z_offset = 0
alias new_col_x_offset = 10
alias new_col_y_offset = 0
alias new_col_z_offset = 0
alias first_cell_x_offset = -35
alias first_cell_y_offset = 35
alias first_cell_z_offset = 0
alias current = temp_obj_00 -- last-spawned cell
alias working = temp_obj_01
alias matched = temp_obj_02
alias first = temp_obj_03 -- first column in current row
function new_column()
working = current.place_at_me(block_1x1_flat, "board_space", never_garbage_collect, 0, 0, 0, none)
working.attach_to(current, new_col_x_offset, new_col_y_offset, new_col_z_offset, relative)
working.detach()
working.copy_rotation_from(board_center, true)
working.set_shape(box, 8, 8, 10, 10)
working.coord_x = current.coord_x
working.coord_y = current.coord_y
working.coord_x += 1
working.is_script_created = 1
working.space_left = current
current.space_right = working
matched = current.space_above
matched = matched.space_right
working.space_above = matched
matched.space_below = working
current = working
end
function start_new_row()
working = first.place_at_me(block_1x1_flat, "board_space", never_garbage_collect, 0, 0, 0, none)
working.attach_to(first, new_row_x_offset, new_row_y_offset, new_row_z_offset, relative)
working.detach()
working.copy_rotation_from(board_center, true)
working.set_shape(box, 8, 8, 10, 10)
working.coord_x = first.coord_x
working.coord_y = first.coord_y
working.coord_y += 1
working.is_script_created = 1
working.space_above = first
first.space_below = working
first = working
current = working
end
function finish_row()
new_column()
new_column()
new_column()
new_column()
new_column()
new_column()
new_column()
end
function construct_and_link_row()
start_new_row()
finish_row()
end
--
board_center = no_object
board_center = get_random_object("board_center", no_object)
--
anchor_white = board_center.place_at_me(hill_marker, none, never_garbage_collect, 0, 0, 0, none)
anchor_black = board_center.place_at_me(hill_marker, none, never_garbage_collect, 0, 0, 0, none)
anchor_white.attach_to(board_center, 0, -50, 0, relative)
anchor_black.attach_to(board_center, 0, 50, 0, relative)
anchor_white.detach()
anchor_black.detach()
anchor_white.face_toward(anchor_black, 0, 0, 0)
anchor_black.face_toward(anchor_white, 0, 0, 0)
--
working = board_center.place_at_me(hill_marker, none, never_garbage_collect, 0, 0, 0, none)
working.attach_to(board_center, 0, 0, 0, relative)
working.detach()
working.copy_rotation_from(board_center, true)
working.set_shape(box, 140, 140, 40, 20)
board_center.piece_deselect_boundary = working
--
working = board_center.place_at_me(hill_marker, none, never_garbage_collect, 0, 0, 0, none)
working.attach_to(board_center, 0, 0, 0, relative)
working.detach()
working.copy_rotation_from(board_center, true)
working.set_shape(box, 90, 90, 2, 0)
board_center.weapon_delete_zone = working
--
current = board_center.place_at_me(block_1x1_flat, "board_space", never_garbage_collect, 0, 0, 0, none)
current.attach_to(board_center, first_cell_x_offset, first_cell_y_offset, first_cell_z_offset, relative)
current.detach()
current.copy_rotation_from(board_center, true)
current.set_shape(box, 8, 8, 10, 10)
current.is_script_created = 1
first = current
--
finish_row()
construct_and_link_row()
construct_and_link_row()
construct_and_link_row()
construct_and_link_row()
construct_and_link_row()
construct_and_link_row()
construct_and_link_row()
--
working = board_center.place_at_me(soft_safe_boundary, none, never_garbage_collect | absolute_orientation, 0, 0, 0, none)
working.attach_to(board_center, 0, 0, 0, absolute)
working.set_shape(box, 310, 310, 200, 50)
working.set_shape_visibility(no_one)
--
for each object with label "board_space" do -- create "extra" markers
working = current_object.place_at_me(hill_marker, "board_space_extra", never_garbage_collect, 0, 0, 0, none)
working.attach_to(current_object, 0, 0, 0, relative)
working.detach()
working.copy_rotation_from(board_center, true)
working.is_script_created = 1
working.marker = current_object
end
--
-- Initialize the pieces:
--
for each object with label "board_space" do
current_object.owner = faction_none
if current_object.coord_y == 1 or current_object.coord_y == 6 then
current_object.piece_type = piece_type_pawn
end
if current_object.coord_y == 0 or current_object.coord_y == 7 then
if current_object.coord_x == 0 or current_object.coord_x == 7 then
current_object.piece_type = piece_type_rook
end
if current_object.coord_x == 1 or current_object.coord_x == 6 then
current_object.piece_type = piece_type_knight
end
if current_object.coord_x == 2 or current_object.coord_x == 5 then
current_object.piece_type = piece_type_bishop
end
if current_object.coord_x == 3 then
current_object.piece_type = piece_type_queen
end
if current_object.coord_x == 4 then
current_object.piece_type = piece_type_king
end
end
--
if current_object.piece_type != piece_type_none then
if current_object.coord_y <= 1 then
current_object.owner = faction_black
end
if current_object.coord_y >= 6 then
current_object.owner = faction_white
end
end
end
end
for each object with label "board_space_extra" do -- generate missing bipeds
alias cell = temp_obj_00
alias extra = current_object
--
cell = extra.marker
if winning_faction == faction_none or winning_faction == cell.owner then
alias biped = temp_obj_01
alias face = temp_obj_02
--
if winning_faction == faction_none and extra.biped != no_object and cell != selected_piece and not cell.shape_contains(extra.biped) then
extra.biped.attach_to(cell, 0, 0, 1, relative)
extra.biped.detach()
end
if extra.biped == no_object then
if cell.piece_type != piece_type_none then
alias species = temp_int_00
alias ownteam = temp_tem_00
--
ownteam = team_black
species = species_black
if cell.owner == faction_white then
ownteam = team_white
species = species_white
end
--
biped = no_object
function setup_biped()
biped.is_script_created = 1
biped.team = ownteam
biped.remove_weapon(secondary, true)
biped.remove_weapon(primary, true)
biped.copy_rotation_from(board_center, false)
--
face = biped.place_at_me(hill_marker, none, none, 0, 0, 0, none)
face.attach_to(biped, 0, -10, 0, relative)
face.detach()
if cell.owner == faction_white then
face.attach_to(biped, 0, 10, 0, relative)
face.detach()
end
biped.face_toward(face, 0, 0, 0)
face.delete()
end
if cell.piece_type == piece_type_pawn then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, male)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, minor)
end
setup_biped()
biped.set_waypoint_icon(skull)
biped.set_waypoint_text("Pawn")
if biped.is_of_type(spartan) then
biped.add_weapon(assault_rifle, primary)
alt
biped.add_weapon(plasma_repeater, primary)
end
end
if cell.piece_type == piece_type_knight then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, emile)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, spec_ops)
end
setup_biped()
biped.set_waypoint_icon(vip)
biped.set_waypoint_text("Knight")
if biped.is_of_type(spartan) then
biped.add_weapon(shotgun, primary)
alt
biped.add_weapon(energy_sword, primary)
end
end
if cell.piece_type == piece_type_bishop then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, female)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, zealot)
end
setup_biped()
biped.set_waypoint_icon(bullseye)
biped.set_waypoint_text("Bishop")
if biped.is_of_type(spartan) then
biped.add_weapon(grenade_launcher, primary)
alt
biped.add_weapon(concussion_rifle, primary)
end
end
if cell.piece_type == piece_type_rook then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, jun)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, space)
end
setup_biped()
biped.set_waypoint_icon(bomb)
biped.set_waypoint_text("Rook")
if biped.is_of_type(spartan) then
biped.add_weapon(sniper_rifle, primary)
alt
biped.add_weapon(beam_rifle, primary)
end
end
if cell.piece_type == piece_type_queen then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, kat)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, ultra)
end
setup_biped()
biped.set_waypoint_icon(crown)
biped.set_waypoint_text("Queen")
if biped.is_of_type(spartan) then
biped.add_weapon(rocket_launcher, primary)
alt
biped.add_weapon(plasma_launcher, primary)
end
end
if cell.piece_type == piece_type_king then
if species == species_human then
biped = cell.place_at_me(spartan, none, none, 0, 0, 1, carter)
alt
biped = cell.place_at_me(elite, none, none, 0, 0, 1, general)
end
setup_biped()
biped.set_waypoint_icon(crown)
biped.set_waypoint_text("King")
biped.add_weapon(flag, primary)
--
for each object with label all_flags do -- set flag color by assigning its team
--
-- The object.get_carrier opcode can only return a player, so we have to do
-- it this way:
--
if current_object.flag_was_processed == 0 then
current_object.flag_was_processed = 1
current_object.team = neutral_team -- best fallback we can do for single-player matches where the other team is absent
current_object.team = biped.team
end
end
end
--
biped.attach_to(cell, 0, 0, 1, relative) -- enforce position (TODO: should we leave it attached?)
biped.detach()
extra.biped = biped
biped.extra = extra
biped.marker = cell
end
end
end
end
for each object with label "board_space_extra" do -- manage piece waypoint visibility
alias extra = current_object
if extra.biped != no_object then
extra.biped.set_waypoint_visibility(allies)
extra.biped.set_waypoint_priority(normal)
--
if winning_faction == faction_none and opt_dim_immovable_piece_waypoints == 1 then
--
-- During a player's turn, fade the waypoints on allied pieces that cannot
-- be moved.
--
alias cell = temp_obj_00
cell = extra.marker
if active_faction == cell.owner then
temp_int_00 = cell.space_flags
temp_int_00 &= space_flag_mask_cannot_move
if temp_int_00 != 0 then
extra.biped.set_waypoint_priority(low)
end
end
end
end
end
for each object do -- try to delete dropped weapons
--
-- We don't have an easy way to identify whether a weapon is being held
-- by a non-player-controlled biped. As such, we'll take a shortcut: we
-- create a "weapon deletion zone" at the board, extending 0.1 Forge
-- units (1 foot) above the surface of the board. Any weapons in this
-- zone must be either dropped or being held by a player-controlled
-- biped.
--
-- We only care about weapons dropped by slain pieces, so don't bother
-- testing for types that we never spawn through script.
--
if current_object.is_of_type(assault_rifle)
or current_object.is_of_type(concussion_rifle)
or current_object.is_of_type(dmr)
or current_object.is_of_type(energy_sword)
or current_object.is_of_type(flag)
or current_object.is_of_type(focus_rifle)
or current_object.is_of_type(grenade_launcher)
or current_object.is_of_type(plasma_launcher)
or current_object.is_of_type(plasma_repeater)
or current_object.is_of_type(rocket_launcher)
or current_object.is_of_type(shotgun)
or current_object.is_of_type(sniper_rifle)
or current_object.is_of_type(frag_grenade) -- bipeds have two frags by default
or current_object.is_of_type(plasma_grenade) -- just in case
then
temp_plr_00 = current_object.get_carrier()
if temp_plr_00 == no_player then
if board_center.weapon_delete_zone.shape_contains(current_object) then
current_object.delete()
end
end
end
end
for each player do -- force players into Monitor bipeds
--
-- This trigger handles players' initial spawns and respawns. When a player relinquishes
-- control of a chess piece and becomes a Monitor again, that is because of the custom
-- quick_force_active_player_to_monitor() function defined further below.
--
alias biped = temp_obj_00
alias created = temp_obj_01
alias anchor = temp_obj_02
if current_player.biped != no_object and not current_player.biped.is_of_type(monitor) then
biped = current_player.biped
if biped.is_script_created == 0 then
created = no_object
if biped != no_object and not biped.is_out_of_bounds() and current_player.did_initial_position == 1 then
created = biped.place_at_me(monitor, none, none, 0, 0, 0, none)
created.attach_to(biped, 0, 0, 6, relative)
created.detach()
created.copy_rotation_from(biped, true)
anchor = biped
end
if created == no_object then
temp_tem_00 = current_player.team
anchor = anchor_black
if temp_tem_00.faction == faction_white then
anchor = anchor_white
end
--
created = anchor.place_at_me(monitor, none, none, -15, 0, 15, none)
end
current_player.set_biped(created)
created.copy_rotation_from(anchor, false)
current_player.did_initial_position = 1
biped.delete()
end
end
if current_player.biped.is_of_type(monitor) then -- do what we can to stop Monitors from picking stuff up
current_player.biped.remove_weapon(secondary, true)
current_player.biped.remove_weapon(primary, true)
current_player.frag_grenades = 0
current_player.plasma_grenades = 0
end
end
for each player do -- player traits
temp_obj_00 = current_player.biped
if temp_obj_00.is_of_type(monitor) then
current_player.apply_traits(monitor_traits)
end
if temp_obj_00.is_script_created == 1 then
current_player.apply_traits(piece_traits)
end
current_player.apply_traits(override_traits)
end
do -- UI
if winning_faction == faction_none then
ui_your_turn.set_text("Your Turn!")
ui_in_check.set_text("Check!")
ui_bad_move.set_text("Invalid move, try another!")
ui_bad_move.set_icon(castle_defense)
ui_turn_clock.set_text("Turn Clock: %s", turn_clock)
--
temp_int_00 = turn_flags
temp_int_00 &= turn_flag_in_check
for each player do
ui_turn_clock.set_visibility(current_player, false)
if opt_turn_clock > 0 then
ui_turn_clock.set_visibility(current_player, true)
end
--
ui_your_turn.set_visibility(current_player, false)
ui_in_check.set_visibility(current_player, false)
ui_bad_move.set_visibility(current_player, false)
if temp_int_00 != 0 and current_player.team == active_team then
ui_in_check.set_visibility(current_player, true)
end
end
--
ui_your_turn.set_visibility(active_player, true)
for each player do
if current_player != active_player then
ui_your_turn.set_visibility(current_player, false)
if current_player.team == active_player.team then
ui_your_turn.set_visibility(current_player, true)
ui_your_turn.set_text("It's %s's turn!", active_player)
end
end
end
--
if active_player.ui_would_self_check != 0 then
ui_bad_move.set_visibility(active_player, true)
if active_player.ui_would_self_check == piece_type_pawn then
ui_bad_move.set_text("You cannot move this Pawn. Doing so would place your King in check. Try another piece.")
end
if active_player.ui_would_self_check == piece_type_rook then
ui_bad_move.set_text("You cannot move this Rook. Doing so would place your King in check. Try another piece.")
end
if active_player.ui_would_self_check == piece_type_knight then
ui_bad_move.set_text("You cannot move this Knight. Doing so would place your King in check. Try another piece.")
end
if active_player.ui_would_self_check == piece_type_queen then
ui_bad_move.set_text("You cannot move this Queen. Doing so would place your King in check. Try another piece.")
end
if active_player.ui_would_self_check == piece_type_bishop then
ui_bad_move.set_text("You cannot move this Bishop. Doing so would place your King in check. Try another piece.")
end
end
end
end
function do_endgame_ui_visibility()
for each player do
ui_your_turn.set_visibility(current_player, false)
ui_in_check.set_visibility(current_player, false)
ui_bad_move.set_visibility(current_player, false)
-- ui_turn_clock and ui_endgame are the same widget
ui_endgame.set_visibility(current_player, true)
end
end
function check_valid_move()
--
-- This function can be used both for determining what squares the player is
-- allowed to move their current piece to, and determining whether a check or
-- checkmate is in progress. In the former case, you'd want to run a for loop
-- to reset object.is_valid_move to 0 for all chess squares BEFORE calling
-- this function. In the latter case, you'd run that loop once, and then call
-- this function on all of the attacker's pieces; then, check whether the
-- defender's king can move to any square for which that variable is still 0.
--
-- Note, when establishing a player's legal moves, that if a player has no
-- legal moves but is not in check, then the game ends in a draw; if they
-- have no legal moves and are in check, then they lose. Note also that if
-- the player is in check, then they are only allowed to move their king.
-- Finally, note that this function does not check whether a target space
-- would put a king in check; you will have to let the player move their
-- king, then test whether their move has put their own king in check, and
-- if so, revert the move.
--
alias current_piece = temp_obj_00 -- argument
alias target_space = temp_obj_01
alias temporary_obj = temp_obj_02
alias temporary_ob2 = temp_obj_03
alias x_diff = temp_int_00
alias y_diff = temp_int_01
--
if current_piece.piece_type == piece_type_bishop
or current_piece.piece_type == piece_type_queen
then
target_space = current_piece
function upperleft()
target_space = target_space.space_left
target_space = target_space.space_above
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
upperleft()
end
end
end
end
upperleft()
--
target_space = current_piece
function upperright()
target_space = target_space.space_right
target_space = target_space.space_above
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
upperright()
end
end
end
end
upperright()
--
target_space = current_piece
function lowerleft()
target_space = target_space.space_left
target_space = target_space.space_below
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
lowerleft()
end
end
end
end
lowerleft()
--
target_space = current_piece
function lowerright()
target_space = target_space.space_right
target_space = target_space.space_below
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
lowerright()
end
end
end
end
lowerright()
end
if current_piece.piece_type == piece_type_rook
or current_piece.piece_type == piece_type_queen
then
target_space = current_piece
function left()
target_space = target_space.space_left
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
left()
end
end
end
end
left()
--
target_space = current_piece
function right()
target_space = target_space.space_right
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
right()
end
end
end
end
right()
--
target_space = current_piece
function up()
target_space = target_space.space_above
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
up()
end
end
end
end
up()
--
target_space = current_piece
function down()
target_space = target_space.space_below
if target_space != no_object then
target_space.threatened_by += 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
if target_space.piece_type == piece_type_none then
down()
end
end
end
end
down()
end
if current_piece.piece_type == piece_type_knight then
for each object with label "board_space" do
x_diff = current_piece.coord_x
x_diff -= current_object.coord_x
y_diff = current_piece.coord_y
y_diff -= current_object.coord_y
x_diff *= y_diff
if x_diff == 2 or x_diff == -2 then -- (2 * 1) or (-2 * 1) or (2 * -1) or (-2 * -1)
current_object.threatened_by += 1
if current_object.piece_type == piece_type_none or current_object.owner != current_piece.owner then
current_object.is_valid_move = 1
end
end
end
end
if current_piece.piece_type == piece_type_king then
function _set_if() -- if we inlined this, each copy would be a separate trigger. since they're all identical, let's just make it a function so we're not compiling tons of duplicate data
target_space.threatened_by = 1
if target_space.piece_type == piece_type_none or target_space.owner != current_piece.owner then
target_space.is_valid_move = 1
end
end
target_space = current_piece.space_left
_set_if()
target_space = target_space.space_above -- upper-left
_set_if()
target_space = current_piece.space_right
_set_if()
target_space = target_space.space_below -- lower-right