WeaponScriptToWiki.py: Difference between revisions
Skizmophonic (talk | contribs) Created page with "import os import re # Utility functions for conversions def kg_to_g(kg): return round(kg * 1000, 2) def kg_to_grains(kg): return round(kg * 15432.36, 2) def kg_to_lbs(kg): return round(kg * 2.20462, 2) # Load the template file def load_template(template_path): with open(template_path, "r", encoding="utf-8") as f: return f.read() # Parse key-value pairs from script file def parse_file(file_path): variables = {} with open(file_path, "r"..." |
Skizmophonic (talk | contribs) No edit summary |
||
| Line 17: | Line 17: | ||
return f.read() | return f.read() | ||
# Parse key-value pairs from script file | # Parse key-value pairs from script file (Source-style weapon script) | ||
def parse_file(file_path): | def parse_file(file_path): | ||
variables = {} | variables = {} | ||
| Line 28: | Line 28: | ||
return variables | return variables | ||
# | # Generic lookup from vietnam_english.txt, stripping any leading '#' | ||
def | def lookup_english_token(token_key): | ||
file_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_english.txt" | file_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_english.txt" | ||
key = token_key.lstrip('#') | |||
encodings = ["utf-8", "utf-16", "latin-1"] | encodings = ["utf-8", "utf-16", "latin-1"] | ||
for enc in encodings: | for enc in encodings: | ||
| Line 42: | Line 38: | ||
for line in f: | for line in f: | ||
line = line.strip() | line = line.strip() | ||
m = re.match(r'^\s*"(.+?)"\s+"(.+?)"\s*$', line) | |||
if m: | |||
if | k, v = m.groups() | ||
if k.lower() == key.lower(): | |||
return v | |||
if | |||
return | |||
except UnicodeDecodeError: | except UnicodeDecodeError: | ||
continue | continue | ||
return None | return None | ||
# Retrieve the printnameenglish value (kept for existing behavior) | |||
def get_printname_english(printname): | |||
return lookup_english_token(printname) | |||
# Get the origin value and process it | # Get the origin value and process it | ||
def get_origin(origin): | def get_origin(origin): | ||
origin = origin.lstrip('#').replace("_", " ").title() | origin = origin.lstrip('#').replace("_", " ").title() | ||
return origin | return origin | ||
# | # ------------------------- | ||
def preprocess_calculations(variables, file_name, folder_path): | # Loadout parsing utilities | ||
# ------------------------- | |||
CLASSES = {"assault", "medic", "gunner", "sniper", "engineer", "radioman"} | |||
FACTIONS = {"US", "VC"} | |||
def parse_vietnam_loadout(loadout_path): | |||
""" | |||
Parse a KeyValues-style loadout and build a mapping: | |||
weapon_name -> {"factions": set(...), "classes": set(...)} | |||
We only consider weapons listed under the "weapons" tree. | |||
""" | |||
mapping = {} | |||
if not os.path.exists(loadout_path): | |||
return mapping | |||
pending_key = None | |||
stack = [] | |||
cur_faction = None | |||
cur_class = None | |||
in_weapons_block = False | |||
def push(key): | |||
nonlocal cur_faction, cur_class, in_weapons_block | |||
stack.append(key) | |||
if key == "weapons": | |||
in_weapons_block = True | |||
if key in FACTIONS: | |||
cur_faction = key | |||
if key in CLASSES: | |||
cur_class = key | |||
def pop(): | |||
nonlocal cur_faction, cur_class, in_weapons_block | |||
if not stack: | |||
return | |||
key = stack.pop() | |||
if key in FACTIONS and cur_faction == key: | |||
cur_faction = None | |||
if key in CLASSES and cur_class == key: | |||
cur_class = None | |||
if key == "weapons" and "weapons" not in stack: | |||
in_weapons_block = False | |||
with open(loadout_path, "r", encoding="utf-8", errors="ignore") as f: | |||
for raw in f: | |||
line = raw.strip() | |||
# Strip comments | |||
if "//" in line: | |||
line = line[: line.index("//")].rstrip() | |||
if not line: | |||
continue | |||
m_key_only = re.fullmatch(r'"([^"]+)"', line) | |||
if m_key_only: | |||
pending_key = m_key_only.group(1) | |||
continue | |||
if line == "{": | |||
if pending_key is not None: | |||
push(pending_key) | |||
pending_key = None | |||
else: | |||
push("__anon__") | |||
continue | |||
if line == "}": | |||
pop() | |||
continue | |||
m_pair = re.fullmatch(r'"([^"]+)"\s+"([^"]*)"', line) | |||
if m_pair and in_weapons_block and cur_faction and cur_class: | |||
key = m_pair.group(1) | |||
if key.startswith("weapon_"): | |||
w = mapping.setdefault(key, {"factions": set(), "classes": set()}) | |||
w["factions"].add(cur_faction) | |||
w["classes"].add(cur_class) | |||
continue | |||
m_inline_open = re.fullmatch(r'"([^"]+)"\s*\{', line) | |||
if m_inline_open: | |||
push(m_inline_open.group(1)) | |||
continue | |||
return mapping | |||
def file_contains_weapon(path, weapon_key): | |||
""" | |||
Simple presence check for a weapon key in a text file. | |||
Uses a word-boundary regex to reduce false positives. | |||
""" | |||
if not os.path.exists(path): | |||
return False | |||
try: | |||
with open(path, "r", encoding="utf-8", errors="ignore") as f: | |||
content = f.read() | |||
pattern = r'\b' + re.escape(weapon_key) + r'\b' | |||
return re.search(pattern, content) is not None | |||
except Exception: | |||
return False | |||
def preprocess_calculations(variables, file_name, folder_path, loadout_main, loadout_zombie, loadout_special, paths): | |||
calculations = {} | calculations = {} | ||
damage_generic = float(variables.get("damagegeneric", "0")) | damage_generic = float(variables.get("damagegeneric", "0")) | ||
damage_head_multiplier = float(variables.get("damageheadmultiplier", "1")) | damage_head_multiplier = float(variables.get("damageheadmultiplier", "1")) | ||
| Line 72: | Line 170: | ||
damage_arm_multiplier = float(variables.get("damagearmmultiplier", "1")) | damage_arm_multiplier = float(variables.get("damagearmmultiplier", "1")) | ||
if damage_generic > 0: | if damage_generic > 0: | ||
calculations["damagegenericxdamageheadmultiplier"] = round(damage_generic * damage_head_multiplier, 2) | calculations["damagegenericxdamageheadmultiplier"] = round(damage_generic * damage_head_multiplier, 2) | ||
| Line 84: | Line 181: | ||
calculations["damagegenericxdamagestomachmultiplier"] = "N/A" | calculations["damagegenericxdamagestomachmultiplier"] = "N/A" | ||
calculations["damagegenericxdamagelegmultiplier"] = "N/A" | calculations["damagegenericxdamagelegmultiplier"] = "N/A" | ||
calculations[" | calculations["damagegenericxdamagearmultiplier"] = "N/A" | ||
has_bayonet = variables.get("hasbayonet", "0") == "1" | has_bayonet = variables.get("hasbayonet", "0") == "1" | ||
calculations["hasbayonet"] = "YES" if has_bayonet else "NO" | calculations["hasbayonet"] = "YES" if has_bayonet else "NO" | ||
file_base_name = os.path.splitext(file_name)[0] | file_base_name = os.path.splitext(file_name)[0] | ||
rifle_grenade_file = os.path.join(folder_path, f"{file_base_name}_riflegrenade.txt") | rifle_grenade_file = os.path.join(folder_path, f"{file_base_name}_riflegrenade.txt") | ||
| Line 96: | Line 191: | ||
calculations["hasriflegrenade"] = "YES" if has_rifle_grenade else "NO" | calculations["hasriflegrenade"] = "YES" if has_rifle_grenade else "NO" | ||
bullet_weight = float(variables.get("bullet_weight", "0")) | bullet_weight = float(variables.get("bullet_weight", "0")) | ||
weight = float(variables.get("weight", "0")) | weight = float(variables.get("weight", "0")) | ||
calculations["bullet_weight"] = kg_to_g(bullet_weight) | calculations["bullet_weight"] = kg_to_g(bullet_weight) | ||
calculations["bullet_weightingr"] = kg_to_grains(bullet_weight) | calculations["bullet_weightingr"] = kg_to_grains(bullet_weight) | ||
calculations["weightinlbs"] = kg_to_lbs(weight) | calculations["weightinlbs"] = kg_to_lbs(weight) | ||
calculations["extrabulletchamber"] = variables.get("extrabulletchamber", "0") | calculations["extrabulletchamber"] = variables.get("extrabulletchamber", "0") | ||
calculations["filename"] = file_base_name | calculations["filename"] = file_base_name | ||
printname = variables.get("printname", "").lstrip("#") | |||
printname = variables.get("printname", "").lstrip("#") | |||
printname_english = get_printname_english(printname) | printname_english = get_printname_english(printname) | ||
calculations["printnameenglish"] = printname_english if printname_english else printname | |||
origin = variables.get("origin", "") | origin = variables.get("origin", "") | ||
calculations["origin"] = get_origin(origin) | calculations["origin"] = get_origin(origin) | ||
# Caliber / primary_ammo via ammo_id_display lookup | |||
ammo_id_display = variables.get("ammo_id_display", "").strip() | |||
ammo_display_name = None | |||
if ammo_id_display: | |||
ammo_display_name = lookup_english_token(ammo_id_display) | |||
if ammo_display_name: | |||
calculations["primary_ammo"] = ammo_display_name | |||
calculations["caliber"] = ammo_display_name | |||
else: | |||
calculations["primary_ammo"] = variables.get("primary_ammo", "N/A") | |||
calculations["caliber"] = variables.get("primary_ammo", "N/A") | |||
# Presence in various files | |||
in_main = file_base_name in loadout_main | |||
in_zombie = file_base_name in loadout_zombie | |||
in_special = file_base_name in loadout_special | |||
in_gamemodes = file_contains_weapon(paths["gamemodes_path"], file_base_name) | |||
# Faction logic: prefer main; else zombie; else special | |||
factions_set = set() | |||
classes_list = [] | |||
if in_main: | |||
factions_set = set(loadout_main[file_base_name]["factions"]) | |||
classes_list = list(loadout_main[file_base_name]["classes"]) | |||
elif in_zombie: | |||
factions_set = set(loadout_zombie[file_base_name]["factions"]) | |||
classes_list = [] # replace class block for non-main | |||
elif in_special: | |||
factions_set = set(loadout_special[file_base_name]["factions"]) | |||
classes_list = [] # replace class block for non-main | |||
# faction string for placeholder | |||
if len(factions_set) == 1: | |||
calculations["faction"] = next(iter(factions_set)) | |||
elif len(factions_set) > 1: | |||
calculations["faction"] = "/".join(sorted(factions_set)) | |||
else: | |||
calculations["faction"] = "N/A" | |||
calculations["factions_set"] = factions_set | |||
calculations["classes_list"] = classes_list | |||
calculations["in_main_loadout"] = in_main | |||
calculations["in_zombie_loadout"] = in_zombie | |||
calculations["in_special_loadout"] = in_special | |||
calculations["in_gamemodes"] = in_gamemodes | |||
# Build category lines if not in main loadout | |||
if not in_main: | |||
category_lines = [] | |||
if in_gamemodes: | |||
category_lines.append('[[Gun Game]]<br>') | |||
if in_zombie: | |||
category_lines.append('[[Zombies]]<br>') | |||
if in_special: | |||
category_lines.append('[[Special Loadout]]<br>') | |||
calculations["category_lines"] = "".join(category_lines) | |||
else: | |||
calculations["category_lines"] = "" | |||
return calculations | return calculations | ||
# | # Case-sensitive mapping for image filenames by class, and display labels | ||
CLASS_IMAGE_MAP = { | |||
"assault": "Class_Assault.png", | |||
"medic": "Class_medic.png", | |||
"gunner": "Class_Gunner.png", | |||
"sniper": "Class_sniper.png", | |||
"engineer": "Class_Engineer.png", | |||
"radioman": "Class_radioman.png", | |||
} | |||
# Enforced output order | |||
CLASS_ORDER = ["assault", "medic", "gunner", "sniper", "engineer", "radioman"] | |||
def build_classes_markup(classes_list): | |||
if not classes_list: | |||
return '[[File:Class_unknown.png|50px]] <b>[[unknown]]</b><br>' | |||
parts = [] | |||
present = set(classes_list) | |||
for cls in CLASS_ORDER: | |||
if cls in present: | |||
img = CLASS_IMAGE_MAP.get(cls, f"Class_{cls}.png") | |||
label = cls.capitalize() | |||
parts.append(f'[[File:{img}|50px]] <b>[[{label}]]</b><br>') | |||
return "".join(parts) | |||
def replace_placeholders(template, variables, calculations): | def replace_placeholders(template, variables, calculations): | ||
result = template | result = template | ||
# | classes_pattern = r'\[\[File:Class_""class""\.png\|50px\]\] <b>\[\[""class""\]\]</b><br>' | ||
# If not in main loadout and we have categories, replace with category lines; | |||
# otherwise, render the ordered classes markup. | |||
if (not calculations.get("in_main_loadout", False)) and calculations.get("category_lines"): | |||
result = re.sub(classes_pattern, calculations["category_lines"], result) | |||
else: | |||
result = re.sub(classes_pattern, build_classes_markup(calculations.get("classes_list", [])), result) | |||
for placeholder in re.findall(r'""([^"]+)""', result): | for placeholder in re.findall(r'""([^"]+)""', result): | ||
key_lower = placeholder.lower() | |||
if | if key_lower == "class": | ||
continue | |||
if key_lower == "filename": | |||
value = calculations["filename"] | value = calculations["filename"] | ||
elif | elif key_lower == "bullet_weightingr": | ||
value = str(calculations["bullet_weightingr"]) | value = str(calculations["bullet_weightingr"]) | ||
elif | elif key_lower == "extrabulletchamber": | ||
extra_bullet_chamber = calculations.get("extrabulletchamber", "0") | extra_bullet_chamber = calculations.get("extrabulletchamber", "0") | ||
if extra_bullet_chamber == "0" | value = "" if extra_bullet_chamber == "0" else "[[]]" | ||
else: | else: | ||
value = calculations.get( | value = calculations.get(key_lower, variables.get(key_lower, "N/A")) | ||
result = result.replace(f'""{placeholder}""', str(value)) | result = result.replace(f'""{placeholder}""', str(value)) | ||
# Handle ammo column | # Handle ammo column +/- marker | ||
result = re.sub(r"(\d+)/(\d+)(\[\[\]\])?", lambda m: m.group(1) + ( | result = re.sub( | ||
r"(\d+)/(\d+)(\[\[\]\])?", | |||
lambda m: m.group(1) | |||
+ "/" + m.group(2), result) | + ( | ||
f"[[+{calculations['extrabulletchamber']}]]" | |||
if calculations["extrabulletchamber"] == "2" | |||
else f"[[+1]]" if calculations["extrabulletchamber"] == "1" else "" | |||
) | |||
+ "/" | |||
+ m.group(2), | |||
result, | |||
) | |||
# Remove faction flags that don't apply | |||
factions_set = calculations.get("factions_set", set()) | |||
if "US" not in factions_set: | |||
result = result.replace('[[File:Flag_us_new.png|50px]]', '') | |||
if "VC" not in factions_set: | |||
result = result.replace('[[File:Flag_vc_new.png|50px]]', '') | |||
return result | return result | ||
def process_files(template, scripts_path, output_path, loadout_main, loadout_zombie, loadout_special, paths): | |||
def process_files(template, scripts_path, output_path): | |||
for root, _, files in os.walk(scripts_path): | for root, _, files in os.walk(scripts_path): | ||
for file in files: | for file in files: | ||
if not file.endswith(".txt"): | if not (file.endswith(".txt") and file.startswith("weapon_")): | ||
continue | continue | ||
| Line 166: | Line 355: | ||
file_name = os.path.basename(file) | file_name = os.path.basename(file) | ||
variables = parse_file(input_file) | variables = parse_file(input_file) | ||
calculations = preprocess_calculations(variables, file_name, root) | calculations = preprocess_calculations(variables, file_name, root, loadout_main, loadout_zombie, loadout_special, paths) | ||
processed_template = replace_placeholders(template, variables, calculations) | processed_template = replace_placeholders(template, variables, calculations) | ||
output_file = os.path.join(output_path, file_name) | output_file = os.path.join(output_path, file_name) | ||
with open(output_file, "w", encoding="utf-8") as f: | with open(output_file, "w", encoding="utf-8") as f: | ||
f.write(processed_template) | f.write(processed_template) | ||
# Paths | # Paths (edit as needed) | ||
template_path = r"C:\ | template_path = r"C:\MCV_PROJECTS\wiki\WikiTemplate.txt" | ||
scripts_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\scripts" | scripts_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\scripts" | ||
output_path = r"C:\ | output_path = r"C:\MCV_PROJECTS\wiki\output" | ||
loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout.txt" | |||
zombie_loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout_zombie.txt" | |||
special_loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout_special.txt" | |||
gamemodes_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\gamemodes.txt" | |||
os.makedirs(output_path, exist_ok=True) | os.makedirs(output_path, exist_ok=True) | ||
template = load_template(template_path) | template = load_template(template_path) | ||
process_files(template, scripts_path, output_path) | loadout_main = parse_vietnam_loadout(loadout_path) | ||
loadout_zombie = parse_vietnam_loadout(zombie_loadout_path) | |||
loadout_special = parse_vietnam_loadout(special_loadout_path) | |||
paths = { | |||
"special_loadout_path": special_loadout_path, | |||
"gamemodes_path": gamemodes_path, | |||
} | |||
process_files(template, scripts_path, output_path, loadout_main, loadout_zombie, loadout_special, paths) | |||
def remove_hash_from_files(directory): | def remove_hash_from_files(directory): | ||
if not os.path.exists(directory): | if not os.path.exists(directory): | ||
print(f"The directory '{directory}' does not exist.") | print(f"The directory '{directory}' does not exist.") | ||
return | return | ||
for filename in os.listdir(directory): | for filename in os.listdir(directory): | ||
file_path = os.path.join(directory, filename) | file_path = os.path.join(directory, filename) | ||
if os.path.isfile(file_path) and filename.endswith('.txt'): | if os.path.isfile(file_path) and filename.endswith('.txt'): | ||
try: | try: | ||
with open(file_path, 'r', encoding='utf-8') as file: | with open(file_path, 'r', encoding='utf-8') as file: | ||
content = file.read() | content = file.read() | ||
updated_content = content.replace('#', '') | updated_content = content.replace('#', '') | ||
with open(file_path, 'w', encoding='utf-8') as file: | with open(file_path, 'w', encoding='utf-8') as file: | ||
file.write(updated_content) | file.write(updated_content) | ||
print(f"Processed file: {filename}") | print(f"Processed file: {filename}") | ||
except Exception as e: | except Exception as e: | ||
print(f"Error processing file {filename}: {e}") | print(f"Error processing file {filename}: {e}") | ||
directory = output_path | |||
directory = | |||
remove_hash_from_files(directory) | remove_hash_from_files(directory) | ||
print("Processing complete.") | print("Processing complete.") | ||
Latest revision as of 00:50, 3 September 2025
import os import re
- Utility functions for conversions
def kg_to_g(kg):
return round(kg * 1000, 2)
def kg_to_grains(kg):
return round(kg * 15432.36, 2)
def kg_to_lbs(kg):
return round(kg * 2.20462, 2)
- Load the template file
def load_template(template_path):
with open(template_path, "r", encoding="utf-8") as f:
return f.read()
- Parse key-value pairs from script file (Source-style weapon script)
def parse_file(file_path):
variables = {}
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
match = re.match(r'\s*"(.+?)"\s+"(.+?)"', line)
if match:
key, value = match.groups()
variables[key.lower()] = value
return variables
- Generic lookup from vietnam_english.txt, stripping any leading '#'
def lookup_english_token(token_key):
file_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_english.txt"
key = token_key.lstrip('#')
encodings = ["utf-8", "utf-16", "latin-1"]
for enc in encodings:
try:
with open(file_path, "r", encoding=enc) as f:
for line in f:
line = line.strip()
m = re.match(r'^\s*"(.+?)"\s+"(.+?)"\s*$', line)
if m:
k, v = m.groups()
if k.lower() == key.lower():
return v
except UnicodeDecodeError:
continue
return None
- Retrieve the printnameenglish value (kept for existing behavior)
def get_printname_english(printname):
return lookup_english_token(printname)
- Get the origin value and process it
def get_origin(origin):
origin = origin.lstrip('#').replace("_", " ").title()
return origin
- -------------------------
- Loadout parsing utilities
- -------------------------
CLASSES = {"assault", "medic", "gunner", "sniper", "engineer", "radioman"} FACTIONS = {"US", "VC"}
def parse_vietnam_loadout(loadout_path):
"""
Parse a KeyValues-style loadout and build a mapping:
weapon_name -> {"factions": set(...), "classes": set(...)}
We only consider weapons listed under the "weapons" tree.
"""
mapping = {}
if not os.path.exists(loadout_path):
return mapping
pending_key = None stack = [] cur_faction = None cur_class = None in_weapons_block = False
def push(key):
nonlocal cur_faction, cur_class, in_weapons_block
stack.append(key)
if key == "weapons":
in_weapons_block = True
if key in FACTIONS:
cur_faction = key
if key in CLASSES:
cur_class = key
def pop():
nonlocal cur_faction, cur_class, in_weapons_block
if not stack:
return
key = stack.pop()
if key in FACTIONS and cur_faction == key:
cur_faction = None
if key in CLASSES and cur_class == key:
cur_class = None
if key == "weapons" and "weapons" not in stack:
in_weapons_block = False
with open(loadout_path, "r", encoding="utf-8", errors="ignore") as f:
for raw in f:
line = raw.strip()
# Strip comments
if "//" in line:
line = line[: line.index("//")].rstrip()
if not line:
continue
m_key_only = re.fullmatch(r'"([^"]+)"', line)
if m_key_only:
pending_key = m_key_only.group(1)
continue
if line == "{":
if pending_key is not None:
push(pending_key)
pending_key = None
else:
push("__anon__")
continue
if line == "}":
pop()
continue
m_pair = re.fullmatch(r'"([^"]+)"\s+"([^"]*)"', line)
if m_pair and in_weapons_block and cur_faction and cur_class:
key = m_pair.group(1)
if key.startswith("weapon_"):
w = mapping.setdefault(key, {"factions": set(), "classes": set()})
w["factions"].add(cur_faction)
w["classes"].add(cur_class)
continue
m_inline_open = re.fullmatch(r'"([^"]+)"\s*\{', line)
if m_inline_open:
push(m_inline_open.group(1))
continue
return mapping
def file_contains_weapon(path, weapon_key):
"""
Simple presence check for a weapon key in a text file.
Uses a word-boundary regex to reduce false positives.
"""
if not os.path.exists(path):
return False
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
pattern = r'\b' + re.escape(weapon_key) + r'\b'
return re.search(pattern, content) is not None
except Exception:
return False
def preprocess_calculations(variables, file_name, folder_path, loadout_main, loadout_zombie, loadout_special, paths):
calculations = {}
damage_generic = float(variables.get("damagegeneric", "0"))
damage_head_multiplier = float(variables.get("damageheadmultiplier", "1"))
damage_chest_multiplier = float(variables.get("damagechestmultiplier", "1"))
damage_stomach_multiplier = float(variables.get("damagestomachmultiplier", "1"))
damage_leg_multiplier = float(variables.get("damagelegmultiplier", "1"))
damage_arm_multiplier = float(variables.get("damagearmmultiplier", "1"))
if damage_generic > 0:
calculations["damagegenericxdamageheadmultiplier"] = round(damage_generic * damage_head_multiplier, 2)
calculations["damagegenericxdamagechestmultiplier"] = round(damage_generic * damage_chest_multiplier, 2)
calculations["damagegenericxdamagestomachmultiplier"] = round(damage_generic * damage_stomach_multiplier, 2)
calculations["damagegenericxdamagelegmultiplier"] = round(damage_generic * damage_leg_multiplier, 2)
calculations["damagegenericxdamagearmmultiplier"] = round(damage_generic * damage_arm_multiplier, 2)
else:
calculations["damagegenericxdamageheadmultiplier"] = "N/A"
calculations["damagegenericxdamagechestmultiplier"] = "N/A"
calculations["damagegenericxdamagestomachmultiplier"] = "N/A"
calculations["damagegenericxdamagelegmultiplier"] = "N/A"
calculations["damagegenericxdamagearmultiplier"] = "N/A"
has_bayonet = variables.get("hasbayonet", "0") == "1"
calculations["hasbayonet"] = "YES" if has_bayonet else "NO"
file_base_name = os.path.splitext(file_name)[0]
rifle_grenade_file = os.path.join(folder_path, f"{file_base_name}_riflegrenade.txt")
has_rifle_grenade = os.path.exists(rifle_grenade_file)
calculations["hasriflegrenade"] = "YES" if has_rifle_grenade else "NO"
bullet_weight = float(variables.get("bullet_weight", "0"))
weight = float(variables.get("weight", "0"))
calculations["bullet_weight"] = kg_to_g(bullet_weight) calculations["bullet_weightingr"] = kg_to_grains(bullet_weight) calculations["weightinlbs"] = kg_to_lbs(weight)
calculations["extrabulletchamber"] = variables.get("extrabulletchamber", "0")
calculations["filename"] = file_base_name
printname = variables.get("printname", "").lstrip("#")
printname_english = get_printname_english(printname)
calculations["printnameenglish"] = printname_english if printname_english else printname
origin = variables.get("origin", "")
calculations["origin"] = get_origin(origin)
# Caliber / primary_ammo via ammo_id_display lookup
ammo_id_display = variables.get("ammo_id_display", "").strip()
ammo_display_name = None
if ammo_id_display:
ammo_display_name = lookup_english_token(ammo_id_display)
if ammo_display_name:
calculations["primary_ammo"] = ammo_display_name
calculations["caliber"] = ammo_display_name
else:
calculations["primary_ammo"] = variables.get("primary_ammo", "N/A")
calculations["caliber"] = variables.get("primary_ammo", "N/A")
# Presence in various files in_main = file_base_name in loadout_main in_zombie = file_base_name in loadout_zombie in_special = file_base_name in loadout_special in_gamemodes = file_contains_weapon(paths["gamemodes_path"], file_base_name)
# Faction logic: prefer main; else zombie; else special
factions_set = set()
classes_list = []
if in_main:
factions_set = set(loadout_main[file_base_name]["factions"])
classes_list = list(loadout_main[file_base_name]["classes"])
elif in_zombie:
factions_set = set(loadout_zombie[file_base_name]["factions"])
classes_list = [] # replace class block for non-main
elif in_special:
factions_set = set(loadout_special[file_base_name]["factions"])
classes_list = [] # replace class block for non-main
# faction string for placeholder
if len(factions_set) == 1:
calculations["faction"] = next(iter(factions_set))
elif len(factions_set) > 1:
calculations["faction"] = "/".join(sorted(factions_set))
else:
calculations["faction"] = "N/A"
calculations["factions_set"] = factions_set calculations["classes_list"] = classes_list calculations["in_main_loadout"] = in_main calculations["in_zombie_loadout"] = in_zombie calculations["in_special_loadout"] = in_special calculations["in_gamemodes"] = in_gamemodes
# Build category lines if not in main loadout
if not in_main:
category_lines = []
if in_gamemodes:
category_lines.append('Gun Game
')
if in_zombie:
category_lines.append('Zombies
')
if in_special:
category_lines.append('Special Loadout
')
calculations["category_lines"] = "".join(category_lines)
else:
calculations["category_lines"] = ""
return calculations
- Case-sensitive mapping for image filenames by class, and display labels
CLASS_IMAGE_MAP = {
"assault": "Class_Assault.png", "medic": "Class_medic.png", "gunner": "Class_Gunner.png", "sniper": "Class_sniper.png", "engineer": "Class_Engineer.png", "radioman": "Class_radioman.png",
}
- Enforced output order
CLASS_ORDER = ["assault", "medic", "gunner", "sniper", "engineer", "radioman"]
def build_classes_markup(classes_list):
if not classes_list:
return 'File:Class unknown.png unknown
'
parts = []
present = set(classes_list)
for cls in CLASS_ORDER:
if cls in present:
img = CLASS_IMAGE_MAP.get(cls, f"Class_{cls}.png")
label = cls.capitalize()
parts.append(f'[[File:{img}|50px]] [[{label}]]
')
return "".join(parts)
def replace_placeholders(template, variables, calculations):
result = template
classes_pattern = r'\[\[File:Class_""class""\.png\|50px\]\] \[\[""class""\]\]
'
# If not in main loadout and we have categories, replace with category lines;
# otherwise, render the ordered classes markup.
if (not calculations.get("in_main_loadout", False)) and calculations.get("category_lines"):
result = re.sub(classes_pattern, calculations["category_lines"], result)
else:
result = re.sub(classes_pattern, build_classes_markup(calculations.get("classes_list", [])), result)
for placeholder in re.findall(r'""([^"]+)""', result):
key_lower = placeholder.lower()
if key_lower == "class":
continue
if key_lower == "filename":
value = calculations["filename"]
elif key_lower == "bullet_weightingr":
value = str(calculations["bullet_weightingr"])
elif key_lower == "extrabulletchamber":
extra_bullet_chamber = calculations.get("extrabulletchamber", "0")
value = "" if extra_bullet_chamber == "0" else "[[]]"
else:
value = calculations.get(key_lower, variables.get(key_lower, "N/A"))
result = result.replace(f'""{placeholder}""', str(value))
# Handle ammo column +/- marker
result = re.sub(
r"(\d+)/(\d+)(\[\[\]\])?",
lambda m: m.group(1)
+ (
f"[[+{calculations['extrabulletchamber']}]]"
if calculations["extrabulletchamber"] == "2"
else f"+1" if calculations["extrabulletchamber"] == "1" else ""
)
+ "/"
+ m.group(2),
result,
)
# Remove faction flags that don't apply
factions_set = calculations.get("factions_set", set())
if "US" not in factions_set:
result = result.replace('
', )
if "VC" not in factions_set:
result = result.replace('
', )
return result
def process_files(template, scripts_path, output_path, loadout_main, loadout_zombie, loadout_special, paths):
for root, _, files in os.walk(scripts_path):
for file in files:
if not (file.endswith(".txt") and file.startswith("weapon_")):
continue
input_file = os.path.join(root, file)
file_name = os.path.basename(file)
variables = parse_file(input_file)
calculations = preprocess_calculations(variables, file_name, root, loadout_main, loadout_zombie, loadout_special, paths)
processed_template = replace_placeholders(template, variables, calculations)
output_file = os.path.join(output_path, file_name)
with open(output_file, "w", encoding="utf-8") as f:
f.write(processed_template)
- Paths (edit as needed)
template_path = r"C:\MCV_PROJECTS\wiki\WikiTemplate.txt" scripts_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\scripts" output_path = r"C:\MCV_PROJECTS\wiki\output" loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout.txt" zombie_loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout_zombie.txt" special_loadout_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\resource\vietnam_loadout_special.txt" gamemodes_path = r"C:\Program Files (x86)\Steam\steamapps\common\Military Conflict - Vietnam\vietnam\gamemodes.txt"
os.makedirs(output_path, exist_ok=True)
template = load_template(template_path) loadout_main = parse_vietnam_loadout(loadout_path) loadout_zombie = parse_vietnam_loadout(zombie_loadout_path) loadout_special = parse_vietnam_loadout(special_loadout_path)
paths = {
"special_loadout_path": special_loadout_path, "gamemodes_path": gamemodes_path,
}
process_files(template, scripts_path, output_path, loadout_main, loadout_zombie, loadout_special, paths)
def remove_hash_from_files(directory):
if not os.path.exists(directory):
print(f"The directory '{directory}' does not exist.")
return
for filename in os.listdir(directory):
file_path = os.path.join(directory, filename)
if os.path.isfile(file_path) and filename.endswith('.txt'):
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
updated_content = content.replace('#', )
with open(file_path, 'w', encoding='utf-8') as file:
file.write(updated_content)
print(f"Processed file: {filename}")
except Exception as e:
print(f"Error processing file {filename}: {e}")
directory = output_path remove_hash_from_files(directory) print("Processing complete.")