371 lines
17 KiB
Python
371 lines
17 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import math
|
|
from typing import Any
|
|
|
|
import lvpwb as app
|
|
|
|
def show_roster(config: dict[str, Any]) -> None:
|
|
names = sorted(config["roster"], key=str.casefold)
|
|
if not names:
|
|
print("The roster is empty.")
|
|
return
|
|
print(f"\nRoster ({len(names)} wrestlers):")
|
|
for position, name in enumerate(names, 1):
|
|
print(f" {position}. {name}")
|
|
|
|
|
|
def edit_custom_roster(config: dict[str, Any]) -> None:
|
|
while True:
|
|
choices = ["Add wrestler", "Remove wrestler", "View roster", "Done"]
|
|
labels = ["Add a wrestler", "Remove a wrestler", "View roster (read-only)",
|
|
f"Done ({len(config['roster'])} listed)"]
|
|
action = app.ask_choice("Edit roster", choices, labels=labels)
|
|
if action == 3:
|
|
break
|
|
if action == 0:
|
|
name = app.ask("Wrestler name").strip()
|
|
if not name:
|
|
print("Wrestler name cannot be blank.")
|
|
elif any(existing.casefold() == name.casefold() for existing in config["roster"]):
|
|
print("That wrestler is already in this roster.")
|
|
else:
|
|
config["roster"].append(name)
|
|
elif action == 2:
|
|
app.show_roster(config)
|
|
elif not config["roster"]:
|
|
print("The roster is empty.")
|
|
else:
|
|
index = app.ask_choice("Remove wrestler", config["roster"])
|
|
removed = config["roster"].pop(index)
|
|
config["women_roster"] = [name for name in config["women_roster"]
|
|
if name.casefold() != removed.casefold()]
|
|
config["light_heavyweight_roster"] = [
|
|
name for name in config.get("light_heavyweight_roster", [])
|
|
if name.casefold() != removed.casefold()
|
|
]
|
|
|
|
|
|
def edit_custom_match_types(config: dict[str, Any]) -> None:
|
|
while True:
|
|
existing = config["match_types"]
|
|
choices = list(range(len(existing))) + ["add", "remove", "done"]
|
|
labels = [f"{item['name']} ({' vs '.join(map(str, item['sides']))}, "
|
|
f"base {item['base_rating']:.1f}%)" for item in existing]
|
|
labels.extend(["Add a match type", "Remove a match type", "Done"])
|
|
action = app.ask_choice("Edit match types", choices, labels=labels)
|
|
if action == len(existing) + 2:
|
|
return
|
|
if action == len(existing) + 1:
|
|
if not existing:
|
|
print("Add at least one match type before removing one.")
|
|
continue
|
|
remove_index = app.ask_choice("Remove which match type", list(range(len(existing))),
|
|
labels=[item["name"] for item in existing])
|
|
existing.pop(remove_index)
|
|
continue
|
|
if action == len(existing):
|
|
name = app.ask("New match type name").strip()
|
|
if not name:
|
|
print("Match type name cannot be blank.")
|
|
continue
|
|
if any(item["name"].casefold() == name.casefold() for item in existing):
|
|
print("That match type already exists.")
|
|
continue
|
|
side_sizes = app.parse_side_sizes(app.ask("Wrestler counts per side, comma-separated (e.g. 1,1)"))
|
|
if side_sizes is None:
|
|
print("Enter at least two positive side sizes, such as 1,1 or 2,2.")
|
|
continue
|
|
if sum(side_sizes) > len(config["roster"]):
|
|
print("This match type needs more wrestlers than the roster contains.")
|
|
continue
|
|
base_default = app.MATCH_BASE_RATINGS.get(name, 4.5)
|
|
while True:
|
|
try:
|
|
base_rating = float(app.ask("Base rating percentage", str(base_default)))
|
|
if math.isfinite(base_rating) and 0 <= base_rating <= 100:
|
|
break
|
|
except ValueError:
|
|
pass
|
|
print("Enter a rating from 0 to 100.")
|
|
default_outcomes = app.MATCH_OUTCOME_OPTIONS.get(name, app.STANDARD_MATCH_OUTCOMES)
|
|
raw_outcomes = app.ask("Legal outcomes, comma-separated", ", ".join(default_outcomes))
|
|
outcomes = [outcome.strip() for outcome in raw_outcomes.split(",") if outcome.strip()]
|
|
if not outcomes:
|
|
print("At least one legal outcome is required.")
|
|
continue
|
|
existing.append({"name": name, "sides": side_sizes,
|
|
"base_rating": base_rating, "outcomes": outcomes})
|
|
|
|
|
|
def edit_custom_titles(config: dict[str, Any]) -> None:
|
|
while True:
|
|
existing = config["championships"]
|
|
choices = list(range(len(existing))) + ["add", "remove", "done"]
|
|
labels = [f"{item['name']} ({item['kind']})" for item in existing]
|
|
labels.extend(["Add a championship", "Remove a championship", "Done"])
|
|
action = app.ask_choice("Edit championship rankings", choices, labels=labels)
|
|
if action == len(existing) + 2:
|
|
return
|
|
if action == len(existing) + 1:
|
|
if not existing:
|
|
print("There are no championships to remove.")
|
|
continue
|
|
remove_index = app.ask_choice("Remove which championship", list(range(len(existing))),
|
|
labels=[item["name"] for item in existing])
|
|
existing.pop(remove_index)
|
|
continue
|
|
if action == len(existing):
|
|
name = app.ask("Championship name").strip()
|
|
if not name or any(item["name"].casefold() == name.casefold() for item in existing):
|
|
print("Enter a non-empty championship name not already listed.")
|
|
continue
|
|
kind_options = ["singles", "tag", "women", "light_heavyweight"]
|
|
kind_labels = ["Singles / open", "Tag team", "Women's division",
|
|
"Light-Heavyweight division"]
|
|
kind = kind_options[app.ask_choice("Championship type", kind_options, labels=kind_labels)]
|
|
existing.append({"name": name, "kind": kind})
|
|
|
|
|
|
def edit_eligible_roster(config: dict[str, Any], field: str, division: str,
|
|
require_eligible: bool = False) -> None:
|
|
eligible = config.setdefault(field, [])
|
|
while True:
|
|
choices = ["add", "remove", "done"]
|
|
labels = ["Add eligible wrestler", "Remove eligible wrestler", "Done"]
|
|
action = app.ask_choice(f"{division} eligibility", choices, labels=labels)
|
|
if action == 2:
|
|
if require_eligible and not eligible:
|
|
print(f"Choose at least one eligible wrestler for the {division} division.")
|
|
continue
|
|
return
|
|
if action == 0:
|
|
available = [name for name in config["roster"]
|
|
if name.casefold() not in {item.casefold() for item in eligible}]
|
|
if not available:
|
|
print("Every roster member is already listed.")
|
|
continue
|
|
selected = app.choose_roster_member(available, "Add which wrestler")
|
|
eligible.append(selected)
|
|
elif not eligible:
|
|
print("No eligible wrestlers are listed.")
|
|
else:
|
|
remove_index = app.ask_choice("Remove eligible wrestler", eligible)
|
|
eligible.pop(remove_index)
|
|
|
|
|
|
def edit_women_roster(config: dict[str, Any]) -> None:
|
|
edit_eligible_roster(config, "women_roster", "Women's division")
|
|
|
|
|
|
def edit_light_heavyweight_roster(config: dict[str, Any]) -> None:
|
|
edit_eligible_roster(
|
|
config, "light_heavyweight_roster", "Light-Heavyweight", require_eligible=True
|
|
)
|
|
|
|
|
|
def edit_custom_shows(config: dict[str, Any]) -> None:
|
|
while True:
|
|
shows = config["shows"]
|
|
labels = [f"{brand}: {shows[brand]}" for brand in app.SHOW_BRANDS] + ["Done"]
|
|
action = app.ask_choice("Edit show names", list(app.SHOW_BRANDS) + ["done"], labels=labels)
|
|
if action == len(app.SHOW_BRANDS):
|
|
return
|
|
brand = app.SHOW_BRANDS[action]
|
|
while True:
|
|
name = app.ask(f"{brand} show name", shows[brand]).strip()
|
|
if not name:
|
|
print("Show name cannot be blank.")
|
|
continue
|
|
others = [shows[other] for other in app.SHOW_BRANDS if other != brand]
|
|
if any(other.casefold() == name.casefold() for other in others):
|
|
print("Show names must be distinct from each other.")
|
|
continue
|
|
shows[brand] = name
|
|
break
|
|
|
|
|
|
def edit_custom_profile(config: dict[str, Any]) -> dict[str, Any]:
|
|
while True:
|
|
options = ["identity", "shows", "roster", "match_types", "titles", "women"]
|
|
labels = ["Game/federation, platform, screenshot directory",
|
|
f"Show names (Raw: {config['shows']['Raw']})",
|
|
f"Edit roster ({len(config['roster'])} wrestlers)",
|
|
f"Edit match types ({len(config['match_types'])})",
|
|
f"Edit championships ({len(config['championships'])})",
|
|
f"Edit Women's eligibility ({len(config['women_roster'])})"]
|
|
has_light_heavyweight = any(
|
|
title.get("kind") == "light_heavyweight" for title in config["championships"]
|
|
)
|
|
if has_light_heavyweight:
|
|
options.append("light_heavyweight")
|
|
labels.append(
|
|
f"Edit Light-Heavyweight eligibility "
|
|
f"({len(config.get('light_heavyweight_roster', []))})"
|
|
)
|
|
options.append("save")
|
|
labels.append("Validate and save")
|
|
action = app.ask_choice("BYOG editor", options, labels=labels)
|
|
if action == 0:
|
|
config["game_title"] = app.ask("Game title", config["game_title"]).strip()
|
|
config["federation_name"] = app.ask("Federation name", config["federation_name"]).strip()
|
|
platform_options = ["ps1", "ps2", "other"]
|
|
platform_labels = ["PlayStation 1", "PlayStation 2", "Other / platform agnostic"]
|
|
config["platform"] = platform_options[
|
|
app.ask_choice("Platform", platform_options, labels=platform_labels)
|
|
]
|
|
current_directory = config.get("screenshot_directory") or "platform default"
|
|
screenshot_directory = app.ask(
|
|
f"Screenshot directory override (absolute, app-relative, or ~; current: "
|
|
f"{current_directory}; blank uses platform default)"
|
|
).strip()
|
|
config["screenshot_directory"] = screenshot_directory or None
|
|
elif action == 1:
|
|
app.edit_custom_shows(config)
|
|
elif action == 2:
|
|
app.edit_custom_roster(config)
|
|
elif action == 3:
|
|
app.edit_custom_match_types(config)
|
|
elif action == 4:
|
|
app.edit_custom_titles(config)
|
|
elif action == 5:
|
|
app.edit_women_roster(config)
|
|
elif has_light_heavyweight and action == 6:
|
|
app.edit_light_heavyweight_roster(config)
|
|
else:
|
|
if has_light_heavyweight and not config.get("light_heavyweight_roster"):
|
|
print("Choose eligible Light-Heavyweight wrestlers before saving this profile.")
|
|
continue
|
|
try:
|
|
return app.save_custom_game(config)
|
|
except ValueError as exc:
|
|
print(f"Cannot save game profile: {exc}")
|
|
|
|
|
|
def builtin_profile_for_copy(game_id: str, custom_id: str) -> dict[str, Any]:
|
|
definitions = [
|
|
{"name": name, "sides": list(sides),
|
|
"base_rating": app.MATCH_BASE_RATINGS.get(name, 4.5),
|
|
"outcomes": list(app.MATCH_OUTCOME_OPTIONS.get(name, app.STANDARD_MATCH_OUTCOMES))}
|
|
for name, sides in app.MATCH_TYPES[game_id]
|
|
]
|
|
return app.validate_custom_game({
|
|
"schema_version": 1,
|
|
"game_id": custom_id,
|
|
"game_title": app.game_title(game_id) + " Custom Edition",
|
|
"federation_name": app.federation_name(game_id),
|
|
"platform": "ps1" if game_id in app.PS1_GAMES else "ps2",
|
|
"roster": app.load_roster(game_id),
|
|
"match_types": definitions,
|
|
"championships": app.title_definitions_for_game(game_id),
|
|
"women_roster": sorted(app.WOMEN_ROSTERS.get(game_id, set())),
|
|
"light_heavyweight_roster": app.load_roster(game_id),
|
|
"shows": {"Raw": "Monday Night Raw", "SmackDown": "Thursday Night SmackDown!",
|
|
"PPV": "WWF PPV"},
|
|
})
|
|
|
|
|
|
def create_custom_game() -> dict[str, Any]:
|
|
title = app.ask("Game title").strip()
|
|
while not title:
|
|
title = app.ask("Game title cannot be blank; enter a game title").strip()
|
|
federation = app.ask("Federation name", "My Wrestling Federation").strip()
|
|
if not federation:
|
|
federation = "My Wrestling Federation"
|
|
platform_options = ["ps1", "ps2", "other"]
|
|
platform_labels = ["PlayStation 1", "PlayStation 2", "Other / platform agnostic"]
|
|
platform = platform_options[app.ask_choice("Platform", platform_options, labels=platform_labels)]
|
|
shows: dict[str, str] = {}
|
|
print("\nName this federation's three shows (weekly #1, weekly #2, and the big event).")
|
|
for brand in app.SHOW_BRANDS:
|
|
default = app.DEFAULT_CUSTOM_SHOWS[brand]
|
|
while True:
|
|
name = app.ask(f"{brand} show name", default).strip() or default
|
|
if any(other.casefold() == name.casefold() for other in shows.values()):
|
|
print("Show names must be distinct from each other.")
|
|
continue
|
|
shows[brand] = name
|
|
break
|
|
config: dict[str, Any] = {
|
|
"schema_version": 1,
|
|
"game_id": app.new_custom_game_id(federation, title),
|
|
"game_title": title,
|
|
"federation_name": federation,
|
|
"platform": platform,
|
|
"roster": [],
|
|
"match_types": [],
|
|
"championships": [],
|
|
"women_roster": [],
|
|
"light_heavyweight_roster": [],
|
|
"shows": shows,
|
|
}
|
|
app.edit_custom_roster(config)
|
|
config["match_types"] = app.default_custom_match_types(len(config["roster"]))
|
|
if not config["match_types"]:
|
|
config["match_types"] = [{"name": "Singles", "sides": [1, 1],
|
|
"base_rating": 4.5,
|
|
"outcomes": list(app.STANDARD_MATCH_OUTCOMES)}]
|
|
print("\nAdd this federation's championship divisions (or finish with none).")
|
|
app.edit_custom_titles(config)
|
|
if any(title["kind"] == "women" for title in config["championships"]):
|
|
app.edit_women_roster(config)
|
|
if any(title["kind"] == "light_heavyweight" for title in config["championships"]):
|
|
app.edit_light_heavyweight_roster(config)
|
|
return app.edit_custom_profile(config)
|
|
|
|
|
|
def choose_builtin_game() -> str:
|
|
ids = list(app.BUILTIN_GAME_IDS)
|
|
return ids[app.ask_choice("Built-in game to copy", ids,
|
|
labels=[app.GAMES[game_id]["title"] for game_id in ids])]
|
|
|
|
|
|
def clone_builtin_game() -> dict[str, Any]:
|
|
app.refresh_custom_games()
|
|
source_id = app.choose_builtin_game()
|
|
title = app.ask("Custom game title", app.game_title(source_id) + " Custom Edition").strip()
|
|
federation = app.ask("Federation name", app.federation_name(source_id)).strip()
|
|
config = app.builtin_profile_for_copy(source_id, app.new_custom_game_id(federation, title))
|
|
config["game_title"] = title or config["game_title"]
|
|
config["federation_name"] = federation or config["federation_name"]
|
|
if any(item["kind"] == "light_heavyweight" for item in config["championships"]):
|
|
config["light_heavyweight_roster"] = []
|
|
app.edit_light_heavyweight_roster(config)
|
|
print(f"Copied {app.game_title(source_id)} into the editable BYOG directory.")
|
|
return app.edit_custom_profile(config)
|
|
|
|
|
|
def edit_existing_custom_game() -> None:
|
|
app.refresh_custom_games()
|
|
game_ids = sorted(app.CUSTOM_GAME_CONFIGS)
|
|
if not game_ids:
|
|
print("No custom games exist yet. Create or copy one first.")
|
|
return
|
|
labels = [app.game_display_name(game_id) for game_id in game_ids]
|
|
game_id = game_ids[app.ask_choice("Choose custom game to edit", game_ids, labels=labels)]
|
|
editable = json.loads(json.dumps(app.CUSTOM_GAME_CONFIGS[game_id]))
|
|
app.edit_custom_profile(editable)
|
|
app.refresh_custom_games()
|
|
|
|
|
|
def manage_games() -> None:
|
|
while True:
|
|
app.refresh_custom_games()
|
|
action = app.ask_choice(
|
|
"BYOG / Manage Games",
|
|
["create", "copy", "edit", "back"],
|
|
labels=["Create a new game and federation", "Copy a built-in WWF game to edit",
|
|
f"Edit a custom game ({len(app.CUSTOM_GAME_CONFIGS)} available)",
|
|
"Back to main menu"],
|
|
)
|
|
if action == 0:
|
|
app.create_custom_game()
|
|
elif action == 1:
|
|
app.clone_builtin_game()
|
|
elif action == 2:
|
|
app.edit_existing_custom_game()
|
|
else:
|
|
return
|
|
|
|
__all__ = ['builtin_profile_for_copy', 'choose_builtin_game', 'clone_builtin_game', 'create_custom_game', 'edit_custom_match_types', 'edit_custom_profile', 'edit_custom_roster', 'edit_custom_shows', 'edit_custom_titles', 'edit_eligible_roster', 'edit_existing_custom_game', 'edit_light_heavyweight_roster', 'edit_women_roster', 'manage_games', 'show_roster']
|