-
Notifications
You must be signed in to change notification settings - Fork 10
/
User_GUI.py
3051 lines (2960 loc) · 207 KB
/
User_GUI.py
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
'''
Created on Aug 21, 2021
@author: Cyrus
###########################
### GUI ERRORS/WARNINGS ###
###########################
* ROM directory cannot include spaces. This is because GZIP.EXE uses the command prompt and treats spaces as new inputs. Avoid using special characters as well.
* ROM file must be a Banjo-Kazooie ROM v1.0 NTSC (.z64). Other formats are currently not supported. Randomizer may work on top of other BK mods, but not guaranteed.
* Seed, upper bounds, and lower bounds must be positive integers.
* GZIP.EXE and CRC Tool must be in their original locations with the BK ROM in the root folder.
#########################
### USER GUI OVERVIEW ###
#########################
###
####### ############ ### ### ######################
# # # # SUBMIT ### ### NO # #
# START # ---> # User GUI # ---------> # ERROR? # ---> # Progression_GUI.py #
# # | # # ### ### # #
####### | ############ ### ### ######################
| ^ ### |
| | | YES |
| | V |
| | ################ |
| | # # |
| |-------------------# Error Window # |
| # # |
| ################ |
| |
| ### | ###
| ### ### ####### | ### ### #######
| ### ### PRESSED # # | ### ### PRESSED # #
|--# CLOSE? # ---------> # END # |--# CLOSE? # ---------> # END #
### ### | # # ### ### # #
### ### | ####### ### ### #######
### | ### ^
| |
|-----------------------------------------------------------------------|
'''
tool_tips_dict = {
"ROM": {
"SELECT_ROM_FILE": "Select the Banjo-Kazooie NTSC (.z64) v1.0 ROM File.",
},
"SEED": {
"RANDOM_SEED_BUTTON": "Click the button to generate a random seed.",
},
"SETTING_CODE": {
"GENERATING_SETTING_CODE": "Generates a settings code to verify matching settings with another user.\n" +
"This code does not apply for the file directory, seed value, BK's model,\n" +
"colors, music settings, and miscellaneous settings.",
},
"FLAGGED_OBJECTS": {
"FRAME":
"NONE:\n"+
" Skips the setting.\n" +
"SHUFFLE WORLD:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the world (with the exception of Click Clock\n" +
" Wood unless the feature is turned on).\n" +
"SHUFFLE GAME:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the game (overrides the Click Clock Wood feature).\n" +
" Recommended using FINAL NOTE DOOR feature.\n" +
"FLAGGED_OBJECT_ABNORMALITIES: Includes Flagged Objects that\n" +
" cause weird glitches, but not ones that would break the game.\n" +
"INCLUDE_POTENTIAL_SOFTLOCKS: Includes Flagged Objects that will\n" +
" probably prevent 100%-ing the game. Still playable, though.",
},
"NON_FLAGGED_OBJECTS": {
"FRAME":
"NONE:\n"+
" Skips the setting.\n" +
"SHUFFLE WORLD:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the world (with the exception of Click Clock\n" +
" Wood unless the feature is turned on).\n" +
"SHUFFLE GAME:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the game (overrides the Click Clock Wood feature).\n" +
" Recommended using FINAL NOTE DOOR feature.\n" +
"INCLUDE_ABNORMALITIES: Includes eggs and feathers that are\n" +
" coded like objects.",
},
"STRUCTS": {
"FRAME":
"NONE:\n"+
" Skips the setting.\n" +
"SHUFFLE WORLD:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the world (with the exception of Click Clock\n" +
" Wood unless the feature is turned on).\n" +
"SHUFFLE GAME:\n" +
" Takes all items of that set and swaps the Object IDs\n" +
" within the game (overrides the Click Clock Wood feature).\n" +
" Recommended using FINAL NOTE DOOR feature.\n" +
"RANDOMIZE:\n" +
" For every item in the world, randomly assign a new Object ID.\n" +
" Recommended using FINAL NOTE DOOR feature. You can set whether\n" +
" extra notes will spawn to make it easier.\n" +
"ALL NOTES:\n" +
" All eggs and feathers become notes. Extra lives are replaced\n" +
" with egg and feather refills. The refill at that location\n" +
" is random.\n"+
"Allow Save & Quit/Reset\n" +
" Sets the limits of all world's notes to 127 to allow exiting\n" +
" the save file. Cannot be used for 'All Notes' feature.",
"CARRY_LIMIT": "Changes how many eggs/feathers can be carried,\n" +
"before and after finding Cheato.",
},
"WORLD_ENTRANCES": {
"FRAME":
"NONE:\n" +
" Skips the setting.\n" +
"BASIC SHUFFLE:\n" +
" All worlds, except Mumbo's Mountain, are shuffled. Mumbo's\n" +
" Mountain will give the needed moves to progress the lair.\n" +
"BOTTLES SHUFFLE:\n" +
" All worlds, including Mumbo's Mountain, are shuffled. Bottles\n" +
" mounds are shuffled with 1-Up locations to promote more\n" +
" exploration. Logic may not work if worlds are opened out of order."+
"LEVEL EXIT:\n"+
" When leaving the level, do you want to spawn at the entrance you\n"+
" came in, or the entrance that normally belongs to that world?",
},
"WITHIN_WORLD_WARPS": {
"FRAME":
"NONE:\n" +
" Skips the setting.\n" +
"SHUFFLE BY WORLD:\n" +
" Shuffles the warps that are within the world." +
"SHUFFLE BY GAME:\n" +
" Shuffles the warps throughout the worlds.\n" +
" Feature is prone to crashes and softlocks.\n" +
" Use at own risk!",
},
"STARTING_AREA": {
"NEW_GAME":
"Spawns you at the location upon a new game.",
},
"ENEMIES": {
"FRAME":
"NONE:\n"+
" Skips the setting.\n" +
"SHUFFLE:\n" +
" Shuffles the enemies within the world by category \n" +
" (ground, wall, air), whether checked or not.\n"
"RANDOMIZE:\n"+
" For every enemy in the game (with some exceptions),\n" +
" randomly assign a new enemy that has a checkbox next to it.\n" +
"SOFTLOCK:\n"+
" Any enemy with a * means it can softlock the game in some way.\n" +
" May cause an incompletable seed. Use at own risk!",
},
"BK_COLOR": {
"FRAME": "Change BK's colors to either presets or create\n"+
"your own custom colors for each part. Most colors\n" +
"are RGB32, but some colors are RGB16. Press the\n" +
"'Transfer Colors' button to try to transfer the\n" +
"RGB32 body part colors over to the RGB16 color parts.",
},
"MAP_CONFIG": {
"FRAME":
"Every checkbox with an (A) means it applies aethetic changes\n" +
"like model or animation replacements. Every checkbox with (P)\n" +
"changes the death property of an object (like an enemy).",
},
"SOUNDS_MUSIC": {
"FULL_DESCRIPTION":
"SHUFFLE_SOUNDS: Shuffles short sounds, like ones for eggs, notes, and feathers.\n" +
"SHUFFLE_JINGLES: Shuffles jingles that last a few seconds.\n" +
"SHUFFLE_MUSIC: Shuffles music for levels and minigames.\n" +
"INCLUDE_BETA_SOUNDS: Shuffles the other categories with unused versions.\n" +
"INCLUDE_JARRING_SOUNDS: Includes harsher sounding sounds with the other categories."
},
"MISC_OPTIONS": {
"CREATE_CHEAT_SHEET": "Writes a JSON file that gives a hint for the location of each Jiggy,\n" +
"the location of empty honeycombs, and how many notes are in each room.",
"REMOVE_EXTRA_FILES": "Removes the compressed and decompressed files extrated from the ROM.\n" +
"Useful for BK modders or debugging an issue.",
"TOOL_TIPS": "Hover over a feature to get an explanation of what it does."
},
"LOAD_CONFIG": "Reads a JSON file to configure the settings automatically.",
"SAVE_CONFIG": "Writes a JSON file for future use. See LOAD CONFIG.",
"OPEN_README": "Opens the README.txt file.",
"SUBMIT": "Runs the randomizer with the current features,\n" +
"barring everything is set correctly.",
"GRUNTILDAS_LAIR": {
"FINAL_NOTE_DOOR": "The 810 note door's note requirement can be altered to any\n" +
"value within the lower and upper bounds, inclusively. The\n" +
"lower bound's minimum is 0; the upper bound's maximum is 900\n" +
"unless the feature 'ALL NOTES' is selected, which will extend\n" +
"the maximum to 2000. All other note doors are removed.",
"RANDOM_NOTE_BUTTON": "Turns on the final note door feature and\n" +
"randomly selects a note value",
"FINAL_PUZZLE": "The door leading to the final boss fight's Jiggy requirement\n" +
"can be altered to any value within the lower and upper bounds,\n" +
"inclusively. The lower bound's minimum is 0; the upper bound's\n" +
"maximum is 99. The number of Jiggies required for red honeycombs\n" +
"is 100 - Jiggies Needed, minimum being 1, maximum being 4. All\n" +
"world puzzles are complete by default, meaning worlds are\n" +
"automatically opened. No floating jiggies will remove all\n" +
"Jiggies that aren't spawned by an event.",
"RANDOM_JIGGY_BUTTON": "Turns on the final puzzle door feature and\n" +
"randomly selects a jiggy value",
"SKIP_FURNACE_FUN": "Warps the player past Furnace Fun.\n" +
"Options appear for Brentilda's texts to be\n" +
"replaced with hints about the randomizer.",
"NO_DETRANSFORMATIONS": "Removes all detransformation barriers in the lair.",
"HARDER_FINAL_BATTLE": "Uses one of three variations of the final battle,\n" +
"each ranging in difficulty from easiest (1) to\n" +
"hardest (3). Zero turns the features off."
},
"MUMBOS_MOUNTAIN": {
"INCLUDE_FLOWERS": "If the notes/eggs/feathers feature is not set to\n" +
"'None', the flowers in the level will be included."
},
"TREASURE_TROVE_COVE": {
"SCATTERED_STRUCTS": "Notes are scattered across the level,\n" +
"both in the water and in the air.",
"SUPER_SLIPPERY_SAND": "At some point, you'll trigger the anti-tampering\n" +
"and you won't be able to change direction unless\n" +
"you jump. Have fun!"
},
"CLANKERS_CAVERN": {
"SHUFFLE_CLANKER_RING_ORDER": "Clanker's ring order is shuffled.",
},
"BUBBLEGLOOP_SWAMP": {
"SHUFFLE_CROCTUS_ORDER": "Croctus spawn order is shuffled.",
"MR_VILE_BIGGER_BADDER_CROCODILE": "Mr. Vile is noticeably bigger.",
"TIPTUP_CHOIR_NO_ASSIGNED_SEATS": "Choir memembers are scattered across the room,\n" +
"only revealing their heads.",
},
"FREEZEEZY_PEAK": {
"BOGGY_RACES_MOVED_FLAGS": "Flag poles for the Boggy race are either tighter left, tighter right,\n" +
"lowered to the floor to make harder to see, or rotated.",
},
"GOBIS_VALLEY": {
"SHUFFLED_ANCIENT_ONES_ORDER": "Ancient Ones order is shuffled.",
"SHUFFLE_MAZE_JINXY_HEADS_ORDER": "Jinxy heads to raise King Sandybutt's Tomb order is shuffled.",
},
"MAD_MONSTER_MANSION": {
"POTS_ARE_LIT": "Flower pots are shuffled with the fire pain objects.",
"MOTZAND_KEYS": "Motzand's music pattern is randomized.",
},
"RUSTY_BUCKET_BAY": {
"RANDOMIZED_BUTTON_COMBO": "Generates a random 6-digit combination of 1s, 2s, and 3s for the\n" +
"whistle buttons and places the code over the original code spot.",
},
"CLICK_CLOCK_WOOD": {
"SHUFFLE_BY": "SEASON: All shuffling happens within each seasons/lobby area,\n" +
" making it easier for players to track what they are missing.\n" +
"WORLD: All shuffling happens throughout the level.\n" +
"OPEN SEASONS: The CCW season doors and buttons are removed,\n" +
" allowing the player to enter the sections without Beak Buster.",
},
}
######################
### PYTHON IMPORTS ###
######################
import tkinter as tk
from tkinter import ttk
import tkinter.filedialog
import os
import json
from random import randint, choice
import logging
from logging.handlers import RotatingFileHandler
from mmap import mmap
####################
### FILE IMPORTS ###
####################
from Progression_GUI import Progression_GUI_Class
from Randomization_Processes.Common_Functions import read_json, dump_json, leading_zeros
from Randomization_Processes.Dicts_And_Lists.Game_Engine import start_level_ids
from Randomization_Processes.Dicts_And_Lists.Enemies import master_enemy_dict
#######################
### ERROR GUI CLASS ###
#######################
def Error_GUI(error_msg):
'''Brings up a GUI that displays an error message'''
def update_bottles_gif(ind):
'''Updates The Gif Frame'''
frame = frames[ind]
ind += 1
if ind == frame_count:
ind = 0
bottles_talking_label.configure(image=frame)
bottles_talking_label.after(60, update_bottles_gif, ind)
error_window = tk.Tk()
error_window.winfo_toplevel().title("Banjo-Kazooie Randomizer Error")
error_window.config(background="#F3E5AB")
# Bottles Talking
frame_count = 10
frames = [tk.PhotoImage(master=error_window, file=(f"{os.getcwd()}/Pictures/Bottles_Speaking.gif"), format = 'gif -index %i' %(i)) for i in range(frame_count)]
bottles_talking_label = tk.Label(error_window, background="#F3E5AB")
bottles_talking_label.pack(padx=5, pady=2)
error_label = tk.Label(error_window, text=error_msg, background="#F3E5AB", font=("LITHOGRAPH-BOLD", 12))
error_label.config(anchor='center')
error_label.pack(padx=5, pady=2)
ok_btn = tk.Button(error_window, text='Doh!', background="#F3E5AB", command=error_window.destroy, font=("LITHOGRAPH-BOLD", 12))
ok_btn.config(anchor='center')
ok_btn.pack(padx=5, pady=2)
error_window.protocol("WM_DELETE_WINDOW", error_window.destroy)
error_window.after(0, update_bottles_gif, 0)
error_window.mainloop()
#########################
### WARNING GUI CLASS ###
#########################
class WARNING_GUI():
'''Brings up a GUI that displays an warning message'''
def __init__(self, warning_msg):
self.warning_msg = warning_msg
self.chosen_option = False
self.warning_window = tk.Tk()
self.warning_window.winfo_toplevel().title("Banjo-Kazooie Randomizer Warning")
self.warning_window.config(background="#F3E5AB")
def update_klungo_gif(self, ind):
'''Updates The Gif Frame'''
self.frame = self.frames[ind]
ind += 1
if ind == self.frame_count:
ind = 0
self.klungo_talking_label.configure(image=self.frame)
self.klungo_talking_label.after(60, self.update_klungo_gif, ind)
def confirm(self):
'''Confirming Option'''
self.warning_window.quit()
self.chosen_option = True
def cancel(self):
'''Cancel Option'''
self.warning_window.quit()
self.chosen_option = False
def main(self):
'''Runs the warning window with Klungo's face'''
# Klungo Talking
self.frame_count = 10
self.frames = [tk.PhotoImage(master=self.warning_window, file=(f"{os.getcwd()}/Pictures/Klungo_Speaking.gif"), format = 'gif -index %i' %(i)) for i in range(self.frame_count)]
self.klungo_talking_label = tk.Label(self.warning_window, background="#F3E5AB")
self.klungo_talking_label.grid(row=0, column=1, padx=5, pady=5)
self.warning_label = tk.Label(self.warning_window, text=self.warning_msg, background="#F3E5AB", font=("LITHOGRAPH-BOLD", 12))
self.warning_label.config(anchor='center')
self.warning_label.grid(row=1, column=0, columnspan=3, padx=5, pady=5)
self.confirm_btn = tk.Button(self.warning_window, text='Confirm', background="#F3E5AB", command=self.confirm, font=("LITHOGRAPH-BOLD", 12))
self.confirm_btn.config(anchor='center')
self.confirm_btn.grid(row=2, column=0, padx=5, pady=5)
self.cancel_btn = tk.Button(self.warning_window, text='Cancel', background="#F3E5AB", command=self.cancel, font=("LITHOGRAPH-BOLD", 12))
self.cancel_btn.config(anchor='center')
self.cancel_btn.grid(row=2, column=2, padx=5, pady=5)
self.warning_window.protocol("WM_DELETE_WINDOW", self.warning_window.destroy)
self.warning_window.after(0, self.update_klungo_gif, 0)
self.warning_window.mainloop()
self.warning_window.destroy()
return self.chosen_option
######################
### USER GUI CLASS ###
######################
class User_GUI_Class():
'''Creates a GUI where users give the directory of the ROM file, select options for the randomization, and optionally provide a seed value'''
def __init__(self, BK_RANDO_VERSION):
'''Creates the Tkinter GUI'''
self.BK_RANDO_VERSION = BK_RANDO_VERSION
self.cwd = os.getcwd() + "/"
self.app_window = tk.Tk()
self.app_window.winfo_toplevel().title(f"Banjo-Kazooie Randomizer v{self.BK_RANDO_VERSION}")
self.padx = 3
self.pady = 1
self.white = "#FFFFFF"
self.blue = "#0040AA"
self.red = "#990000"
self.generic_background_color = "#BFBF00"
self.black = "#000000"
self.font_type = "LITHOGRAPH-BOLD"
self.large_font_size = 22
self.medium_font_size = 16
self.small_font_size = 10
### LOGGER ###
self.logger = logging.getLogger("Rotating Log")
self.logger.setLevel(logging.INFO)
FORMAT = '[%(levelname)s] %(asctime)-15s - %(funcName)s: %(message)s'
handler = RotatingFileHandler(f"{self.cwd}\Randomizer_Log_File.log", maxBytes=(512*1024), backupCount=1)
self.logger.addHandler(handler)
logging.basicConfig(format=FORMAT)
######################
### TOOL TIP CLASS ###
######################
class CreateToolTip(object):
'''Create a tooltip for a given widget'''
def __init__(self, widget, master, text='widget info'):
self.widget = widget
self.master = master
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.close)
def enter(self, event=None):
'''When hovering over the tool tip icon, start displaying the tool tip'''
try:
if(self.master.tool_tips_var.get() == 1):
x = y = 0
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 20
# creates a toplevel window
self.tw = tk.Toplevel(self.widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left',
background='white', relief='solid', borderwidth=1,
font=("times", "10", "normal"))
label.pack(ipadx=1)
except AttributeError:
pass
def close(self, event=None):
'''When no longer hovering over the tool tip icon, stop displaying the tool tip'''
try:
if self.tw:
self.tw.destroy()
except Exception:
pass
#############################
### FILE BUTTON FUNCTIONS ###
#############################
def _select_rom_file(self):
'''Opens a browser to select the ROM file ending in .z64'''
self.logger.info("Selecting ROM file")
filename = tkinter.filedialog.askopenfilename(initialdir=self.cwd, title="Select The BK ROM File", filetype =(("Rom Files","*.z64"),("all files","*.*")) )
if(not filename):
return
self.rom_file_entry.set(filename)
self._verify_rom_file(filename)
def _verify_rom_file(self, filename):
'''Compares the checksum of the selected ROM with known ROM file checksums to verify the version'''
rom_version_dict = {
"NTSC-U v1.0": [0xA4, 0xBF, 0x93, 0x06, 0xBF, 0x0C, 0xDF, 0xD1],
"NTSC-U v1.1": [0xCD, 0x75, 0x59, 0xAC, 0xB2, 0x6C, 0xF5, 0xAE],
"PAL v1.0": [0x3F, 0x73, 0xB1, 0xCC, 0x48, 0x44, 0xF9, 0x92],
"NTSC-J v1.1": [0x68, 0x51, 0x20, 0xD5, 0x5F, 0xCA, 0x0D, 0xCD]
}
with open(filename, "r+b") as rom_file:
mm_rom = mmap(rom_file.fileno(), 0)
rom_checksum = []
for index in range(0x10, 0x18):
rom_checksum.append(mm_rom[index])
version = "Ellie Bobellie"
for rom_version in rom_version_dict:
if(rom_version_dict[rom_version] == rom_checksum):
version = rom_version
break
if(version == "NTSC-U v1.0"):
return True
elif(version in ["NTSC-U v1.1", "PAL v1.0", "NTSC-J v1.1"]):
Error_GUI(f"The selected ROM is '{version}'.\nThis version is not supported by the Randomizer.\nPlease use a NTSC-U v1.0 BK ROM.")
return False
else:
Error_GUI(f"You're using either a modded game\n or a non-BK ROM ending in z64.\nPlease use a NTSC-U v1.0 BK ROM.")
return False
def _open_file(self, file_to_open):
'''Generic open file button. If they use this for the README, bless their hearts'''
self.logger.info(f"Openning File: {file_to_open}")
os.startfile(file_to_open)
###############################
### RANDOM BUTTON FUNCTIONS ###
###############################
def _random_seed(self):
'''Randomly selects a seed'''
self.logger.info("Select Random Seed")
self.seed_value.set(str(randint(10000000, 19940303)))
def _random_note_value(self):
'''Randomly selects a note value'''
self.logger.info("Select Random Note Value")
self.final_note_door_value.set("?")
def _random_puzzle_value(self):
'''Randomly selects a puzzle value'''
self.logger.info("Select Random Puzzle Value")
self.final_puzzle_var.set(1)
self.final_puzzle_value.set("?")
def _random_bk_model_preset(self):
'''Randomly selects a BK Preset from the JSON file'''
self.logger.info("Select Random BK Model Preset")
key_list = []
for key in self.bk_model_json:
key_list.append(key)
random_bk_model = choice(key_list)
self.bk_model_var.set(random_bk_model)
def _random_hex(self, digit_len):
'''Randomly generates hex values for the colors in BK'''
max_num = "F" * digit_len
random_hex_val = leading_zeros(randint(0, int(max_num, 16)), digit_len).upper()
if(digit_len == 4):
choices = [str(hex(num))[2:].upper() for num in range(0x1, 0xF, 0x2)]
new_end_val = choice(choices)
random_hex_val = random_hex_val[:-1] + new_end_val
elif(digit_len == 6):
random_hex_val = random_hex_val + "FF"
return random_hex_val
def _random_bk_model_colors(self):
'''Randomly generates all of the BK model's hex colors'''
self.logger.info("Select Random Colors For BK")
self.bk_model_var.set("Custom")
self.banjo_fur_var.set(self._random_hex(6))
self.tooth_necklace_var.set(self._random_hex(6))
self.banjo_skin_var.set(self._random_hex(6))
self.banjo_feet_var.set(self._random_hex(4))
self.kazooie_primary_var.set(self._random_hex(6))
self.kazooie_secondary_var.set(self._random_hex(6))
self.kazooie_wing_primary_var.set(self._random_hex(4))
self.kazooie_wing_secondary_var.set(self._random_hex(4))
self.backpack_var.set(self._random_hex(6))
self.wading_boots_var.set(self._random_hex(6))
self.shorts_vertex_var.set(self._random_hex(6))
self.shorts_texture_var.set(self._random_hex(4))
def _update_bk_model(self, *args):
'''When selecting a BK preset, it will update the color fields with the proper hex values'''
self.logger.info("Update BK Model")
bk_model_preset = self.bk_model_var.get()
if(bk_model_preset in self.bk_model_json):
self.banjo_fur_var.set(self.bk_model_json[bk_model_preset]["Banjo_Fur"])
self.tooth_necklace_var.set(self.bk_model_json[bk_model_preset]["Tooth_Necklace"])
self.banjo_skin_var.set(self.bk_model_json[bk_model_preset]["Banjo_Skin"])
self.banjo_feet_var.set(self.bk_model_json[bk_model_preset]["Banjo_Feet"])
self.kazooie_primary_var.set(self.bk_model_json[bk_model_preset]["Kazooie_Primary"])
self.kazooie_secondary_var.set(self.bk_model_json[bk_model_preset]["Kazooie_Secondary"])
self.kazooie_wing_primary_var.set(self.bk_model_json[bk_model_preset]["Kazooie_Wing_Primary"])
self.kazooie_wing_secondary_var.set(self.bk_model_json[bk_model_preset]["Kazooie_Wing_Secondary"])
self.backpack_var.set(self.bk_model_json[bk_model_preset]["Backpack"])
self.wading_boots_var.set(self.bk_model_json[bk_model_preset]["Wading_Boots"])
self.shorts_vertex_var.set(self.bk_model_json[bk_model_preset]["Shorts_Vertex"])
self.shorts_texture_var.set(self.bk_model_json[bk_model_preset]["Shorts_Texture"])
preset_image_path = f"{self.cwd}Pictures/BK_Models/{bk_model_preset}.png"
if(os.path.isfile(preset_image_path)):
self.bk_model_image = tk.PhotoImage(file=preset_image_path)
else:
self.bk_model_image = tk.PhotoImage(file=f"{self.cwd}Pictures/BK_Models/Question_Mark.png")
self.bk_model_image_label.config(image=self.bk_model_image)
else:
self.banjo_fur_var.set("?")
self.tooth_necklace_var.set("?")
self.banjo_skin_var.set("?")
self.banjo_feet_var.set("?")
self.kazooie_primary_var.set("?")
self.kazooie_secondary_var.set("?")
self.kazooie_wing_primary_var.set("?")
self.kazooie_wing_secondary_var.set("?")
self.backpack_var.set("?")
self.wading_boots_var.set("?")
self.shorts_vertex_var.set("?")
self.shorts_texture_var.set("?")
self.bk_model_image = tk.PhotoImage(file=f"{self.cwd}Pictures/BK_Models/Question_Mark.png")
self.bk_model_image_label.config(image=self.bk_model_image)
def _enemy_option_select(self, *args):
'''Shows enemy options if Randomize is selected'''
self.logger.info("Enemy Option Select")
if(self.enemies_var.get() == "Randomize"):
self.enemy_checklist_frame.grid(row=2, column=0, columnspan=6, padx=self.padx, pady=self.pady, sticky='w')
self.non_softlock_enemies_button.grid(row=0, column=2, padx=self.padx, pady=self.pady, sticky='e')
self.clear_enemies_button.grid(row=0, column=3, padx=self.padx, pady=self.pady, sticky='e')
else:
self.enemy_checklist_frame.grid_remove()
self.non_softlock_enemies_button.grid_remove()
self.clear_enemies_button.grid_remove()
def _select_non_softlock_enemies(self):
'''Checks the boxes for all non-softlock enemies and unchecks all softlock enemies'''
self.logger.info("Select Non-Softlock Enemies")
for enemy_name in self.enemy_checkbox_dict:
if("*" in enemy_name):
self.enemy_checkbox_dict[enemy_name].set(0)
else:
self.enemy_checkbox_dict[enemy_name].set(1)
def _remove_all_enemies(self):
'''Unchecks all enemy checkboxes'''
self.logger.info("Deselect All Enemies")
for enemy_name in self.enemy_checkbox_dict:
self.enemy_checkbox_dict[enemy_name].set(0)
def _all_custom_aesthetics(self):
'''Selects all aesthetic MAP configs'''
self.logger.info("Select All Custom Aesthetic")
for custom_name in self.map_config_checkbox_dict:
if(custom_name.startswith("(A)")):
self.map_config_checkbox_dict[custom_name].set(1)
def _no_customization(self):
'''Removes all MAP configs'''
self.logger.info("Removing Customizations")
for custom_name in self.map_config_checkbox_dict:
self.map_config_checkbox_dict[custom_name].set(0)
def _random_customization(self):
'''Randomly selects MAP configs'''
self.logger.info("Random Customization")
if(self.hiding_customization):
self.logger.info("Hiding Customization")
for custom_name in self.map_config_checkbox_dict:
self.map_config_checkbox_dict[custom_name].set(2)
self.random_customization_button.configure(text='Random By Seed\nAnd Hide Options')
for checkbutton_count, checkbutton in enumerate(self.customization_checkbuttons):
checkbutton.grid(row=(checkbutton_count // 4) + 1, column=(checkbutton_count % 4), padx=self.padx, pady=self.pady, sticky='w')
self.hiding_customization = False
else:
for custom_name in self.map_config_checkbox_dict:
self.map_config_checkbox_dict[custom_name].set(randint(0, 1))
self.logger.info("Re-Adding Customization")
self.random_customization_button.configure(text='Show Customize\nCheckboxes')
for checkbutton in self.customization_checkbuttons:
checkbutton.grid_remove()
self.hiding_customization = True
def _default_starting_area(self):
'''Selects a random starting area'''
self.logger.info("Select Default Starting Area")
self.new_area_var.set("SM - Main")
self.skip_intro_cutscenes_var.set(0)
def _new_area_option(self, *args):
'''If the starting area is not the default area, skip the intro cutscene'''
self.logger.info("Selecting 'Skip Intro Cutscene'")
if(self.new_area_var.get() != "SM - Main"):
self.skip_intro_cutscenes_var.set(1)
if(self.new_area_var.get() == "Random Starting Area (Auto Have All Moves)"):
self.all_starting_moves_var.set(1)
self.all_starting_moves_checkbutton.configure(state='disabled')
else:
self.all_starting_moves_checkbutton.configure(state='normal')
def _set_random_carry_capacities(self, *args):
'''Select random capacities for blue eggs, red feathers, and gold feathers'''
self.logger.info("Random Carry Capacities")
self.before_blue_egg_carry_value.set("?")
self.after_blue_egg_carry_value.set("?")
self.before_red_feather_carry_value.set("?")
self.after_red_feather_carry_value.set("?")
self.before_gold_feather_carry_value.set("?")
self.after_gold_feather_carry_value.set("?")
def _lock_final_puzzle_value(self, *args):
'''Displays options depending on whether final puzzle is selected'''
self.logger.info("Lock Final Puzzle Value")
if(self.final_puzzle_var.get() == 0):
self.final_puzzle_value.set("25")
self.final_puzzle_entry.configure(state='disabled')
self.remove_floating_jiggies_var.set(0)
self.remove_floating_jiggies_checkbox.grid_remove()
else:
self.final_puzzle_entry.configure(state='normal')
self.remove_floating_jiggies_checkbox.grid(row=3, column=3, padx=self.padx, pady=self.pady, sticky='w')
def _lock_struct_options(self, *args):
'''Displays options depending on which struct option is selected'''
self.logger.info("Lock Struct Options")
if(self.struct_var.get() == "No Shuffle"):
self.struct_note_count_var.set("Produce Extra Notes")
self.struct_note_count_dropdown.grid_remove()
self.note_overflow_var.set("Allow Save & Quit/Reset")
self.note_overflow_dropdown.grid_remove()
elif(self.struct_var.get() == "Shuffle (World)"):
self.struct_note_count_var.set("Produce Extra Notes")
self.struct_note_count_dropdown.grid_remove()
self.note_overflow_var.set("Allow Save & Quit/Reset")
self.note_overflow_dropdown.grid_remove()
elif(self.struct_var.get() == "Shuffle (Game)"):
self.struct_note_count_var.set("Produce Extra Notes")
self.struct_note_count_dropdown.grid_remove()
self.note_overflow_dropdown.grid(row=0, column=2, columnspan=2, padx=self.padx, pady=self.pady, sticky='w')
elif(self.struct_var.get() == "Randomize"):
self.struct_note_count_dropdown.grid(row=1, column=2, columnspan=2, padx=self.padx, pady=self.pady, sticky='w')
self.note_overflow_dropdown.grid(row=0, column=2, columnspan=2, padx=self.padx, pady=self.pady, sticky='w')
elif(self.struct_var.get() == "All Notes"):
self.struct_note_count_var.set("Produce Extra Notes")
self.struct_note_count_dropdown.grid_remove()
self.note_overflow_var.set("Allow Save & Quit/Reset")
self.note_overflow_dropdown.grid_remove()
def _convert_rgb32_to_rgb16(self, _32_bit_color):
'''Converts RGB32 colors to RBG16'''
if(len(_32_bit_color) == 0):
return ""
elif(_32_bit_color == "?"):
return None
elif(len(_32_bit_color) == 6):
alpha = 255
elif(len(_32_bit_color) == 8):
alpha = int(_32_bit_color[6:8], 16)
else:
Error_GUI(f"Error: 32-bit color is not length 6 or 8.\nPlease check for proper formatting.\nex: 'F5D9E2' or 'F5D9E2FF'")
return None
red = int(_32_bit_color[0:2], 16)
green = int(_32_bit_color[2:4], 16)
blue = int(_32_bit_color[4:6], 16)
r = (red >> 3)
g = (green >> 3)
b = (blue >> 3)
a = (alpha >> 7)
_16_bit_color = (r << 11) | (g << 6) | (b << 1) | (a)
return leading_zeros(_16_bit_color, 4)
def _transfer_rgb32_to_rgb16(self):
'''Overwrites RGB16 colors with translated RGB32 colors'''
color = self._convert_rgb32_to_rgb16(self.banjo_skin_var.get())
if(color):
self.banjo_feet_var.set(color)
color = self._convert_rgb32_to_rgb16(self.kazooie_primary_var.get())
if(color):
self.kazooie_wing_primary_var.set(color)
color = self._convert_rgb32_to_rgb16(self.kazooie_secondary_var.get())
if(color):
self.kazooie_wing_secondary_var.set(color)
color = self._convert_rgb32_to_rgb16(self.shorts_vertex_var.get())
if(color):
self.shorts_texture_var.set(color)
def _save_bk_colors(self):
'''Saves the BK colors as a preset'''
new_custom_name = self.custom_bk_model_name_var.get()
if(new_custom_name == "Default"):
Error_GUI("You can't overwrite the OG colors!\nThey are classic!")
return
elif(new_custom_name in self.bk_model_json):
warning_gui = WARNING_GUI(f"Are you sure you want to overwrite this preset?\n{new_custom_name}")
else:
warning_gui = WARNING_GUI(f"Are you sure you want to save this new preset?\n{new_custom_name}")
confirmation = warning_gui.main()
del warning_gui
if(confirmation):
self.bk_model_json[new_custom_name] = {
"Banjo_Fur": self.banjo_fur_var.get(),
"Banjo_Skin": self.banjo_skin_var.get(),
"Banjo_Feet": self.banjo_feet_var.get(),
"Kazooie_Primary": self.kazooie_primary_var.get(),
"Kazooie_Secondary": self.kazooie_secondary_var.get(),
"Kazooie_Wing_Primary": self.kazooie_wing_primary_var.get(),
"Kazooie_Wing_Secondary": self.kazooie_wing_secondary_var.get(),
"Backpack": self.backpack_var.get(),
"Wading_Boots": self.wading_boots_var.get(),
"Shorts_Vertex": self.shorts_vertex_var.get(),
"Shorts_Texture": self.shorts_texture_var.get(),
"Tooth_Necklace": self.tooth_necklace_var.get()
}
dump_json(f"{self.cwd}Randomization_Processes/Misc_Manipulation/Model_Data/BK_Model_Presets.json", self.bk_model_json)
self.bk_model_options = ["Seed Determined Preset", "Seed Determined Colors"]
self.custom_color_count = 0
for item in sorted(self.bk_model_json):
self.bk_model_options.append(item)
if(item.startswith("Custom Preset")):
self.custom_color_count += 1
self.bk_model_var.set(new_custom_name)
self.bk_model_dropdown = ttk.Combobox(self.bk_model_frame, textvariable=self.bk_model_var, foreground=self.black, background="#F3E5AB", font=(self.font_type, self.small_font_size), width=30)
self.bk_model_dropdown['values'] = self.bk_model_options
self.bk_model_dropdown['state'] = 'readonly'
self.bk_model_dropdown.grid(row=0, column=1, columnspan=2, padx=self.padx, pady=self.pady, sticky='w')
def _delete_bk_colors(self):
'''Delete BK preset'''
if(self.bk_model_var.get() == "Default"):
Error_GUI("You can't delete the OG colors!\nThey are classic!")
else:
warning_gui = WARNING_GUI(f"Are you sure you want to delete this preset?\n{self.bk_model_var.get()}")
confirmation = warning_gui.main()
del warning_gui
if(confirmation):
del self.bk_model_json[self.bk_model_var.get()]
dump_json(f"{self.cwd}Randomization_Processes/Misc_Manipulation/Model_Data/BK_Model_Presets.json", self.bk_model_json)
self.bk_model_options = ["Seed Determined Preset", "Seed Determined Colors"]
self.custom_color_count = 0
for item in sorted(self.bk_model_json):
self.bk_model_options.append(item)
if(item.startswith("Custom Preset")):
self.custom_color_count += 1
self.bk_model_var.set(self.bk_model_options[0])
self.bk_model_dropdown = ttk.Combobox(self.bk_model_frame, textvariable=self.bk_model_var, foreground=self.black, background="#F3E5AB", font=(self.font_type, self.small_font_size), width=30)
self.bk_model_dropdown['values'] = self.bk_model_options
self.bk_model_dropdown['state'] = 'readonly'
self.bk_model_dropdown.grid(row=0, column=1, columnspan=2, padx=self.padx, pady=self.pady, sticky='w')
self.bk_model_var.set("Default")
self.custom_bk_model_name_var.set(f"Custom Preset {self.custom_color_count}")
def _select_all_short_sounds(self):
'''Selects all short sounds'''
for short_sound_type in self.short_sounds_dict:
for short_sound_name in self.short_sounds_dict[short_sound_type]:
(self.short_sounds_dict[short_sound_type][short_sound_name]).set(1)
def _select_non_jarring_short_sounds(self):
'''Selects all non-jarring short sounds'''
for short_sound_name in self.short_sounds_dict["Normal"]:
(self.short_sounds_dict["Normal"][short_sound_name]).set(1)
for short_sound_name in self.short_sounds_dict["Jarring"]:
(self.short_sounds_dict["Jarring"][short_sound_name]).set(0)
def _deselect_all_short_sounds(self):
'''Deselects all short sounds'''
for short_sound_type in self.short_sounds_dict:
for short_sound_name in self.short_sounds_dict[short_sound_type]:
(self.short_sounds_dict[short_sound_type][short_sound_name]).set(0)
def _select_all_jingles(self):
'''Selects all jingles'''
for short_sound_name in self.jingles_dict:
(self.jingles_dict[short_sound_name]).set(1)
def _deselect_all_jingles(self):
'''Deselects all jingles'''
for short_sound_name in self.jingles_dict:
(self.jingles_dict[short_sound_name]).set(0)
def _select_all_music(self):
'''Selects all music'''
for short_sound_type in self.music_dict:
for short_sound_name in self.music_dict[short_sound_type]:
(self.music_dict[short_sound_type][short_sound_name]).set(1)
def _deselect_all_music(self):
'''Deselects all music'''
for short_sound_type in self.music_dict:
for short_sound_name in self.music_dict[short_sound_type]:
(self.music_dict[short_sound_type][short_sound_name]).set(0)
def _skip_furnace_fun_checkbox_trace(self, *args):
'''Shows options depending on whether skip furnace fun is selected'''
if(self.skip_furnace_fun_var.get() == 1):
self.brentilda_hints_var.set("Vague Brentilda Rando Hints")
self.brentilda_hints_dropdown.grid(row=1, column=1, padx=self.padx, pady=self.pady, sticky='w')
else:
self.brentilda_hints_var.set("Base Game Brentilda Hints")
self.brentilda_hints_dropdown.grid_remove()
def _gruntilda_difficulty_trace(self, *args):
'''Shows options depending on whether harder grunty is selected'''
if(self.gruntilda_difficulty_var.get() == 0):
self.what_floor_var.set(0)
self.what_floor_checkbox.grid_remove()
self.grunty_size_var.set(0)
self.grunty_size_checkbox.grid_remove()
self.monster_house_var.set(0)
self.monster_house_checkbox.grid_remove()
else:
self.what_floor_var.set(1)
self.what_floor_checkbox.grid(row=4, column=1, padx=self.padx, pady=self.pady, sticky='w')
self.grunty_size_var.set(1)
self.grunty_size_checkbox.grid(row=5, column=1, padx=self.padx, pady=self.pady, sticky='w')
self.monster_house_var.set(1)
self.monster_house_checkbox.grid(row=6, column=1, padx=self.padx, pady=self.pady, sticky='w')
################################
### RANDOMIZER SETTINGS CODE ###
################################
def _randomizer_settings_int_to_char_translator(self):
'''Translates the randomizer settings code from numeric to char'''
# print(f"Randomizer Settings Generated Code: {self.randomizer_settings_code}")
randomizer_settings_code = self.generated_randomizer_settings_code
ascii_code = ""
while(randomizer_settings_code > 0):
curr_val = (randomizer_settings_code % 26) + 65
ascii_code = chr(curr_val) + ascii_code
randomizer_settings_code = randomizer_settings_code // 26
self.randomizer_setting_code_value.set(ascii_code)
def _add_randomizer_settings_to_code(self, add_val, counter_add=1):
'''Adds the numerical value of a setting to the randomizer settings code'''
self.generated_randomizer_settings_code += (int(add_val) << self.randomizer_settings_count)
self.randomizer_settings_count += counter_add
def _generate_randomizer_settings_code(self):
'''Generates the randomizer settings code by turning all settings into numerical values'''
self.logger.info("Generating Randomizer Settings Code")
self.generated_randomizer_settings_code = 0
self.randomizer_settings_count = 0
### General Settings ###
# Flagged Objects
self._add_randomizer_settings_to_code(["No Shuffle", "Shuffle (World)", "Shuffle (Game)"].index(self.flagged_object_var.get()), 2)
self._add_randomizer_settings_to_code(self.flagged_object_abnormalities_var.get())
self._add_randomizer_settings_to_code(self.flagged_object_softlock_var.get())
self._add_randomizer_settings_to_code(self.final_puzzle_var.get())
if(self.final_puzzle_value.get() == "?"):
self._add_randomizer_settings_to_code(100, 8)
else:
self._add_randomizer_settings_to_code(self.final_puzzle_value.get(), 8)
self._add_randomizer_settings_to_code(["Base Game Costs", "World Order Scaled Costs", "Free Transformations"].index(self.free_transformations_var.get()), 2)
self._add_randomizer_settings_to_code(["Normal Health", "Four Health Only", "Two Health Only", "One Health Only", "Zero Health (Unbeatable)", "Random Health Option"].index(self.max_health_banjo_var.get()), 3)
self._add_randomizer_settings_to_code(self.remove_floating_jiggies_var.get())
# Non-Flagged Objects
self._add_randomizer_settings_to_code(["No Shuffle", "Shuffle (World)"].index(self.non_flagged_object_var.get()))
self._add_randomizer_settings_to_code(["Default Jinjo Colors", "Random Jinjo Colors"].index(self.jinjo_color_var.get()))
self._add_randomizer_settings_to_code(self.non_flagged_object_abnormalities_var.get())
self._add_randomizer_settings_to_code(self.starting_lives_value.get(), 8)
# Structs
self._add_randomizer_settings_to_code(["No Shuffle", "Shuffle (World)", "Shuffle (Game)", "Randomize", "All Notes"].index(self.struct_var.get()), 3)
self._add_randomizer_settings_to_code(["Allow Save & Quit/Reset", "Possible No Save & Quit/Reset"].index(self.note_overflow_var.get()))
self._add_randomizer_settings_to_code(["Produce Extra Notes", "Produce Exactly Enough Notes"].index(self.struct_note_count_var.get()))
self._add_randomizer_settings_to_code(["Scaling Note Doors", "Final Note Door Only"].index(self.final_note_door_var.get()))
if(self.final_note_door_value.get() == "?"):
self._add_randomizer_settings_to_code(2001, 11)
else:
self._add_randomizer_settings_to_code(self.final_note_door_value.get(), 11)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.before_blue_egg_carry_value.get(), 9)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.after_blue_egg_carry_value.get(), 9)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.before_red_feather_carry_value.get(), 9)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.after_red_feather_carry_value.get(), 9)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.before_gold_feather_carry_value.get(), 9)
if(self.before_blue_egg_carry_value.get() == "?"):
self._add_randomizer_settings_to_code(256, 9)
else:
self._add_randomizer_settings_to_code(self.after_gold_feather_carry_value.get(), 9)
# World Entrances
self._add_randomizer_settings_to_code(["No Shuffle", "Basic Shuffle", "Bottles Shuffle"].index(self.world_entrance_var.get()), 2)
self._add_randomizer_settings_to_code(["Exit From World You Were Just In", "Exit From Entrance You Entered From"].index(self.world_exit_var.get()))
self._add_randomizer_settings_to_code(self.all_starting_moves_var.get())
# Within World Warps
self._add_randomizer_settings_to_code(["No Shuffle", "Shuffle By World", "Shuffle By Game"].index(self.within_world_warps_var.get()), 2)
# Starting World
starting_world_options = [option for option in start_level_ids]
starting_world_options.insert(0, "Random Starting Area (Auto Have All Moves)")
self._add_randomizer_settings_to_code(starting_world_options.index(self.new_area_var.get()), 8)
self._add_randomizer_settings_to_code(self.skip_intro_cutscenes_var.get())
# Enemies
self._add_randomizer_settings_to_code(["Default Enemies", "Shuffle", "Randomize"].index(self.enemies_var.get()), 2)
self._add_randomizer_settings_to_code(["Random Size Setting", "Random Setting Per World", "Random Setting Per Area",
"Default Sizes", "Scale Factor", "Uniform Size Range",
"Generally Small", "Generally Large",
"Everything Small", "Everything Large"].index(self.enemy_size_var.get()), 4)
for enemy_name in sorted(self.enemy_checkbox_dict):
self._add_randomizer_settings_to_code(self.enemy_checkbox_dict[enemy_name].get())
### Aesthetic Settings ###
# Models, Animations, Properties
for custom_name in sorted(self.map_config_checkbox_dict):
self._add_randomizer_settings_to_code(self.map_config_checkbox_dict[custom_name].get())
### World Specific ###
# Gruntilda's Lair
self._add_randomizer_settings_to_code(self.skip_furnace_fun_var.get())
self._add_randomizer_settings_to_code(["Base Game Brentilda Hints", "Vague Brentilda Rando Hints", "Detailed Brentilda Rando Hints"].index(self.brentilda_hints_var.get()), 2)
self._add_randomizer_settings_to_code(self.remove_magic_barriers_var.get())
self._add_randomizer_settings_to_code(self.gruntilda_difficulty_var.get(), 2)
self._add_randomizer_settings_to_code(self.monster_house_var.get())
self._add_randomizer_settings_to_code(self.what_floor_var.get())
self._add_randomizer_settings_to_code(self.grunty_size_var.get())
# Mumbo's Mountain
self._add_randomizer_settings_to_code(self.flowers_var.get())
# Treasure Trove Cove
self._add_randomizer_settings_to_code(["No Scatter", "Low Scatter", "High Scatter"].index(self.scattered_structs_var.get()), 2)
self._add_randomizer_settings_to_code(self.super_slippery_ttc_var.get())
# Clanker's Cavern
self._add_randomizer_settings_to_code(self.hard_rings_var.get())
# Bubblegloop Swamp