aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2024-04-05 16:59:45 -0400
committerB. Stack <bgstack15@gmail.com>2024-04-05 16:59:45 -0400
commit9f94a372f0aabc2967efa38023086ca904f59c2d (patch)
treee82eac38eadbce2a481c273ddc6b5309202aa757
parentlib: rename to completed_levels, tk: show enabled levels (diff)
downloadsrb_lib-9f94a372f0aabc2967efa38023086ca904f59c2d.tar.gz
srb_lib-9f94a372f0aabc2967efa38023086ca904f59c2d.tar.bz2
srb_lib-9f94a372f0aabc2967efa38023086ca904f59c2d.zip
all: add reset breakables
-rw-r--r--images/breakables/Barrel.pngbin0 -> 330 bytes
-rw-r--r--images/breakables/Battery.pngbin0 -> 398 bytes
-rw-r--r--images/breakables/Geyser.pngbin0 -> 467 bytes
-rw-r--r--images/breakables/Haystack.pngbin0 -> 579 bytes
-rw-r--r--images/breakables/Pumpkin.pngbin0 -> 436 bytes
-rw-r--r--images/breakables/empty.pngbin0 -> 93 bytes
-rw-r--r--images/lightning1.pngbin12993 -> 0 bytes
-rwxr-xr-xsrb.py10
-rw-r--r--srb_lib.py22
-rwxr-xr-xsrb_tk.py103
10 files changed, 117 insertions, 18 deletions
diff --git a/images/breakables/Barrel.png b/images/breakables/Barrel.png
new file mode 100644
index 0000000..ba9d3cd
--- /dev/null
+++ b/images/breakables/Barrel.png
Binary files differ
diff --git a/images/breakables/Battery.png b/images/breakables/Battery.png
new file mode 100644
index 0000000..a6dae8f
--- /dev/null
+++ b/images/breakables/Battery.png
Binary files differ
diff --git a/images/breakables/Geyser.png b/images/breakables/Geyser.png
new file mode 100644
index 0000000..3bbd4c9
--- /dev/null
+++ b/images/breakables/Geyser.png
Binary files differ
diff --git a/images/breakables/Haystack.png b/images/breakables/Haystack.png
new file mode 100644
index 0000000..70eb56e
--- /dev/null
+++ b/images/breakables/Haystack.png
Binary files differ
diff --git a/images/breakables/Pumpkin.png b/images/breakables/Pumpkin.png
new file mode 100644
index 0000000..c9ded19
--- /dev/null
+++ b/images/breakables/Pumpkin.png
Binary files differ
diff --git a/images/breakables/empty.png b/images/breakables/empty.png
new file mode 100644
index 0000000..12fbf84
--- /dev/null
+++ b/images/breakables/empty.png
Binary files differ
diff --git a/images/lightning1.png b/images/lightning1.png
deleted file mode 100644
index efbf664..0000000
--- a/images/lightning1.png
+++ /dev/null
Binary files differ
diff --git a/srb.py b/srb.py
index 725ab88..3cbcd3d 100755
--- a/srb.py
+++ b/srb.py
@@ -53,6 +53,7 @@ parser.add_argument("--set-level",action="append",help="Set completion status, b
parser.add_argument("--set-level-status",action="append",help="Set completion status for this level for profile. Example value to pass: \"0,general\"")
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("--reset-level-breakables",action="append",help="Reset breakables for these levels for profile. Can be used multiple times.")
parser.add_argument("--get-levelset",help="Print status for 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.")
@@ -89,7 +90,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_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):
+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 or args.reset_level_breakables):
ferror("Warning: Cannot perform most actions without --profile. Not all tasks may run.")
else:
if args.get_money:
@@ -260,6 +261,13 @@ else:
else:
#print(f"good?")
srb_lib.write_file(args.file,0,data)
+ if args.reset_level_breakables:
+ for l in args.reset_level_breakables:
+ data, message = srb_lib.set_collected_breakables(args.file,profile_id,l)
+ if (type(data) == int and data == -1) or message != "":
+ ferror(f"Failed to reset {profile_id} level {l} breakables because {message}")
+ else:
+ 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 completed levels count.
diff --git a/srb_lib.py b/srb_lib.py
index be3683a..3cfa318 100644
--- a/srb_lib.py
+++ b/srb_lib.py
@@ -21,7 +21,6 @@
# <https://gamefaqs.gamespot.com/pc/930591-snoopy-vs-the-red-baron/faqs/46161>
# Improve:
# Rewrite as a class so we do not have to pass data around everywhere?
-# Write: set level breakables
# Documentation:
# winetricks vd=1024x768
# winetricks vd=off
@@ -412,6 +411,27 @@ 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_collected_breakables(data_object, profile_id, level, amount = "none"):
+ """ This resets the collected breakables for a level. Returns silently if given a level that does not have breakables. """
+ data = _get_data_from_data_object(data_object)
+ level_obj, message = get_level_info(level)
+ if message != "" or level == -1:
+ return -1, f"Unable to set collected breakables for {level}."
+ levelset_obj, message = get_levelset_info(level_obj["setid"])
+ if message != "" or levelset_obj == -1:
+ return -1, f"Unable to get levelset info for {level_obj['setid']}."
+ if not level_obj["r"]:
+ return data, ""
+ else:
+ thisint = 0 if amount == "none" else 5
+ thismask = 0 if amount == "none" else [i["p"] for i in BREAKABLES if i["name"] == "all"][0]
+ pos_level_balloons = PROFILE_START_POSITION[profile_id]+POS_LEVEL_BALLOONS+(level_obj["setid"]*POS_LEVEL_BALLOONS_MULTIPLIER_LEVELSET)+(POS_LEVEL_BALLOONS_MULTIPLIER_LEVEL*level_obj["set_pos"])
+ pos_level_breakables = pos_level_balloons + ((-2 * levelset_obj["id"]) + 12) * INT_SIZE
+ pos_level_which_breakables = pos_level_breakables + INT_SIZE
+ data = srb_pack("<1I",data,pos_level_breakables,thisint)
+ data = srb_pack("<1I",data,pos_level_which_breakables,thismask)
+ return data, ""
+
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)
diff --git a/srb_tk.py b/srb_tk.py
index dda1029..c141bb6 100755
--- a/srb_tk.py
+++ b/srb_tk.py
@@ -21,8 +21,14 @@
# possible way to have a widget store its variable: https://stackoverflow.com/questions/63871376/tkinter-widget-cgetvariable
# count number of 1s in a binary value https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation/71307775#71307775
# https://stackoverflow.com/questions/67334913/can-i-possibly-put-an-image-label-into-an-option-box-in-tkinter
+# haybale <a target="_blank" href="https://icons8.com/icon/8Sk1THlBUpC6/hay-bale">Hay Bale</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
+# barrel <a target="_blank" href="https://icons8.com/icon/23346/wooden-beer-keg">Wooden Beer Keg</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
+# geyser <a target="_blank" href="https://icons8.com/icon/IjFwYfQ6Hmgc/geyser">Geyser</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
+# pumpkin <a target="_blank" href="https://icons8.com/icon/17358/pumpkin">Pumpkin</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
+# battery <a target="_blank" href="https://icons8.com/icon/dwzqRjO6XcTN/battery">Battery</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
+# transparent image https://stackoverflow.com/questions/8376359/how-to-create-a-transparent-gif-or-png-with-pil-python-imaging
+# combine images https://stackoverflow.com/questions/30227466/combine-several-images-horizontally-with-python/30228308#30228308
# Improve:
-# Add reset-level-breakables
# Dependencies:
# dep-devuan: python3-tkinter, python3-pil.imagetk
# rec-devuan: python3-cairosvg
@@ -48,13 +54,16 @@ SPDX-License-Identifier: GPL-3.0-only
Icon courtesy of https://icons8.com/icon/9336/aircraft
"""
-def get_srb_image(filename, sizex, sizey, resample=Image.Resampling.LANCZOS):
+def get_srb_image(filename, sizex, sizey, resample=Image.Resampling.LANCZOS, photoImage = True):
""" helper function that returns an ImageTk from the named file from the configured images directory, resized for you. """
global IMAGE_FAILURES
if IMAGES and IMAGE_FAILURES < 5:
try:
img = Image.open(os.path.join(image_dir,filename)).resize((sizex,sizey),resample=resample)
- return ImageTk.PhotoImage(img)
+ if photoImage:
+ return ImageTk.PhotoImage(img)
+ else:
+ return img
except:
IMAGE_FAILURES += 1
print(f"Failed to load image {filename}, only {5-IMAGE_FAILURES} failures left before giving up on images entirely.",file=sys.stderr)
@@ -147,12 +156,14 @@ class App(tk.Frame):
self.level_letters = []
statuses = [i["name"] for i in srb_lib.LEVEL_STATUSES]
statuses_hex = [i["b"] for i in srb_lib.LEVEL_STATUSES]
+ self.level_breakables = []
for i in range(0,len(srb_lib.LEVELS)):
self.level_strs.append(tk.StringVar(value=srb_lib.get_level_info(i)[0]["name"]))
self.level_statuses_hex.append(tk.StringVar(value=statuses_hex[0]))
self.level_statuses.append(tk.StringVar(value=statuses[0]))
self.level_balloons.append(tk.IntVar())
self.level_letters.append(tk.IntVar())
+ self.level_breakables.append(tk.IntVar())
# END DATA VARIABLES
# statusbar variable
@@ -253,8 +264,6 @@ class App(tk.Frame):
self.spn_gun.grid(row=4,column=1)
self.lbl_pur = tk.Label(self.frm_curprof,text="Purchased: Weapons")
self.lbl_pur.grid(row=5,column=0)
- #self.lbl_pur_weap = tk.Label(self.frm_curprof,text="Weapons")
- #self.lbl_pur_weap.grid(row=5,column=0)
self.lbl_pur_weap = tk.Label(self.frm_curprof,text="Planes")
self.lbl_pur_weap.grid(row=5,column=1)
self.img_potato = get_srb_image("Potato Gun.png",16,16)
@@ -300,16 +309,12 @@ 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/Completed")
- self.lbl_levelsets_name.grid(row=0,column=0,sticky="EW",columnspan=4)
- 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")
- self.lbl_levels_status.grid(row=0,column=6,sticky="EW",columnspan=2)
- self.lbl_levels_balloons = tk.Label(self.frm_levels,text="Balloons")
- self.lbl_levels_balloons.grid(row=0,column=8,columnspan=4,sticky="EW")
- self.lbl_levels_letters = tk.Label(self.frm_levels,text="Letters")
- self.lbl_levels_letters.grid(row=0,column=12,columnspan=4,sticky="EW")
+ tk.Label(self.frm_levels,text="Billboard/Completed").grid(row=0,column=0,sticky="EW",columnspan=4)
+ tk.Label(self.frm_levels,text="Level").grid(row=0,column=5,sticky="EW")
+ tk.Label(self.frm_levels,text="Rank").grid(row=0,column=6,sticky="EW",columnspan=2)
+ tk.Label(self.frm_levels,text="Balloons").grid(row=0,column=8,columnspan=4,sticky="EW")
+ tk.Label(self.frm_levels,text="Letters").grid(row=0,column=12,columnspan=4,sticky="EW")
+ tk.Label(self.frm_levels,text="Breakables").grid(row=0,column=16,columnspan=1,sticky="EW")
self.lbl_levels = []
self.opt_levels_hex = []
self.opt_levels = []
@@ -319,8 +324,17 @@ class App(tk.Frame):
self.btn_balloons_all = []
self.btn_letters_none = []
self.btn_letters_all = []
- x = 1
+ self.btn_levels_breakables = []
+ self.bim_levels_breakables = []
self.img_balloons = []
+ self.img_breakables = []
+ self.BREAKABLES = sorted(srb_lib.BREAKABLES,key=lambda i: i["w"])
+ self.BREAKABLES = [b for b in self.BREAKABLES if b["w"] != 0]
+ self.BREAKABLES_NAMES = [b["name"] for b in self.BREAKABLES if b["w"] != 0]
+ for i in self.BREAKABLES:
+ self.img_breakables.append(get_srb_image(os.path.join(image_dir,"breakables",i["name"]+".png"),16,16,photoImage=False))
+ self.img_breakables.append(get_srb_image(os.path.join(image_dir,"breakables","empty.png"),16,16,photoImage=False))
+ x = 1
for i in range(0,len(srb_lib.LEVELSETS)):
tl = srb_lib.get_levelset_info(i)[0]
if "c" not in tl or tl["c"] == "none":
@@ -371,7 +385,17 @@ class App(tk.Frame):
self.btn_letters_none[-1].grid(row=x,column=14)
self.btn_letters_all.append(tk.Button(self.frm_levels,text="all",padx=0,pady=0,command=lambda tl=j,action="all",ty="letters": self.button_action_level(tl,action,ty)))
self.btn_letters_all[-1].grid(row=x,column=15)
+ # breakables
+ if "r" in j and j["r"]:
+ self.bim_levels_breakables.append(get_srb_image(os.path.join("breakables","empty.png"),16,16))
+ # until you apply an image, width of a widget measures approximate width of text characters
+ self.btn_levels_breakables.append(tk.Button(self.frm_levels,padx=0,pady=0,width=2,compound="left",command=lambda tl=j: self.button_reset_level_breakables(tl)))
+ if IMAGES:
+ # and now all of a sudden it does pixels.
+ self.btn_levels_breakables[-1].config(width=88,image=self.bim_levels_breakables[-1])
+ self.btn_levels_breakables[-1].grid(row=x,column=16)
x += 1
+ # end for-j-in-levels
x += 1
# end for-i-in-levelsets
@@ -381,6 +405,22 @@ class App(tk.Frame):
# and now open the first file
self.func_open(self.current_file.get())
+ def get_single_image_from_breakables(self,b_list):
+ """ Given a list of breakable names, return a single image that has those items displayed. Fixed size of 80px x 16px. """
+ #print(f"debug: got list of {b_list}")
+ if IMAGES:
+ new_im = Image.new("RGBA",(16*5,16),(255,127,127,0))
+ x_offset = 0
+ for i in self.BREAKABLES_NAMES:
+ using = i in b_list
+ #print(f"For {i}, using {using}, index is {self.BREAKABLES_NAMES.index(i)}")
+ this_img = self.img_breakables[self.BREAKABLES_NAMES.index(i)] if i in b_list else self.img_breakables[-1]
+ new_im.paste(this_img, (x_offset,0))
+ x_offset += this_img.size[0]
+ return ImageTk.PhotoImage(new_im)
+ else:
+ return -1
+
def set_frame_backgrounds(self, arg1 = None, arg2 = None, arg3 = None):
""" Reacts to the frame colors checkbox and sets frames which make development and layout easier. """
if self.frame_backgrounds.get():
@@ -393,6 +433,18 @@ class App(tk.Frame):
self.frm_curprof.config(background=bg,borderwidth=0)
self.frm_levels.config(background=bg,borderwidth=0)
+ def button_reset_level_breakables(self, level_obj):
+ """ Handle button press to reset breakables for a level. """
+ profile_id = self.selected_profile.get()
+ _, b_mask = srb_lib.get_collected_breakables(self.bdata,profile_id,level_obj,silent=True)
+ amount = "all" if 0 == b_mask else "none"
+ bdata, message = srb_lib.set_collected_breakables(self.bdata,profile_id,level_obj,amount)
+ if -1 == bdata or message != "":
+ raise Exception("Unable to reset breakables for level {level_obj} because {message}")
+ else:
+ self.bdata = bdata
+ self.load_form_into_data()
+
def button_action_level(self, level_obj, action, type_):
""" Handle button press from "none" and "all" for balloons and letters for a level """
#print(f"debug (button_action_level): {level_obj}, {action}, {type_}")
@@ -623,6 +675,7 @@ class App(tk.Frame):
if not self.bdata:
return "No file loaded."
profile_id = self.selected_profile.get()
+ y = 0
if profile_id >= 1 and profile_id <= 3:
self.mark_traces(False)
# money
@@ -690,6 +743,7 @@ class App(tk.Frame):
self.levelset_status_ints[x].set(mission_mask)
self.levelset_balloons_ints[x].set(balloon_count)
x += 1
+ # end for-in-in-levelsets
# levels
for i in range(0,len(srb_lib.LEVELS)):
this_level = [j for j in srb_lib.LEVELS if j["id"] == i][0]
@@ -731,6 +785,23 @@ class App(tk.Frame):
# 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)
+ if this_level["r"]:
+ b_str, b_mask = srb_lib.get_collected_breakables(self.bdata,profile_id,this_level,silent=True)
+ if "all" == b_str:
+ b_str = ','.join([b["name"] for b in self.BREAKABLES])
+ b_list = b_str.split(",")
+ if "none" in b_list:
+ b_list.pop(b_list.index("none"))
+ #print(f"debug: for {this_level['name']}, i {i}, y {y}, got b {b_list}, mask {b_mask}")
+ self.bim_levels_breakables[y] = self.get_single_image_from_breakables(b_list)
+ if self.bim_levels_breakables[y] != -1: # which really means, if IMAGES
+ self.btn_levels_breakables[y].config(image=self.bim_levels_breakables[y])
+ self.btn_levels_breakables[y].config(text=str(len(b_list)))
+ # y counts number of levels with breakables
+ y = y + 1
+ # end of if-level-has-breakables
+
+ # end for-i-in-levels
# end if-profile_id is 1,2 or 3.
# mark-traces-enable must happen at the end!
self.mark_traces(True)
bgstack15