def restore_fields(param, default_param, fields_to_restore): log(f"Restoring fields {fields_to_restore} for param {param.name}") for key in param.data: for field in fields_to_restore: old_value = param.data[key][field] value = default_param.data[key][field] log(f"Restore Field: {field} [{old_value} -> {value}]") param.data[key][field] = value return param
def main(name="Bullet",param_path="",layout_path="",save_dir=""): full_param_path = param_path + name +".param" paramb = pr.parse_param(full_param_path) param_data = pr.pack_param(paramb, full_param_path) log("Saving param") saved_file = dt.save_file(param_data,save_dir + name + ".param") log("Done saving") return paramb
def replace_zero_in_field(param, replacement_value, field): """ Replaces all zeros in specific field """ log( f"replacing all 0 in field {field} with {replacement_value} for param {param.name}", Logging_Level.INFO) for key in param.data: if (param.data[key][field] == 0): param.data[key][field] = replacement_value log(f"ID: {key}", Logging_Level.INFO) return param
def alter_params(name="Bullet"): full_param_path = param_path + name +".param" paramb = pr.parse_param(full_param_path) paramb = dop.replaceZero(paramb,1) param_data = pr.pack_param(paramb, full_param_path) log("Saving param") saved_file = dt.save_file(param_data,save_dir + name + ".param") log("Done saving") return paramb
def shuffle_param_ids(name="Bullet"): full_param_path = param_path + name +".param" paramb = pr.parse_param(full_param_path) paramb = dop.shuffle_ids(paramb) param_data = pr.pack_param(paramb, full_param_path) log("Saving param") saved_file = dt.save_file(param_data,save_dir + name + ".param") log("Done saving") return paramb
def mod_speffect_param(chance,name="SpEffectParam"): full_param_path = param_path + name +".param" paramb = pr.parse_param(full_param_path) paramb = dop.shuffle_ids(paramb) paramb = dop.add_random_self_refs(paramb,78,chance) #replacespeffectId #paramb = dop.restore_fields(paramb,get_param(name),0) #paramb = dop.limit_fields(paramb,{4:[0.1,5,1]}) param_data = pr.pack_param(paramb, full_param_path) log("Saving param") saved_file = dt.save_file(param_data,save_dir + name + ".param") log("Done saving") return paramb
def add_random_self_refs(param, fields_to_change, chance=0.3): """ assign random ids to the specified field if the value is <0 and random > chance """ log(f"Adding random id refs to some ids for param {param.name}") ids = list(param.data.keys()) random.shuffle(ids) index = 0 value_to_set = 0 for key in param.data: random_chance = random.random() if (random_chance <= chance): for field in fields_to_change: if (param.data[key][field] > 1): pass else: value_to_set = ids[index] if (value_to_set != key): index += 1 log( f"Set field {field} of ID: {key} to ID: {value_to_set}", Logging_Level.INFO) param.data[key][field] = ids[index] log(f"[{index}] references added") return param
def replaceZero(param, replacement_value): """ Replaces all zeros in any field """ log(f"replacing all 0 with {replacement_value} for param {param.name}", Logging_Level.INFO) for key in param.data: log(f"ID: {key}", Logging_Level.INFO) index = 0 for field in param.layout: log(f"Field: {field.name}", Logging_Level.INFO) log(f"Type: {field.variable_type}", Logging_Level.INFO) value = param.data[key][index] if (value == 0): param.data[key][index] = replacement_value index += 1 return param
def parse_type_string(input_string): t_string = input_string e_type = Data_type.String.value signed = False size = 0 if (t_string[0:6] == "fixstr"): log("ERROR: fixstr not implemented yet", Logging_Level.ERROR) size = int(int(t_string[5:]) / 8) return e_type, size, False if (t_string[0:5] == "dummy"): size = int(int(t_string[5:]) / 8) e_type = Data_type.AoB.value return e_type, size, False elif (t_string[0] == "f"): size = int(int(t_string[1:]) / 8) if (size == 4): e_type = Data_type.Float.value elif (size == 8): e_type = Data_type.Double.value else: log("ERROR unknown floatingpoint size", Logging_Level.ERROR) return e_type, size, False elif t_string[0] == "b": return Data_type.Binary.value, 0, False else: if (t_string[0] == "s"): signed = True size = int(int(t_string[1:]) / 8) if (size == 1): e_type = Data_type.Byte.value if (size == 2): e_type = Data_type.Bytes_2.value if (size == 4): e_type = Data_type.Bytes_4.value if (size == 8): e_type = Data_type.Bytes_8.value return e_type, size, signed
def replace_zero_fields(param, dict_field_value): log(f"replacing all 0 in fields {dict_field_value.keys()} for param {param.name}" ) for key in param.data: log(f"ID: {key}") for field in dict_field_min_max.keys(): log(f"Field: {field}") value = param.data[key][field] if (value == 0): param.data[key][field] = dict_field_value[field] return param
def limit_fields(param, dict_field_min_max, ignore_inf=False): """ param, {min,max,default} treats -1 as max""" log(f"Limiting fields {dict_field_min_max.keys()} for param {param.name}") for key in param.data: for field in dict_field_min_max.keys(): value = param.data[key][field] if (dict_field_min_max[field][1] < value or (abs(value + 1.0) < 0.01 and not ignore_inf)): new_value = dict_field_min_max[field][3] param.data[key][field] = new_value log(f"clamped[{value} -> {new_value}]") if (value < dict_field_min_max[field][0] and abs(value + 1.0) > 0.01): new_value = dict_field_min_max[field][2] param.data[key][field] = new_value log(f"clamped[{value} -> {new_value}]") return param
def copy_param_data(param, second_param, fields_to_ignore): log(f"Copying fields for param {param.name}") log(f"Ignoring {fields_to_ignore}") ids2 = list(second_param.data.keys()) id_index = 0 for key in param.data: field_index = 0 for field in param.layout: if (field_index in fields_to_ignore): pass #log(f"Ignoring field {field.name}", Logging_Level.DEBUG) else: old_value = param.data[key][field_index] value = second_param.data[ids2[id_index]][field_index] #log(f"Restore Field: {field.name} [{old_value} -> {value}]", Logging_Level.DEBUG) param.data[key][field_index] = value field_index += 1 id_index += 1 log(f"{id_index} IDs replaced") return param
def print_layout(param): for i, field in enumerate(param.layout): log(f"[{i}]: {field.name} : {field.variable_type}", Logging_Level.INFO)
def multiply_random(param, fields_to_change, chance=0.3, mult_max=3, adjust_bullet_angle=False): """ multiply specified field for random entries if the value is <0 and random > chance """ log( f"Mult random for param {param.name}\nMult_max{mult_max} Field{fields_to_change}", Logging_Level.INFO) SHOOT_ANGLE = 34 SHOOT_ANGLE_INTERVAL = 35 index = 0 value_to_set = 0 for key in param.data: random_chance = random.random() if (random_chance <= chance): for field in fields_to_change: b_multiplier = random.randint(1, mult_max) temp_value_to_set = int(param.data[key][field] * b_multiplier) log(f"Set field {field} of ID: {key} to value: {value_to_set}") value_to_set = max(min(temp_value_to_set, 32767), -32768) if (value_to_set > 50): log( f"\n[WARNING] num_shoot > 50\n NUM:{value_to_set} (ORIGINAL: {param.data[key][field]})ID: {key}\n", Logging_Level.WARN) if (value_to_set != temp_value_to_set): log(f"num_shoot value outside of short", Logging_Level.WARN) log(f"Value set to 50", Logging_Level.WARN) value_to_set = int(50) param.data[key][field] = value_to_set if (adjust_bullet_angle and param.name == "BULLET_PARAM_ST"): if (field == 32): old_shoot_angle = param.data[key][SHOOT_ANGLE] new_shoot_angle = 0 old_shoot_angle_interval = param.data[key][ SHOOT_ANGLE_INTERVAL] if (b_multiplier % 2 == 1): if (old_shoot_angle == 0): new_shoot_angle = int(-3 * (b_multiplier - 1) / 2) else: new_shoot_angle = int(old_shoot_angle + (old_shoot_angle * (b_multiplier - 1) / 2)) param.data[key][SHOOT_ANGLE] = max( min(new_shoot_angle, 32767), -32768) if (old_shoot_angle_interval == 0): param.data[key][SHOOT_ANGLE_INTERVAL] = int(3) #else: param.data[key][SHOOT_ANGLE_INTERVAL] = old_shoot_angle_interval else: #keep one shot centered if (old_shoot_angle_interval == 0): param.data[key][SHOOT_ANGLE_INTERVAL] = int(3) log( f"Adjusted angles to {param.data[key][SHOOT_ANGLE]} : {param.data[key][SHOOT_ANGLE_INTERVAL]}", Logging_Level.DEBUG) return param
def mod_param(): log("\n\n---------- [Unpacking Param] -----------\n", Logging_Level.INFO) param = pr.parse_param(param_file_path,layout_path) if(replace_zeros!=0): log("[Replace Zeros] ->", Logging_Level.INFO) param = dop.replace_zero(param,replace_zeros) if(shuffle_param_ids): log("[Shuffle Ids] ->", Logging_Level.INFO) field_list_keep = [] for field in fields_to_keep: field_index = pr.get_field_index(field,param.layout) field_list_keep.append(field_index) if(shuffle_safe): if(param.name=="BULLET_PARAM_ST"): param_pc_atk = pr.parse_param(param_path + "AtkParam_Pc.param",layout_path) param_npc_atk = pr.parse_param(param_path + "AtkParam_Npc.param",layout_path) pc_ids = param_pc_atk.data.keys() npc_ids = param_npc_atk.data.keys() if(len(self_ref_fields)>0): log("[Add self references SAFE] ->") param = dop.shuffle_bullet_ids_safe(param,field_list_keep,ids_to_keep,pc_ids,npc_ids,secondary_only,chance) else: param = dop.shuffle_bullet_ids_safe(param,field_list_keep,ids_to_keep,pc_ids,npc_ids,secondary_only) else: log(f"Safe shuffle not implemented for {param.name}") return else: param = dop.shuffle_ids(param,field_list_keep,ids_to_keep,secondary_only) if(len(self_ref_fields)>0 and not shuffle_safe): log("[Add self references] ->") field_list_ref = [] for field in self_ref_fields: index = pr.get_field_index(field,param.layout) field_list_ref.append(index) param = dop.add_random_self_refs(param,field_list_ref,chance) if(len(rand_bullet_mult_fields)>0): log("[Multiply random fields] ->") field_list_mult_ref = [] for field in rand_bullet_mult_fields: index = pr.get_field_index(field,param.layout) field_list_mult_ref.append(index) param = dop.multiply_random(param,field_list_mult_ref,chance,mult_max,adjust_bullet_angle) if(make_bullets_visible>0 and param.name=="BULLET_PARAM_ST"): log("[Add SFX to invisible bullets] ->") param = dop.replace_zero_in_field(param,make_bullets_visible,1) if(len(restore)>0): log("[Restore fields] ->") field_list_res = [] for field in restore: index = pr.get_field_index(field,param.layout) field_list_res.append(index) default_param = pr.parse_param(param_file_path,layout_path) param = dop.restore_fields(param,default_param,field_list_res) #TODO its probably better to deepcopy the original param if(len(copy_param)>0): log("[Copy fields] ->") field_list_ignore = [] for field in copy_ignore: index = pr.get_field_index(field,param.layout) field_list_ignore.append(index) log("ignore: ", field) second_param = pr.parse_param(copy_param[0],copy_param[1]) param = dop.copy_param_data(param,second_param,field_list_ignore) #paramb = dop.restore_fields(paramb,get_param(name),0) if(len(limit)>0): log("[Limiting values] ->") param = dop.limit_fields(param,limit_dict) log("\n\n---------- [Repacking Param] -----------\n") param_data = pr.pack_param(param, param_file_path) log("[Saving param]") saved_file = dt.save_file(param_data,save_dir + param_name + ".param") if(saved_file!=None): log("[Done saving]") else: log("Couldn't save packed param to file", Logging_Level.ERROR) return param
def shuffle_bullet_ids_safe(param, fields_to_keep, ids_to_keep, atk_pc, atk_npc, secondary_only, chance=0): log(f"Randomizing all ids for param {param.name} (Safe Mode)") log(f"Excluded Fields [{fields_to_keep})") log(f"Excluded IDs [{ids_to_keep})") atk_inter = atk_pc & atk_npc atk_pc_only = atk_pc - atk_npc atk_npc_only = atk_npc - atk_pc ids_pc = get_param_ids_with_value_in_field(param, 0, atk_pc_only) ids_npc = get_param_ids_with_value_in_field(param, 0, atk_npc_only) ids_inter = get_param_ids_with_value_in_field(param, 0, atk_inter) secondary_ids = [] if (secondary_only): secondary_ids = get_secondary_bullet_ids(param) log(f"secondary Hit IDs:{secondary_ids}", Logging_Level.DEBUG) log( f"{len(secondary_ids)} of {len(param.data.keys())} bullets have hit ids", Logging_Level.DEBUG) random.shuffle(ids_pc) random.shuffle(ids_npc) random.shuffle(ids_inter) log("ids:") log(ids_pc[0:10]) log(ids_npc[0:10]) log(ids_inter[0:10]) param_data = param.data new_param_data = copy.deepcopy(param_data) index = 0 for key in new_param_data: replacement_id = 0 hit_id = 0 if (key in ids_to_keep): log(f"skipping {key}") else: if (key in ids_inter): replacement_id = ids_inter[index % len(ids_inter)] hit_id = ids_inter[(index + 1) % len(ids_inter)] else: if (key in ids_pc): replacement_id = ids_pc[index % len(ids_pc)] hit_id = ids_pc[(index + 1) % len(ids_pc)] if (key in ids_npc): replacement_id = ids_npc[index % len(ids_npc)] hit_id = ids_npc[(index + 1) % len(ids_npc)] if ((not secondary_only) or (key in secondary_ids)): new_param_data[key] = param_data[replacement_id] log(f"Swapping ID: {key} with ID: {replacement_id}") if (random.random() <= chance): # and new_param_data[key][26]<=0): log(f"Set hitId {hit_id} for id {key}") new_param_data[key][26] = hit_id index += 1 if (len(fields_to_keep) > 0): for key in new_param_data: for field in fields_to_keep: log(f"restore field {field} for ID: {key}") new_param_data[key][field] = param_data[key][field] param.data = new_param_data log("\n\nCHECK FOR LOOPS 1\n\n") param = check_loops(param, 26, secondary_ids, True) #TODO maybe add this as a flag log("\n\nCHECK FOR LOOPS AGAIN 2\n\n") param = check_loops(param, 26, secondary_ids, False) if (10001500 in param.data.keys()): log("Rebalancing bullet 10001500 for Sekiro") #alter the bullet that is fired at Sekiro when enemies discover you #this makes sure the player is not nuked at sight param.data[10001500][ 4] = 2 #double the time until the childbullet is spawned random_sfxid = param.data[ids_inter[random.randint( 0, len(ids_inter) - 1)]][1] if (random_sfxid > 0): param.data[10001500][1] = random_sfxid else: param.data[10001500][1] = 60 #only allow one childbullet childbullet = param.data[10001500][26] if (childbullet > -1): param.data[childbullet][26] = -1 else: log("No bullet with id 10001500 - probably not Sekiro Bulletparam") return param
def check_loops(param, field, secondary_ids=[], fix_loops=False): ids_checked = [] current_chain = [] current_id = 0 discovered_loops = [] longest_loop = [] for id in param.data.keys(): current_id = id if (current_id in ids_checked ): #if one id was checked, all later in the chain were log(f"ID {current_id} was already checked (Skipped)", Logging_Level.DEBUG) else: while True: if (current_id not in param.data.keys()): log( f"[WARNING] referenced bullet {current_id} does not exist", Logging_Level.WARN) break if (param.data[current_id][field] < 0): break if (current_id in current_chain): log( f"Infinite Loop [LEN {len(current_chain)}] detected for IDs:\n{current_chain}\n", Logging_Level.DEBUG) discovered_loops.append( current_chain ) #additionally save a list of all loops lists if (len(current_chain) > len(longest_loop)): longest_loop = current_chain break else: current_chain.append(current_id) current_id = param.data[current_id][field] ids_checked.extend(current_chain) current_chain = [] if (id not in ids_checked): ids_checked.append(id) if (len(discovered_loops) > 0): log(f"Number of Loops discovered: {len(discovered_loops)}") log(f"Longest Loop [LEN {len(longest_loop)}]: {longest_loop}") else: log("\n\n\n\n---- [No loops found] ----\n\n\n\n") if (fix_loops): for loop in discovered_loops: fixed_loop = False for n in range(len(loop) - 1, 0, -1): l_id = loop[n] if (l_id in secondary_ids or len(secondary_ids) < 1): if (param.name == "BULLET_PARAM_ST"): param.data[l_id][ field] = 0 #set to special bullet without hitid else: param.data[l_id][field] = -1 #set to invalid fixed_loop = True log(f"broke loop at id {l_id}") break if (not fixed_loop): log(f"Could not break loop by replacing secondary hitids\nReplacing first hitid instead [LOOP:{loop}]" ) log(f"fixed {len(discovered_loops)} loops") if (param.name == "BULLET_PARAM_ST"): log("[modifying bullet 0]") #alter bullet 0 param.data[0][ 0] = 200 #might have to change this or create special atkid, but 200 is in both pc and npc atk param and is not empty param.data[0][1] = 1060 # sfxid = idol blue flame param.data[0][4] = 1 param.data[0][26] = -1 param.data[0][32] = 1 #num shoot return param
def shuffle_ids(param, fields_to_keep, ids_to_keep, secondary_only): """ shuffle all ids for the specified param """ log(f"Randomizing all ids for param {param.name}", Logging_Level.INFO) log(f"Excluded Fields [{fields_to_keep})", Logging_Level.INFO) log(f"Excluded IDs [{ids_to_keep})", Logging_Level.INFO) ids = list(param.data.keys()) random.shuffle(ids) log(ids[0:10], Logging_Level.DEBUG) secondary_ids = [] if (secondary_only): secondary_ids = get_secondary_bullet_ids(param) log(f"secondary Hit IDs:{secondary_ids}") log( f"{len(secondary_ids)} of {len(param.data.keys())} bullets have hit ids", Logging_Level.DEBUG) param_data = param.data new_param_data = copy.deepcopy(param_data) index = 0 for key in new_param_data: if (key in ids_to_keep or (secondary_only and key not in secondary_ids)): log(f"skipping {key}", Logging_Level.INFO) else: new_param_data[key] = param_data[ids[index]] log(f"Swapping ID: {key} with ID: {ids[index]}", Logging_Level.INFO) index += 1 if (len(fields_to_keep) > 0): for key in new_param_data: for field in fields_to_keep: log(f"restore field {field} for ID: {key}", Logging_Level.INFO) new_param_data[key][field] = param_data[key][field] param.data = new_param_data log("\n\nCHECK FOR LOOPS\n\n") param = check_loops(param, 26, secondary_ids, True) #TODO maybe add this as a flag return param
parser.add_argument("--copy", dest="copy_param", nargs="?", help="Param to copy to this one [PATH_TO_PARAM] [PATH_TO_LAYOUT_PREFIX]") parser.add_argument("--copy_ignore", dest="copy_ignore", nargs="+", help="Param fields to ignore when copying (name or index)") parser.add_argument("-i", "--interactive", action="store_true", dest="interactive", default=False, help="don't load or process any param files (use with interactive console)") if(len(sys.argv)==1): sys.argv.append("--help") log(sys.argv) args = parser.parse_args() d_args = vars(args) """ read arguments """ param_name = d_args["param_name"] param_path = d_args["param_path"] layout_path = d_args["layout_path"] save_dir = d_args["out_path"] # Alternative param_file_path = d_args["param_filename"] layout_file_path = d_args["layout_filename"] set_LOG_LEVEL(d_args["verbosity"]) if(bool(d_args["debug_level"])):