WeaponScriptToWiki.py

Revision as of 00:50, 3 September 2025 by Skizmophonic (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

import os import re

  1. 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)
  1. Load the template file

def load_template(template_path):

   with open(template_path, "r", encoding="utf-8") as f:
       return f.read()
  1. 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
  1. 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
  1. Retrieve the printnameenglish value (kept for existing behavior)

def get_printname_english(printname):

   return lookup_english_token(printname)
  1. Get the origin value and process it

def get_origin(origin):

   origin = origin.lstrip('#').replace("_", " ").title()
   return origin
  1. -------------------------
  2. Loadout parsing utilities
  3. -------------------------

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
  1. 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",

}

  1. 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)
  1. 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.")