diff options
author | B. Stack <bgstack15@gmail.com> | 2024-04-05 12:07:53 -0400 |
---|---|---|
committer | B. Stack <bgstack15@gmail.com> | 2024-04-05 12:07:53 -0400 |
commit | 219e0ff7b8e40d49a1b386823bfbef9f25e94166 (patch) | |
tree | a38e7dd4a0b6369394d29e2607bd9de6594e7ef8 | |
parent | tk: moved flash_entry to lib, added frame_backgrounds checkbox (diff) | |
download | srb_lib-219e0ff7b8e40d49a1b386823bfbef9f25e94166.tar.gz srb_lib-219e0ff7b8e40d49a1b386823bfbef9f25e94166.tar.bz2 srb_lib-219e0ff7b8e40d49a1b386823bfbef9f25e94166.zip |
lib: rename to completed_levels, tk: show enabled levels
-rwxr-xr-x | srb.py | 26 | ||||
-rw-r--r-- | srb_lib.py | 55 | ||||
-rwxr-xr-x | srb_tk.py | 40 |
3 files changed, 71 insertions, 50 deletions
@@ -13,7 +13,7 @@ # Reference: # bgconf.py # Improve: -# still some confusion around the --unlock-everything --lock-everything and the levelset available levels. +# still some confusion around the --unlock-everything --lock-everything and the levelset completed levels. # Documentation: README.md # Dependencies: @@ -54,7 +54,7 @@ parser.add_argument("--set-level-status",action="append",help="Set completion st parser.add_argument("--set-level-balloons",action="append",help="Set balloon status for this level for profile. Example value to pass: \"0,all\" or \"0,none\"") parser.add_argument("--set-level-letters",action="append",help="Set collected letters for level for profile. Examples: 0,all 15,none") parser.add_argument("--get-levelset",help="Print status for this levelset for profile.") -parser.add_argument("--set-levelset-available-levels",action="append",help="Set number of available levels in this levelset for profile.") +parser.add_argument("--set-levelset-completed-levels",action="append",help="Set number of completed levels in this levelset for profile.") parser.add_argument("--get-name",action="store_true",help="Print name for profile.") parser.add_argument("--set-name",help="Set name for profile.") parser.add_argument("--get-profile-in-use",action="store_true",help="Print if profile is in use.") @@ -89,7 +89,7 @@ profile_id = args.profile #print(f"profile_id={profile_id}") # New actions that need --profile must be added here. -if not profile_id and (args.get_money or args.set_money or args.get_weapon or args.set_weapon or args.get_level or args.get_name or args.set_name or args.get_profile_in_use or args.get_purchased_weapons or args.get_tutorial_completed or args.add_purchased_weapons or args.remove_purchased_weapons or args.get_health or args.get_stunt or args.get_gun or args.set_health or args.set_stunt or args.set_gun or args.get_levelset or args.get_purchased_planes or args.add_purchased_planes or args.remove_purchased_planes or args.set_level_status or args.set_level_balloons or args.set_levelset_available_levels or args.set_level_letters or args.unlock_everything or args.lock_everything or args.buy_everything or args.unbuy_everything or args.set_level or args.set_profile_in_use): +if not profile_id and (args.get_money or args.set_money or args.get_weapon or args.set_weapon or args.get_level or args.get_name or args.set_name or args.get_profile_in_use or args.get_purchased_weapons or args.get_tutorial_completed or args.add_purchased_weapons or args.remove_purchased_weapons or args.get_health or args.get_stunt or args.get_gun or args.set_health or args.set_stunt or args.set_gun or args.get_levelset or args.get_purchased_planes or args.add_purchased_planes or args.remove_purchased_planes or args.set_level_status or args.set_level_balloons or args.set_levelset_completed_levels or args.set_level_letters or args.unlock_everything or args.lock_everything or args.buy_everything or args.unbuy_everything or args.set_level or args.set_profile_in_use): ferror("Warning: Cannot perform most actions without --profile. Not all tasks may run.") else: if args.get_money: @@ -233,17 +233,17 @@ else: ferror(f"Failed to add purchased planes {args.remove_purchased_planes} because {message}") else: srb_lib.write_file(args.file,0,data) - if args.set_levelset_available_levels: - for l in args.set_levelset_available_levels: + if args.set_levelset_completed_levels: + for l in args.set_levelset_completed_levels: levelset_num = -1 try: levelset_num, count = l.split(",") except: - ferror(f"Warning! Skipping un-parseable levelset available spec {l}. Please use \"0,all\" format.") + ferror(f"Warning! Skipping un-parseable levelset completed spec {l}. Please use \"0,all\" format.") if levelset_num != -1: - data, message = srb_lib.set_levelset_available_levels(args.file,profile_id,levelset_num,count) + data, message = srb_lib.set_levelset_completed_levels(args.file,profile_id,levelset_num,count) if (type(data) == int and data == -1) or message != "": - ferror(f"Failed to set profile {profile_id} level {levelset_num} available levels to {count} because {message}") + ferror(f"Failed to set profile {profile_id} level {levelset_num} completed levels to {count} because {message}") else: srb_lib.write_file(args.file,0,data) if args.set_level_letters: @@ -256,13 +256,13 @@ else: if level_num != -1: data, message = srb_lib.set_level_letters(args.file,profile_id,level_num, count) if (type(data) == int and data == -1) or message != "": - ferror(f"Failed to set profile {profile_id} level {levelset_num} available levels to {count} because {message}") + ferror(f"Failed to set profile {profile_id} level {levelset_num} completed levels to {count} because {message}") else: #print(f"good?") srb_lib.write_file(args.file,0,data) if args.unlock_everything: data = srb_lib._get_data_from_data_object(args.file) - # counting backwards helps the levelset available levels count. + # counting backwards helps the levelset completed levels count. for i in range(len(srb_lib.LEVELS)-1,0,-1): data, new_status, message = srb_lib.set_level_status(data,profile_id,i,"general") if message != "": @@ -277,7 +277,7 @@ else: ferror(f"Failed on loop {i}, set_level_letters, {message}") break for i in range(len(srb_lib.LEVELSETS)-1,0,-1): - data, message = srb_lib.set_levelset_available_levels(data,profile_id,i,"all") + data, message = srb_lib.set_levelset_completed_levels(data,profile_id,i,"all") if message != "": ferror(f"Failed on levelset loop {i}, {message}") break @@ -287,7 +287,7 @@ else: srb_lib.write_file(args.file,0,data) if args.lock_everything: data = srb_lib._get_data_from_data_object(args.file) - # counting backwards helps the levelset available levels count. + # counting backwards helps the levelset completed levels count. for i in range(len(srb_lib.LEVELS)-1,0,-1): data, new_status, message = srb_lib.set_level_status(data,profile_id,i,"none") if message != "": @@ -302,7 +302,7 @@ else: ferror(f"Failed on loop {i}, set_level_letters, {message}") break for i in range(len(srb_lib.LEVELSETS)-1,0,-1): - data, message = srb_lib.set_levelset_available_levels(data,profile_id,i,"none") + data, message = srb_lib.set_levelset_completed_levels(data,profile_id,i,"none") if message != "": ferror(f"Failed on levelset loop {i}, {message}") break @@ -28,7 +28,7 @@ # Dependencies: import sys, struct -srb_lib_version = "20240404a" +srb_lib_version = "20240405a" # Table of byte positions of values in the savegame file, minus the first four bytes which are the checksum. Due to zero-indexing of python lists, but for ease of usage, we will always put a zero as the value of index 0. That is, profile 1 will use index 1 of the list. # "Z<dddddddd" is the start of a profile. @@ -104,17 +104,18 @@ PLANES = [ {"p":0x3B20,"name":"all"}, ] -# c = balloon color # l = level count -# le = letters, calculated +# a = level 0 is Available if previous levelset has this number of completed missions +# c = balloon color # b = balloon count, calculated +# le = letters, calculated LEVELSETS = [ - {"id":0,"l":6,"c":"red" ,"b":0,"le":"","name":"Aerodrome Island"}, - {"id":1,"l":3,"c":"yellow","b":0,"le":"","name":"Woods of Montsec"}, - {"id":2,"l":3,"c":"green" ,"b":0,"le":"","name":"Front Lines of Verdun"}, - {"id":3,"l":5,"c":"blue" ,"b":0,"le":"","name":"Mines of the Matterhorn"}, - {"id":4,"l":3,"c":"orange","b":0,"le":"","name":"Verdon Gorge"}, - {"id":5,"l":2,"c":"none" ,"b":0,"le":"","name":"Flying Fortress"}, + {"id":0,"l":6,"a":0,"c":"red" ,"b":0,"le":"","name":"Aerodrome Island"}, + {"id":1,"l":3,"a":3,"c":"yellow","b":0,"le":"","name":"Woods of Montsec"}, + {"id":2,"l":3,"a":2,"c":"green" ,"b":0,"le":"","name":"Front Lines of Verdun"}, + {"id":3,"l":5,"a":2,"c":"blue" ,"b":0,"le":"","name":"Mines of the Matterhorn"}, + {"id":4,"l":3,"a":5,"c":"orange","b":0,"le":"","name":"Verdon Gorge"}, + {"id":5,"l":2,"a":3,"c":"none" ,"b":0,"le":"","name":"Flying Fortress"} ] # pos_r = relative position for level status @@ -411,7 +412,7 @@ def get_collected_breakables(data_object,profile_id, level, silent=False): print(f"Debug: abs 0x{CHECKSUM_LENGTH+pos_level_which_breakables:04x} which breakables: b{profile_level_which_breakables:05b}") return ','.join(breakables_list), breakables_mask -def set_level_status(data_object,profile_id,level,status,fix_levelset_available_levels=True): +def set_level_status(data_object,profile_id,level,status,fix_levelset_completed_levels=True): """ Set completion rank for a level, e.g., general """ data = _get_data_from_data_object(data_object) level_obj, message = get_level_info(level) @@ -427,19 +428,19 @@ def set_level_status(data_object,profile_id,level,status,fix_levelset_available_ #print(f"debug: will try to set bits {bits:02x}") data = srb_pack('<1I',data,PROFILE_START_POSITION[profile_id]+POS_LEVEL_START+(INT_SIZE*level_obj["pos_r"]),bits) current_status, _, _ = get_level_status(data, profile_id, level, silent=True) - levelset_available_levels = get_levelset_available_levels(data,profile_id,level_obj["setid"]) - #print(f"debug: levelset {level_obj['setid']} currently has {levelset_available_levels} available levels.") - # if setting to any completed status, if the levelset available levels is less than this level, then make it this. - if fix_levelset_available_levels: - if levelset_available_levels < (level_obj["set_pos"] + 1) and status not in ["none"]: - data, message = set_levelset_available_levels(data,profile_id,level_obj["setid"],level_obj["set_pos"] + 1) + levelset_completed_levels = get_levelset_completed_levels(data,profile_id,level_obj["setid"]) + #print(f"debug: levelset {level_obj['setid']} currently has {levelset_completed_levels} completed levels.") + # if setting to any completed status, if the levelset completed levels is less than this level, then make it this. + if fix_levelset_completed_levels: + if levelset_completed_levels < (level_obj["set_pos"] + 1) and status not in ["none"]: + data, message = set_levelset_completed_levels(data,profile_id,level_obj["setid"],level_obj["set_pos"] + 1) if message != "": - return -1, -1, f"Unable to set levelset available levels to minimum of this level set_pos {level_obj['set_pos']}" - # decrement the available levels in the levelset if clearing out this level and the available levels is exactly this one. - if levelset_available_levels == (level_obj["set_pos"] + 1) and status in ["none"]: - data, message = set_levelset_available_levels(data,profile_id,level_obj["setid"],level_obj["set_pos"]) + return -1, -1, f"Unable to set levelset completed levels to minimum of this level set_pos {level_obj['set_pos']}" + # decrement the completed levels in the levelset if clearing out this level and the completed levels is exactly this one. + if levelset_completed_levels == (level_obj["set_pos"] + 1) and status in ["none"]: + data, message = set_levelset_completed_levels(data,profile_id,level_obj["setid"],level_obj["set_pos"]) if message != "": - return -1, -1, f"Unable to decrement levelset available levels." + return -1, -1, f"Unable to decrement levelset completed levels." return data, current_status, "" def set_level_balloons(data_object,profile_id,level,count, silent = False): @@ -575,19 +576,19 @@ def set_level_letters(data_object,profile_id,level,letters, silent = False): data = srb_pack('<1I',data,pos_levelset_completed_letters_mask,new_letter_mask_final) return data, "" -def get_levelset_available_levels(data_object,profile_id,levelset): +def get_levelset_completed_levels(data_object,profile_id,levelset): data = _get_data_from_data_object(data_object) levelset_obj, message = get_levelset_info(levelset) if message != "": - return -1, f"For set_levelset_available_levels unable to get levelset for {levelset}." + return -1, f"For set_levelset_completed_levels unable to get levelset for {levelset}." pos_levelset_completed_mission_mask = PROFILE_START_POSITION[profile_id]+POS_LEVELSET_COMPLETED_MISSIONS_MASK+(INT_SIZE*levelset_obj["id"]) return data[pos_levelset_completed_mission_mask] -def set_levelset_available_levels(data_object,profile_id,levelset,completed_count): +def set_levelset_completed_levels(data_object,profile_id,levelset,completed_count): data = _get_data_from_data_object(data_object) levelset_obj, message = get_levelset_info(levelset) if message != "": - return -1, f"For set_levelset_available_levels unable to get levelset for {levelset}." + return -1, f"For set_levelset_completed_levels unable to get levelset for {levelset}." if completed_count == "all": completed_count = 8 # no levelset has more than 6 levels so this is a safe maximum, and it will get checked farther below. if completed_count == "none": @@ -596,13 +597,13 @@ def set_levelset_available_levels(data_object,profile_id,levelset,completed_coun try: completed_count = int(completed_count) except: - return -1, f"cannot set levelset available levels to {completed_count}" + return -1, f"cannot set levelset completed levels to {completed_count}" if completed_count < 0: completed_count = 0 completed_count = min(levelset_obj["l"], completed_count) pos_levelset_completed_mission_mask = PROFILE_START_POSITION[profile_id]+POS_LEVELSET_COMPLETED_MISSIONS_MASK+(INT_SIZE*levelset_obj["id"]) completed_bitmask = pow(2,completed_count)-1 - #print(f"debug: need to set levelset {levelset} available count to {completed_count}, which stored as a bitmask should be {completed_bitmask:07b}") + #print(f"debug: need to set levelset {levelset} completed count to {completed_count}, which stored as a bitmask should be {completed_bitmask:07b}") data = srb_pack('<1I',data,pos_levelset_completed_mission_mask,completed_bitmask) return data, "" @@ -23,8 +23,6 @@ # https://stackoverflow.com/questions/67334913/can-i-possibly-put-an-image-label-into-an-option-box-in-tkinter # Improve: # Add reset-level-breakables -# disable/gray out levels labels not reachable because of levelset_available_levels -# setting that sets the background colors of the regions # Dependencies: # dep-devuan: python3-tkinter, python3-pil.imagetk # rec-devuan: python3-cairosvg @@ -302,10 +300,8 @@ class App(tk.Frame): self.lbl_levelsets = [] self.spn_levelsets = [] self.ent_levelsets_balloons = [] - self.lbl_levelsets_name = tk.Label(self.frm_levels,text="Billboard/Available") + self.lbl_levelsets_name = tk.Label(self.frm_levels,text="Billboard/Completed") self.lbl_levelsets_name.grid(row=0,column=0,sticky="EW",columnspan=4) - #self.lbl_levelsets_available_levels = tk.Label(self.frm_levels,text="Available") - #self.lbl_levelsets_available_levels.grid(row=0,column=1,columnspan=2) self.lbl_levels_name = tk.Label(self.frm_levels,text="Level") self.lbl_levels_name.grid(row=0,column=5,sticky="EW") self.lbl_levels_status = tk.Label(self.frm_levels,text="Rank") @@ -505,15 +501,15 @@ class App(tk.Frame): bdata, message = srb_lib.set_purchased_planes(bdata, profile_id, "add", pur_planes_list, silent=self.silent.get()) if bdata == -1 or message != "": raise Exception(f"Failed when setting purchased planes, message {message}.") - # levelset available levels + # levelset completed levels for i in range(0,len(srb_lib.LEVELSETS)): - bdata, message = srb_lib.set_levelset_available_levels(bdata, profile_id, i, self.levelset_status_ints[i].get()) + bdata, message = srb_lib.set_levelset_completed_levels(bdata, profile_id, i, self.levelset_status_ints[i].get()) if bdata == -1 or message != "": - raise Exception(f"Failed to set levelset available levels for levelset {i}, message {message}.") + raise Exception(f"Failed to set levelset completed levels for levelset {i}, message {message}.") for i in range(0,len(srb_lib.LEVELS)): ts = self.level_statuses[i].get() if ts != "unknown": - bdata, current_status, message = srb_lib.set_level_status(bdata, profile_id, i, ts, fix_levelset_available_levels=False) + bdata, current_status, message = srb_lib.set_level_status(bdata, profile_id, i, ts, fix_levelset_completed_levels=False) if bdata == -1 or current_status == -1 or message != "": raise Exception(f"Failed to set level status for {i} to {ts}, because {message}") except Exception as e: @@ -688,7 +684,7 @@ class App(tk.Frame): self.pur_plane_str_woodstock.set(letters) elif 5 == x: self.pur_plane_str_baron.set(letters) - # levelset available levels + # levelset completed levels mission_mask = bin(int(mission_mask[1:],2)).count('1') #print(f"for levelset {i}, need to set max {i['l']}, current {mission_mask}") self.levelset_status_ints[x].set(mission_mask) @@ -696,6 +692,7 @@ class App(tk.Frame): x += 1 # levels for i in range(0,len(srb_lib.LEVELS)): + this_level = [j for j in srb_lib.LEVELS if j["id"] == i][0] tl_status, tl_balloons, tl_letters = srb_lib.get_level_status(self.bdata,profile_id,i,silent=True) # level status tl_status = int(tl_status,16) @@ -711,6 +708,29 @@ class App(tk.Frame): # level balloons self.level_balloons[i].set(tl_balloons) self.level_letters[i].set(tl_letters) + # level available because of completed levels + # we are not in the loop of levelsets, so we need to re-get mission_mask. + mission_mask, _, _ = srb_lib.get_levelset_status(self.bdata,profile_id,this_level["setid"],silent=True) + mission_mask = bin(int(mission_mask[1:],2)).count('1') + prev_mission_mask = 0 + this_levelset = [l for l in srb_lib.LEVELSETS if l["id"] == this_level["setid"]][0] + if this_level["setid"] > 0: + prev_mission_mask, _, _ = srb_lib.get_levelset_status(self.bdata,profile_id,this_level["setid"]-1,silent=True) + prev_mission_mask = bin(int(prev_mission_mask[1:],2)).count('1') + # work up from here + state = "disabled" + if this_level["set_pos"] == 0: + if this_level["setid"] == 0: + if srb_lib.get_tutorial_completed(self.bdata,profile_id): + state = "normal" + elif prev_mission_mask >= this_levelset["a"]: + state = "normal" + elif this_level["set_pos"] <= mission_mask: + state = "normal" + self.lbl_levels[this_level['id']].config(state=state) + # if level set_pos 0, then update the label for the whole levelset + if this_level["set_pos"] == 0: + self.lbl_levelsets[this_level["setid"]].config(state=state) # end if-profile_id is 1,2 or 3. # mark-traces-enable must happen at the end! self.mark_traces(True) |