-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.v
More file actions
5573 lines (5357 loc) · 175 KB
/
main.v
File metadata and controls
5573 lines (5357 loc) · 175 KB
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
module main
import math { pow }
import os
import rand
import time
import gg
import sokol.sgl
import toml
@[mingw]
#flag windows --static
// NEXT BIG STEPS:
// MULTITHREADING
// MULTIPLAYER
// PUZZLES / LEADERBOARDS
const game_data_path = 'game_data/'
const player_data_path = 'player_data/'
const sprites_path = game_data_path + 'sprites/'
const logs_path = player_data_path + 'logs'
const palette_path = player_data_path + 'palette.toml'
const font_path = game_data_path + 'fonts/0xProtoNerdFontMono-Regular.ttf'
const default_button_color = gg.Color{75, 108, 136, 255}
const default_colorchip_color_on = gg.Color{197, 209, 227, 255}
const default_colorchip_color_off = gg.Color{47, 49, 54, 255}
const default_camera_pos_x = f64(2_000_000_000.0)
const default_camera_pos_y = f64(2_000_000_000.0)
const gates_path = player_data_path + 'saved_gates/'
const maps_path = player_data_path + 'saved_maps/'
const empty_id = u64(0)
const on_bits = u64(0x2000_0000_0000_0000) // 0010_0000_000...
const elem_not_bits = u64(0x0000_0000_0000_0000) // 0000_0000_000...
const elem_diode_bits = u64(0x4000_0000_0000_0000) // 0100_0000_000...
const elem_on_bits = u64(0x8000_0000_0000_0000) // 1010_0000_000... always on
const elem_wire_bits = u64(0xC000_0000_0000_0000) // 1100_0000_000...
const elem_crossing_bits = u64(0xFFFF_FFFF_FFFF_FFFF) // 1111_1111_...
// x++=east y++=south
const north = u64(0x0)
const south = u64(0x0800_0000_0000_0000) // 0000_1000..
const west = u64(0x1000_0000_0000_0000) // 0001_000...
const east = u64(0x1800_0000_0000_0000) // 0001_100..
const rid_mask = u64(0x07FF_FFFF_FFFF_FFFF) // 0000_0111_11111... bit map to get the real id with &
const elem_type_mask = u64(0xC000_0000_0000_0000) // 1100_0000...
const id_mask = rid_mask | elem_type_mask // unique
const ori_mask = u64(0x1800_0000_0000_0000) // 0001_1000...
const chunk_size = 128
const chunk_power = 7
const chunk_bitmask = chunk_size - 1
const chunk_inv_bitmask = ~chunk_bitmask
const invalid_coo = u32(-1)
const cardinal_coords = [[0, 1]!, [0, -1]!, [1, 0]!, [-1, 0]!]!
const diode_poly_unscaled = [
[f32(0.2), 1.0, 0.4, 0.0, 0.6, 0.0, 0.8, 1.0], // north
[f32(0.2), 0.0, 0.8, 0.0, 0.6, 1.0, 0.4, 1.0], // south
[f32(1.0), 0.2, 1.0, 0.8, 0.0, 0.6, 0.0, 0.4], // west
[f32(0.0), 0.2, 1.0, 0.4, 1.0, 0.6, 0.0, 0.8], // east
]
const not_rect_unscaled = [// x, y, width, height
[f32(0.33), 0.0, 0.33, 0.2], // north
[f32(0.33), 0.8, 0.33, 0.2], // south
[f32(0.0), 0.33, 0.2, 0.33], // west
[f32(0.8), 0.33, 0.2, 0.33], // east
]
const not_poly_unscaled = [
[f32(0.2), 1.0, 0.5, 0.0, 0.8, 1.0], // north
[f32(0.2), 0.0, 0.8, 0.0, 0.5, 1.0], // south
[f32(1.0), 0.2, 1.0, 0.8, 0.0, 0.5], // west
[f32(0.0), 0.2, 1.0, 0.5, 0.0, 0.8], // east
]
const on_poly_unscaled = [
[f32(0.2), 0.6, 0.4, 0.0, 0.6, 0.0, 0.8, 0.6], // north
[f32(0.2), 0.4, 0.8, 0.4, 0.6, 1.0, 0.4, 1.0], // south
[f32(0.6), 0.2, 0.6, 0.8, 0.0, 0.6, 0.0, 0.4], // west
[f32(0.4), 0.2, 1.0, 0.4, 1.0, 0.6, 0.4, 0.8], // east
]
const nots_array_default = [Nots{
x: invalid_coo
}]
const diode_array_default = [Diode{
x: invalid_coo
}]
const wire_array_default = [Wire{
cable_coords: [Coo{invalid_coo, 0}]
}]
// UI sizes
const default_win_height = f32(800.0)
const default_win_width = f32(1422.0)
const log_cfg = gg.TextCfg{
size: 16
color: gg.black
}
const log_width = 200
const log_interline_spacing = 4
const log_border = 5
const selected_cc_circle_r = 6
const input_box_off = 10.0
const input_text_off_x = 15.0
const input_text_off_y = 10.0
const input_box_w = 250.0
const input_box_h = 35.0
const info_text_off = 10.0
const info_text_spacing = 20.0
const button_solo_w = f32(244.0)
const button_solo_h = f32(125.0)
const button_solo_x = f32(900.0) - button_solo_w / 2.0
const button_solo_y = f32(500.0) - button_solo_h / 2.0
const button_quit_size = f32(50.0)
const btn_quit_ofst = f32(20.0)
const btn_quit_y = f32(700.0)
const maps_x_offset = f32(50.0)
const maps_y_offset = f32(50.0)
const maps_top_spacing = f32(0.0)
const maps_w = f32(500.0)
const button_new_map_x = f32(5.0)
const button_new_map_y = f32(5.0)
const button_new_map_size = f32(40.0)
const maps_h = button_new_map_size
const btn_back_x = f32(5.0)
const btn_back_y = f32(50.0)
const btn_back_s = f32(40.0)
const text_field_x = f32(50.0)
const text_field_y = f32(5.0)
const editmenu_offset_x = default_win_width * 2 / 3
const editmenu_offset_y = f32(30.0)
const editmenu_offset_inputs_x = f32(60.0)
const editmenu_offset_inputs_y = f32(10.0)
const editmenu_offset_colors_x = f32(60.0)
const editmenu_offset_colors_y = f32(120.0)
const editmenu_inputsize = f32(20.0)
const editmenu_input_x_space = f32(5.0)
const editmenu_rgb_y = f32(60.0)
const editmenu_rgb_h = f32(40.0)
const editmenu_rgb_w = f32(40.0)
const editmenu_r_x = f32(60.0)
const editmenu_g_x = f32(110.0)
const editmenu_b_x = f32(160.0)
const editmenu_colorsize = f32(20.0)
const editmenu_colorborder = f32(6.0)
const gate_text_off_x = 10.0
const gate_x_ofst = f32(5.0)
const gate_y_offset = f32(50.0)
const gate_top_spacing = f32(10.0)
const gate_h = f32(50.0)
const gate_w = f32(500.0)
const ui_width = f32(50.0)
const button_size = f32(40.0)
const button_left_padding = f32(5.0)
const button_top_padding = f32(5.0)
enum Buttons {
cancel_button // escapes modes
confirm_save_gate // save gate mode
selection_button // no mode
rotate_copy // paste mode
copy_button // selection mode
choose_colorchip // edit mode
load_gate // no/paste
save_gate // selection
edit_color // edit
item_nots // no/placement
create_color_chip // selection
add_input // edit
item_diode // no/placement
copy_settings // edit
item_crossing // no/placement
delete_colorchip // edit
item_on // no/placement
item_wire // no/placement
speed // no
slow // no
pause // no
paste // no/selection
save_map // no
keyinput // no
hide_colorchips // no
quit_map // no
selection_delete // selection
flip_h // paste
flip_v // paste
trash
cc_mode // no
}
const selec_buttons = [Buttons.cancel_button, .copy_button, .save_gate, .create_color_chip,
.selection_delete, .paste]
const no_mode_buttons = [Buttons.cancel_button, .selection_button, .load_gate, .item_nots,
.item_diode, .item_crossing, .item_on, .item_wire, .speed, .slow, .pause, .paste, .save_map,
.keyinput, .hide_colorchips, .cc_mode, .quit_map]
const save_gate_buttons = [Buttons.cancel_button, .confirm_save_gate]
const paste_buttons = [Buttons.cancel_button, .rotate_copy, .load_gate, .flip_h, .flip_v]
const placement_buttons = [Buttons.cancel_button, .item_nots, .item_diode, .item_crossing, .item_on,
.item_wire]
const edit_buttons = [Buttons.cancel_button, .choose_colorchip, .edit_color, .add_input,
.copy_settings, .delete_colorchip, .hide_colorchips]
struct ButtonData {
mut:
pos u32
img gg.Image
}
struct Palette {
mut:
junc gg.Color = gg.Color{0, 0, 0, 255}
junc_v gg.Color = gg.Color{255, 217, 46, 255} // vertical line
junc_h gg.Color = gg.Color{190, 92, 247, 255} // horiz line
wire_on gg.Color = gg.Color{131, 247, 92, 255}
wire_off gg.Color = gg.Color{247, 92, 92, 255}
on gg.Color = gg.Color{89, 181, 71, 255}
not gg.Color = gg.Color{247, 152, 239, 255}
diode gg.Color = gg.Color{92, 190, 247, 255}
background gg.Color = gg.Color{255, 235, 179, 255}
place_preview gg.Color = gg.Color{128, 128, 128, 128}
copy_preview gg.Color = gg.Color{255, 255, 255, 128}
selection_start gg.Color = gg.Color{26, 107, 237, 128}
selection_end gg.Color = gg.Color{72, 97, 138, 128}
selection_box gg.Color = gg.Color{66, 136, 245, 128}
input_preview gg.Color = gg.Color{217, 159, 0, 128}
selected_ui gg.Color = gg.Color{128, 128, 128, 64}
ui_bg gg.Color = gg.Color{255, 255, 255, 255}
grid gg.Color = gg.Color{232, 217, 203, 255}
}
const palette_def = Palette{
wire_off: gg.Color{239, 71, 111, 255}
wire_on: gg.Color{6, 214, 160, 255}
background: gg.Color{252, 252, 252, 255}
diode: gg.Color{31, 176, 255, 255}
not: gg.Color{255, 209, 102, 255}
on: gg.Color{37, 248, 157, 255}
junc: gg.Color{30, 33, 43, 255}
junc_v: gg.Color{255, 220, 92, 255}
junc_h: gg.Color{255, 132, 39, 255}
ui_bg: gg.Color{234, 235, 235, 255}
}
struct ColorChip {
x u32
y u32
w u32
h u32
mut:
colors []gg.Color // colors to show
inputs []Coo // the state will be converted to a number (binary) and it will be the index of the shown color
}
const button_map = {
Buttons.cancel_button: ButtonData{
pos: 0
}
.confirm_save_gate: ButtonData{
pos: 1
}
.selection_button: ButtonData{
pos: 1
}
.rotate_copy: ButtonData{
pos: 1
}
.copy_button: ButtonData{
pos: 1
}
.choose_colorchip: ButtonData{
pos: 1
}
.load_gate: ButtonData{
pos: 2
}
.save_gate: ButtonData{
pos: 2
}
.edit_color: ButtonData{
pos: 2
}
.flip_h: ButtonData{
pos: 3
}
.item_nots: ButtonData{
pos: 3
}
.create_color_chip: ButtonData{
pos: 3
}
.add_input: ButtonData{
pos: 3
}
.flip_v: ButtonData{
pos: 4
}
.item_diode: ButtonData{
pos: 4
}
.copy_settings: ButtonData{
pos: 4
}
.item_crossing: ButtonData{
pos: 5
}
.delete_colorchip: ButtonData{
pos: 5
}
.item_on: ButtonData{
pos: 6
}
.item_wire: ButtonData{
pos: 7
}
.speed: ButtonData{
pos: 8
}
.slow: ButtonData{
pos: 9
}
.pause: ButtonData{
pos: 10
}
.paste: ButtonData{
pos: 11
}
.save_map: ButtonData{
pos: 12
}
.keyinput: ButtonData{
pos: 13
}
.hide_colorchips: ButtonData{
pos: 14
}
.cc_mode: ButtonData{
pos: 15
}
.quit_map: ButtonData{
pos: 16
}
.selection_delete: ButtonData{
pos: 7
}
.trash: ButtonData{}
}
struct App {
mut:
ctx &gg.Context = unsafe { nil }
e &gg.Event = &gg.Event{}
s gg.Size // screen size
ui f32 = 1.0 // ui scale, factor by which all ui is multiplied
draw_count int = 1
draw_step int = 20000 // number of draw calls before .end( passthru
tile_size int = 30
text_input string // holds what the user typed
colorchips_hidden bool // if colorchips are hidden
mouse_down bool
mouse_map_x u32
mouse_map_y u32
mouse_action string
scroll_pos f32
debug_mode bool
// main menu
main_menu bool
solo_img gg.Image
banner_img gg.Image
// solo menu TODO: display map info of the hovered map (size bits, nb of hours played, gates placed... fun stuff)
solo_menu bool
map_delete_nb int = -1
map_names_list []string // without folder name
// edit mode -> edit colorchips
edit_mode bool
editmenu_nb_color_by_row int = 20
editmenu_selected_color int // TODO: show coords of input
editmenu_nb_inputs_by_row int = 16
editmenu_hold int
delete_colorchip_submode bool
create_colorchip_submode bool // select start and end of the new colorchip
choose_colorchip_submode bool // select a colorchip to edit
copy_settings_submode bool
add_input_submode bool // to add an input to a colorchip
edit_color_submode bool // edit colors of a colorchip
selected_colorchip int = -1 // index of the selected colorchip
// test mode
test_mode bool
// camera moving -> default mode
cam_x f64 = default_camera_pos_x
cam_y f64 = default_camera_pos_y
move_down bool
click_x f32 // Holds the position of the click (start point of the camera movement)
click_y f32
drag_x f32 // Holds the actual position of the click (to be able to render the moved map even if the camera movement is not yet finished (by releasing the mouse))
drag_y f32
// placement mode
placement_mode bool
place_down bool
place_start_x u32 = u32(-1)
place_start_y u32
place_end_x u32 // Only used for preview
place_end_y u32
// selection mode (left click: starting pos of selection, right click: ending position of selection)
selection_mode bool
select_start_x u32 = u32(-1)
select_start_y u32
select_end_x u32
select_end_y u32
// paste mode
paste_mode bool
// load gate mode
load_gate_mode bool
gate_name_list []string // without folder name
// save gate mode
save_gate_mode bool
// keyboard input (force state of a gate to ON) mode
keyinput_mode bool
key_pos map[u8][]Coo // `n` -> [[0, 3], [32, 53]] : will force the state to ON at x:0 y:3 and x:32 y:53 if the key is pressed
tmp_pos_x u32 = u32(-1)
tmp_pos_y u32 = u32(-1)
// UI on the left border
buttons map[Buttons]ButtonData = button_map.clone()
log []string
log_border gg.Color
log_timer int
not_on_img gg.Image
not_off_img gg.Image
diode_on_img gg.Image
diode_off_img gg.Image
wire_on_img gg.Image
wire_off_img gg.Image
junc_img gg.Image
on_img gg.Image
not_on_coo []f32
not_off_coo []f32
diode_on_coo []f32
diode_off_coo []f32
wire_on_coo []f32
wire_off_coo []f32
junc_coo []f32
on_coo []f32
// logic
map []Chunk
cl_thread thread
map_name string // to fill when loading a map
comp_running bool // is a map loaded and running
pause bool // is the map updating
nb_updates i32 = 5 // number of updates per second
avg_update_time f64 // nanosecs
todo []TodoInfo
selected_item Elem
selected_ori u64 = north
copied []PlaceInstruction
actual_state i32 // indicate which list is the old state list and which is the actual one, 0 for the first, 1 for the second
nots []Nots = nots_array_default.clone() // to start the rid at 1
n_states [2][]bool = [[false], [false]]! // the old state and the actual state list
dead_nots []u64 // stores the rid of the dead nots (with invalid_coo, the ones that were removed)
dead_nots_lower i64 // marks the beggining of the valid dead nots (invalids not removed from the array)
diodes []Diode = diode_array_default.clone()
d_states [2][]bool = [[false], [false]]!
dead_diodes []u64
dead_diodes_lower i64
wires []Wire = wire_array_default.clone()
w_states [2][]bool = [[false], [false]]!
dead_wires []u64
dead_wires_lower i64
forced_states []Coo // forced to ON state by a keyboard input
colorchips []ColorChip // screens
palette Palette = palette_def // TODO: edit palette and save palette
comp_alive bool
chunk_cache map[u64]i32 // x u32 y u32 -> index of app.map
}
// graphics
fn main() {
mut app := &App{}
app.main_menu = true
app.log('Start: ${time.now()}', .info)
app.ctx = gg.new_context(
create_window: true
window_title: 'Not Gates'
user_data: app
init_fn: on_init
frame_fn: on_frame
event_fn: on_event
sample_count: 0
bg_color: app.palette.background
font_path: font_path
)
app.ctx.run()
}
fn on_init(mut app App) {
app.init_graphics() or { app.log_quit('${err}') }
}
fn (mut app App) init_graphics() ! {
// lancement du programme/de la fenêtre
unsafe {
app.buttons[.cancel_button].img = app.ctx.create_image(sprites_path + 'cancel_button.png')!
app.buttons[.confirm_save_gate].img = app.ctx.create_image(sprites_path +
'confirm_save_gate.png')!
app.buttons[.selection_button].img = app.ctx.create_image(sprites_path +
'selection_button.png')!
app.buttons[.rotate_copy].img = app.ctx.create_image(sprites_path + 'rotate_copy.png')!
app.buttons[.copy_button].img = app.ctx.create_image(sprites_path + 'copy_button.png')!
app.buttons[.choose_colorchip].img = app.ctx.create_image(sprites_path +
'choose_colorchip.png')!
app.buttons[.load_gate].img = app.ctx.create_image(sprites_path + 'load_gate.png')!
app.buttons[.save_gate].img = app.ctx.create_image(sprites_path + 'save_gate.png')!
app.buttons[.edit_color].img = app.ctx.create_image(sprites_path + 'edit_color.png')!
app.buttons[.item_nots].img = app.ctx.create_image(sprites_path + 'not_off.png')!
app.buttons[.create_color_chip].img = app.ctx.create_image(sprites_path +
'create_color_chip.png')!
app.buttons[.add_input].img = app.ctx.create_image(sprites_path + 'add_input.png')!
app.buttons[.item_diode].img = app.ctx.create_image(sprites_path + 'diode_off.png')!
app.buttons[.copy_settings].img = app.ctx.create_image(sprites_path + 'steal_settings.png')!
app.buttons[.item_crossing].img = app.ctx.create_image(sprites_path + 'junction.png')!
app.buttons[.delete_colorchip].img = app.ctx.create_image(sprites_path +
'delete_colorchip.png')!
app.buttons[.item_on].img = app.ctx.create_image(sprites_path + 'on.png')!
app.buttons[.item_wire].img = app.ctx.create_image(sprites_path + 'wire_on.png')!
app.buttons[.speed].img = app.ctx.create_image(sprites_path + 'speed.png')!
app.buttons[.slow].img = app.ctx.create_image(sprites_path + 'slow.png')!
app.buttons[.pause].img = app.ctx.create_image(sprites_path + 'pause.png')!
app.buttons[.paste].img = app.ctx.create_image(sprites_path + 'paste.png')!
app.buttons[.save_map].img = app.ctx.create_image(sprites_path + 'save_map.png')!
app.buttons[.keyinput].img = app.ctx.create_image(sprites_path + 'keyinput.png')!
app.buttons[.hide_colorchips].img = app.ctx.create_image(sprites_path +
'hide_colorchips.png')!
app.buttons[.quit_map].img = app.ctx.create_image(sprites_path + 'quit_map.png')!
app.buttons[.cc_mode].img = app.ctx.create_image(sprites_path + 'edit_color.png')!
app.buttons[.flip_h].img = app.ctx.create_image(sprites_path + 'flip_h.png')!
app.buttons[.flip_v].img = app.ctx.create_image(sprites_path + 'flip_v.png')!
app.buttons[.selection_delete].img = app.ctx.create_image(sprites_path +
'selection_delete.png')!
app.buttons[.trash].img = app.ctx.create_image(sprites_path + 'trash.png')!
}
app.not_on_img = app.ctx.create_image(sprites_path + 'not_on.png')!
app.not_off_img = app.ctx.create_image(sprites_path + 'not_off.png')!
app.diode_on_img = app.ctx.create_image(sprites_path + 'diode_on.png')!
app.diode_off_img = app.ctx.create_image(sprites_path + 'diode_off.png')!
app.wire_on_img = app.ctx.create_image(sprites_path + 'wire_on.png')!
app.wire_off_img = app.ctx.create_image(sprites_path + 'wire_off.png')!
app.junc_img = app.ctx.create_image(sprites_path + 'junction.png')!
app.on_img = app.ctx.create_image(sprites_path + 'on.png')!
app.banner_img = app.ctx.create_image(sprites_path + 'banner.png')!
app.solo_img = app.ctx.create_image(sprites_path + 'play.png')!
app.load_palette()
}
fn toml_palette_color(color string, doc toml.Doc) gg.Color {
a := doc.value(color).array()
return gg.Color{u8(a[0].u64()), u8(a[1].u64()), u8(a[2].u64()), u8(a[3].u64())}
}
fn (mut app App) load_palette() {
doc := toml.parse_file(palette_path) or {
app.log('Loading palette: ${err}', .err)
return
}
app.palette.background = toml_palette_color('background', doc)
app.palette.place_preview = toml_palette_color('place_preview', doc)
app.palette.copy_preview = toml_palette_color('copy_preview', doc)
app.palette.selection_start = toml_palette_color('selection_start', doc)
app.palette.selection_end = toml_palette_color('selection_end', doc)
app.palette.selection_box = toml_palette_color('selection_box', doc)
app.palette.input_preview = toml_palette_color('input_preview', doc)
app.palette.selected_ui = toml_palette_color('selected_ui', doc)
app.palette.ui_bg = toml_palette_color('ui_bg', doc)
app.palette.grid = toml_palette_color('grid', doc)
}
fn (app App) scale_sprite(a [][]f32) [][]f32 {
mut new_a := [][]f32{len: a.len, init: []f32{len: a[0].len}}
for i, dir_a in a {
for j, coo in dir_a {
new_a[i][j] = coo * app.tile_size
}
}
return new_a
}
fn on_frame(mut app App) {
app.mouse_map_x = u32(app.cam_x + app.ctx.mouse_pos_x / app.tile_size)
app.mouse_map_y = u32(app.cam_y + app.ctx.mouse_pos_y / app.tile_size)
app.draw_count = 1
app.s = app.ctx.window_size()
app.ui = f32(app.s.height) / f32(default_win_height)
// Draw
app.ctx.begin()
app.ctx.draw_rect_filled(0, 0, app.s.width, app.s.height, app.palette.background)
mut ui_log_cfg := gg.TextCfg{
size: int(log_cfg.size * app.ui)
color: log_cfg.color
}
if app.comp_running {
if app.selected_colorchip < -1 || app.selected_colorchip > app.colorchips.len {
app.selected_colorchip = -1
}
if app.edit_mode && app.mouse_down {
if app.move_down {
} else if app.e.mouse_x < app.ui * ui_width {
} else {
if app.edit_color_submode && app.selected_colorchip != -1
&& (app.editmenu_hold >= 20 || app.editmenu_hold % 5 == 0) {
app.check_and_change_color_cc(app.e.mouse_x, app.e.mouse_y)
}
app.editmenu_hold += 1
}
}
app.draw_map()
if !app.colorchips_hidden {
virt_cam_x := app.cam_x - (app.drag_x - app.click_x) / app.tile_size
virt_cam_y := app.cam_y - (app.drag_y - app.click_y) / app.tile_size
for j, cc in app.colorchips {
pos_x := f32((f64(cc.x) - virt_cam_x) * app.tile_size)
pos_y := f32((f64(cc.y) - virt_cam_y) * app.tile_size)
w := f32(int(cc.w) * app.tile_size)
h := f32(int(cc.h) * app.tile_size)
mut color_i := 0
mut chunk_i := app.get_chunkmap_idx_at_coords(cc.x, cc.y)
mut chunkmap := &app.map[chunk_i].id_map
mut xmap := cc.x & chunk_bitmask
mut ymap := cc.y & chunk_bitmask
mut id := unsafe { chunkmap[xmap * chunk_size + ymap] }
mut last_cm_x := cc.x
mut last_cm_y := cc.y
for i, coo in cc.inputs {
if check_change_chunkmap(last_cm_x, last_cm_y, coo.x, coo.y) {
last_cm_x = cc.x
last_cm_y = cc.y
chunk_i = app.get_chunkmap_idx_at_coords(coo.x, coo.y)
}
chunkmap = &app.map[chunk_i].id_map
xmap = coo.x & chunk_bitmask
ymap = coo.y & chunk_bitmask
id = unsafe { chunkmap[xmap * chunk_size + ymap] }
if id == elem_crossing_bits || id == empty_id {
continue
}
if id & elem_type_mask == elem_on_bits || id & on_bits != 0 {
color_i += int(pow(2, i))
}
}
app.ctx.draw_rect_filled(pos_x, pos_y, w, h, cc.colors[color_i])
if app.edit_mode && j == app.selected_colorchip {
app.ctx.draw_circle_filled(pos_x, pos_y, selected_cc_circle_r, gg.red)
}
}
}
// placing preview
if app.placement_mode && app.place_start_x != u32(-1) { // did not hide the check to be able to see when it is happening
app.draw_placing_preview()
}
if app.selection_mode {
app.draw_selection_box()
}
if app.keyinput_mode {
app.draw_input_buttons()
}
if app.paste_mode {
app.draw_paste_preview()
}
app.draw_ingame_ui_buttons()
compute_info := '${app.nb_updates}/s = ${int(time.second / app.nb_updates) / 1_000_000}ms/update (required:${app.avg_update_time / 1_000_000.0:.2f}ms)'
coords_info := 'x:${i64(app.cam_x)} y:${i64(app.cam_y)}'
info_text_x := int((ui_width + 10) * app.ui)
app.ctx.draw_text(info_text_x, int(info_text_off * app.ui), app.map_name, ui_log_cfg)
app.ctx.draw_text(info_text_x, int((info_text_off + info_text_spacing) * app.ui),
compute_info, ui_log_cfg)
app.ctx.draw_text(info_text_x, int((info_text_off + 2 * info_text_spacing) * app.ui),
coords_info, ui_log_cfg)
app.ctx.draw_text(info_text_x, app.s.height - int(info_text_spacing * app.ui),
app.mouse_action, ui_log_cfg)
if app.save_gate_mode {
input_x := (ui_width + input_box_off) * app.ui
box_y := input_box_off * app.ui
box_w := input_box_w * app.ui
box_h := input_box_h * app.ui
text_x := int((ui_width + input_text_off_x) * app.ui)
text_y := int(box_y + input_text_off_y * app.ui)
app.ctx.draw_rect_filled(input_x, box_y, box_w, box_h, app.palette.ui_bg)
app.ctx.draw_text(text_x, text_y, 'Save gate: ${app.text_input}', ui_log_cfg)
}
if app.load_gate_mode {
input_x := (ui_width + input_box_off) * app.ui
box_y := input_box_off * app.ui
box_w := input_box_w * app.ui
box_h := input_box_h * app.ui
text_x := int((ui_width + input_text_off_x) * app.ui)
text_y := int(box_y + input_text_off_y * app.ui)
app.ctx.draw_rect_filled(input_x, box_y, box_w, box_h, app.palette.ui_bg)
app.ctx.draw_text(text_x, text_y, 'Load gate: ${app.text_input}', ui_log_cfg)
// search results
if !os.exists(gates_path) {
if !os.exists(player_data_path) {
os.mkdir(player_data_path) or {
app.log('Cannot create ${player_data_path}, ${err}', .err)
return
}
}
os.mkdir(gates_path) or {
app.log('Cannot create ${gates_path}, ${err}', .err)
return
}
}
app.gate_name_list = os.ls(gates_path) or {
app.log('cannot list files in ${gates_path}, ${err}', .err)
[]string{}
}
x := (ui_width + gate_x_ofst) * app.ui
total_h := (gate_top_spacing + gate_h) * app.ui
w := (ui_width + gate_x_ofst + gate_w) * app.ui
for pos, name in app.gate_name_list.filter(it.contains(app.text_input)) { // the maps are filtered with the search field
y := pos * total_h + gate_y_offset * app.ui
y_text := int(y + gate_text_off_x * app.ui)
app.ctx.draw_rect_filled(x, y, w, gate_h * app.ui, app.palette.ui_bg)
app.ctx.draw_text(int(x), y_text, name, ui_log_cfg)
}
}
if app.edit_mode {
if app.edit_color_submode && app.selected_colorchip != -1 {
last_rel_input_y := (app.colorchips[app.selected_colorchip].inputs.len - 1) / app.editmenu_nb_inputs_by_row * app.ui * editmenu_inputsize
x_bg := app.ui * (editmenu_offset_x + editmenu_offset_colors_x / 2)
y_bg := app.ui * editmenu_offset_y
h_bg := app.ui * (editmenu_offset_y + editmenu_offset_colors_y) +
(app.colorchips[app.selected_colorchip].colors.len / app.editmenu_nb_color_by_row +
1) * app.ui * editmenu_colorsize + last_rel_input_y - y_bg + editmenu_colorsize
w_bg := app.ui * 1000
app.ctx.draw_rect_filled(x_bg, y_bg, w_bg, h_bg, app.palette.ui_bg)
for i in 0 .. app.colorchips[app.selected_colorchip].inputs.len {
x := app.ui * (editmenu_offset_inputs_x + editmenu_offset_x) +
i % app.editmenu_nb_inputs_by_row * app.ui * (editmenu_inputsize +
editmenu_input_x_space)
y := app.ui * (editmenu_offset_inputs_y + editmenu_offset_y) +
i / app.editmenu_nb_inputs_by_row * app.ui * editmenu_inputsize
size := app.ui * editmenu_inputsize
app.ctx.draw_square_filled(x, y, size, app.palette.input_preview)
app.ctx.draw_text(int(x), int(y), i.str(), ui_log_cfg)
}
for i, color in app.colorchips[app.selected_colorchip].colors {
x := app.ui * (editmenu_offset_x + editmenu_offset_colors_x) +
i % app.editmenu_nb_color_by_row * app.ui * editmenu_colorsize
y := app.ui * (editmenu_offset_y + editmenu_offset_colors_y) +
i / app.editmenu_nb_color_by_row * app.ui * editmenu_colorsize +
last_rel_input_y
size := app.ui * editmenu_colorsize
app.ctx.draw_square_filled(x, y, size, color)
}
// Selected color
if app.editmenu_selected_color >= 0
&& app.editmenu_selected_color < app.colorchips[app.selected_colorchip].colors.len {
x := app.ui * (editmenu_offset_x + editmenu_offset_colors_x) +
app.editmenu_selected_color % app.editmenu_nb_color_by_row * app.ui * editmenu_colorsize
y := app.ui * (editmenu_offset_y + editmenu_offset_colors_y) +
app.editmenu_selected_color / app.editmenu_nb_color_by_row * app.ui * editmenu_colorsize +
last_rel_input_y
size := app.ui * editmenu_colorsize
border := app.ui * editmenu_colorborder
app.ctx.draw_square_filled(x - border, y - border, size + 2 * border,
app.colorchips[app.selected_colorchip].colors[app.editmenu_selected_color])
app.ctx.draw_square_empty(x - border, y - border, size + 2 * border,
gg.rgb(39, 188, 235))
}
// rgb picker
rgb_y := app.ui * (editmenu_rgb_y + editmenu_offset_y)
rgb_h := app.ui * editmenu_rgb_h
rgb_w := app.ui * editmenu_rgb_w
r_x := app.ui * (editmenu_r_x + editmenu_offset_x)
g_x := app.ui * (editmenu_g_x + editmenu_offset_x)
b_x := app.ui * (editmenu_b_x + editmenu_offset_x)
app.ctx.draw_rect_filled(r_x, rgb_y, rgb_w, rgb_h, gg.red)
app.ctx.draw_rect_filled(g_x, rgb_y, rgb_w, rgb_h, gg.green)
app.ctx.draw_rect_filled(b_x, rgb_y, rgb_w, rgb_h, gg.Color{64, 128, 255, 255})
selected_color := app.colorchips[app.selected_colorchip].colors[app.editmenu_selected_color]
app.ctx.draw_text(int(r_x), int(rgb_y), selected_color.r.str(), ui_log_cfg)
app.ctx.draw_text(int(g_x), int(rgb_y), selected_color.g.str(), ui_log_cfg)
app.ctx.draw_text(int(b_x), int(rgb_y), selected_color.b.str(), ui_log_cfg)
}
if app.selected_colorchip != -1 {
virt_cam_x := app.cam_x - (app.drag_x - app.click_x) / app.tile_size
virt_cam_y := app.cam_y - (app.drag_y - app.click_y) / app.tile_size
half := app.tile_size / 2
quar := half / 2
for i, inp in app.colorchips[app.selected_colorchip].inputs {
pos_x := f32((f64(inp.x) - virt_cam_x) * app.tile_size)
pos_y := f32((f64(inp.y) - virt_cam_y) * app.tile_size)
// check if input button is selected
x := app.ui * (editmenu_offset_inputs_x + editmenu_offset_x) +
i % app.editmenu_nb_inputs_by_row * app.ui * (editmenu_inputsize +
editmenu_input_x_space)
y := app.ui * (editmenu_offset_inputs_y + editmenu_offset_y) +
i / app.editmenu_nb_inputs_by_row * app.ui * editmenu_inputsize
if app.edit_color_submode && app.e.mouse_x >= x
&& app.e.mouse_x < x + app.ui * editmenu_inputsize && app.e.mouse_y >= y
&& app.e.mouse_y < y + app.ui * editmenu_inputsize {
app.ctx.draw_square_filled(pos_x, pos_y, app.tile_size, app.palette.input_preview)
} else {
app.ctx.draw_square_filled(pos_x + quar, pos_y + quar, half, app.palette.input_preview)
}
app.ctx.draw_text(int(pos_x + half - ui_log_cfg.size / 4), int(pos_y + half - ui_log_cfg.size / 2),
i.str(), ui_log_cfg)
}
}
}
if app.debug_mode {
x := app.mouse_map_x
y := app.mouse_map_y
mut chunk_i := app.get_chunkmap_idx_at_coords(x, y)
mut chunkmap := &app.map[chunk_i].id_map
x_map := x & chunk_bitmask
y_map := y & chunk_bitmask
id := unsafe { chunkmap[x_map * chunk_size + y_map] }
rid := id & rid_mask
tile_mid := app.tile_size / 2
if id == 0x0 { // map empty
} else if id == elem_crossing_bits { // same bits as wires so need to be separated
} else {
match id & elem_type_mask {
elem_wire_bits {
w := app.wires[rid]
// println('Wire inps: ${w.inps.map('${it:064b}')} outs: ${w.outs.map('${it:064b}')} cable_coords: ${w.cable_coords} cable_chunk_i: ${w.cable_chunk_i}')
for inp in w.inps {
info := app.get_info(inp)
for c in info.coos {
inp_x := f32((f64(c.x) - app.cam_x) * app.tile_size) + tile_mid
inp_y := f32((f64(c.y) - app.cam_y) * app.tile_size) + tile_mid
app.ctx.draw_circle_empty(inp_x, inp_y, 10, gg.yellow)
}
}
for out in w.outs {
info := app.get_info(out)
for c in info.coos {
inp_x := f32((f64(c.x) - app.cam_x) * app.tile_size) + tile_mid
inp_y := f32((f64(c.y) - app.cam_y) * app.tile_size) + tile_mid
app.ctx.draw_circle_empty(inp_x, inp_y, 10, gg.purple)
}
}
}
elem_not_bits {
n := app.nots[rid]
pos_x := f32((f64(x) - app.cam_x) * app.tile_size) + tile_mid
pos_y := f32((f64(y) - app.cam_y) * app.tile_size) + tile_mid
if n.x == invalid_coo {
app.ctx.draw_circle_empty(pos_x, pos_y, 20, gg.red)
}
app.ctx.draw_circle_empty(pos_x, pos_y, 10, gg.blue)
// input info
info := app.get_info(n.inp)
if info.dead {
app.ctx.draw_circle_filled(app.e.mouse_x, app.e.mouse_y, 10,
gg.black)
} else {
app.ctx.draw_circle_empty(app.e.mouse_x, app.e.mouse_y, 10,
gg.white)
}
for c in info.coos {
inp_x := f32((f64(c.x) - app.cam_x) * app.tile_size) + tile_mid
inp_y := f32((f64(c.y) - app.cam_y) * app.tile_size) + tile_mid
app.ctx.draw_circle_empty(inp_x, inp_y, 10, gg.yellow)
}
}
elem_diode_bits {
d := app.diodes[rid]
pos_x := f32((f64(x) - app.cam_x) * app.tile_size) + tile_mid
pos_y := f32((f64(y) - app.cam_y) * app.tile_size) + tile_mid
if d.x == invalid_coo {
app.ctx.draw_circle_empty(pos_x, pos_y, 20, gg.red)
}
app.ctx.draw_circle_empty(pos_x, pos_y, 10, gg.blue)
// input info
info := app.get_info(d.inp)
if info.dead {
app.ctx.draw_circle_filled(app.e.mouse_x, app.e.mouse_y, 10,
gg.black)
} else {
app.ctx.draw_circle_empty(app.e.mouse_x, app.e.mouse_y, 10,
gg.white)
}
for c in info.coos {
inp_x := f32((f64(c.x) - app.cam_x) * app.tile_size) + tile_mid
inp_y := f32((f64(c.y) - app.cam_y) * app.tile_size) + tile_mid
app.ctx.draw_circle_empty(inp_x, inp_y, 10, gg.yellow)
}
}
elem_on_bits {}
else {
app.log_quit('${@LOCATION} should not get into this else')
}
}
}
}
} else if app.main_menu {
app.ctx.draw_image(0, 0, default_win_width * app.ui, default_win_height * app.ui,
app.banner_img)
app.ctx.draw_image(button_solo_x * app.ui, button_solo_y * app.ui, button_solo_w * app.ui,
button_solo_h * app.ui, app.solo_img)
app.ctx.draw_image(btn_quit_ofst * app.ui, btn_quit_y * app.ui, button_quit_size * app.ui,
button_quit_size * app.ui, unsafe { app.buttons[.quit_map].img })
} else if app.solo_menu {
for i, m in app.map_names_list.filter(it.contains(app.text_input)) { // the maps are filtered with the search field
y := (maps_y_offset + maps_top_spacing) * (i + 1) * app.ui
app.ctx.draw_rect_filled(maps_x_offset * app.ui, y, maps_w * app.ui, maps_h * app.ui,
app.palette.ui_bg)
app.ctx.draw_text(int(maps_x_offset * app.ui), int(y), m, ui_log_cfg)
end_x := (maps_x_offset + maps_w) * app.ui
h := maps_h * app.ui
if i != app.map_delete_nb {
app.ctx.draw_image(end_x, y, h, h, unsafe { app.buttons[.trash].img })
} else {
app.ctx.draw_image(end_x + h, y, h, h, unsafe { app.buttons[.trash].img })
}
}
new_x := button_new_map_x * app.ui
new_y := button_new_map_y * app.ui
new_size := button_new_map_size * app.ui
app.ctx.draw_image(new_x, new_y, new_size, new_size, unsafe { app.buttons[.confirm_save_gate].img })
back_x := btn_back_x * app.ui
back_y := btn_back_y * app.ui
back_s := btn_back_s * app.ui
app.ctx.draw_image(back_x, back_y, back_s, back_s, unsafe { app.buttons[.quit_map].img })
text_x := int(text_field_x * app.ui)
text_y := int((text_field_y + button_new_map_size / 2 - f32(ui_log_cfg.size) / 2) * app.ui)
text_input := if app.text_input != '' {
app.text_input
} else {
'[enter a map name]'
}
app.ctx.draw_text(text_x, text_y, 'Create/search map: ' + text_input, ui_log_cfg)
} else {
app.disable_all_ingame_modes()
app.ctx.draw_square_filled(0, 0, 10, gg.Color{255, 0, 0, 255})
app.log('Not implemented on_frame UI', .err)
}
if app.log_timer > 0 {
border := log_border * app.ui
width := log_width * app.ui
interline_spacing := log_interline_spacing * app.ui
h := app.log.len * (ui_log_cfg.size + interline_spacing)
rect_x := app.s.width - width - log_border
rect_y := app.s.height - h - border
bor_x := rect_x - border
bor_y := rect_y - border
bor_w := width + 2 * border
bor_h := h + 2 * border
// colored border rect
app.ctx.draw_rect_filled(bor_x, bor_y, bor_w, bor_h, app.log_border)
app.ctx.draw_rect_filled(rect_x, rect_y, width, h, gg.white)
for i, l in app.log {
ly := int(rect_y + i * (interline_spacing + ui_log_cfg.size))
app.ctx.draw_text(int(rect_x), ly, l, ui_log_cfg)
}
app.log_timer -= 1
}
app.ctx.end(how: .passthru)
}
struct DebugInfo {
mut:
id u64
coos []Coo
dead bool
}
fn (mut app App) get_info(id u64) DebugInfo {
mut info := DebugInfo{
id: id
}
rid := id & rid_mask
if id == 0x0 { // map empty
} else if id == elem_crossing_bits { // same bits as wires so need to be separated
} else {
match id & elem_type_mask {
elem_wire_bits {
w := app.wires[rid]
info.dead = w.cable_coords[0].x == invalid_coo
info.coos = w.cable_coords.clone()
}
elem_not_bits {
n := app.nots[rid]
info.dead = n.x == invalid_coo
info.coos << Coo{n.x, n.y}
}
elem_diode_bits {
d := app.diodes[rid]
info.dead = d.x == invalid_coo
info.coos << Coo{d.x, d.y}
}
elem_on_bits {}
else {
app.log_quit('${@LOCATION} should not get into this else')
}