Vollständiger, engineunabhängiger Regelkern (Luneraria.Core, ohne UnityEngine): Partie mit sechs Plätzen und zwei Teams, private Planung mit atomarer Übernahme, Gold, Käufe, Aufwertungen, Formation, drei Lanes mit zwei Laneturmen und zwei Basistürmen je Team, Ancients, unbegrenzte Wellen, Helden mit Stufen, Items und Talenten, Angriffschampions, Bossgrotte mit Anspruch, Entsendung, Kopfgeld und dreirundiger Sperre, Bots und Siegbedingung (nur Ancient-Fall). Serverautoritative Befehlsschnittstelle. - Deterministisch: fester Takt, geordnete Maps, fortlaufende IDs, kein Zufall, keine Uhr, keine Threads - Tabellen einmal aus Godot erzeugt (tools/port/gen_content.py) - Golden-Referenzen aus Godot (tools/godot-golden): 19 Szenarien, 120 Snapshots, 51.221 Werte, Abweichung 0 (Toleranzen dokumentiert) - Tests: content 75, core 91, m2 73, golden 157, determinism 6 = 402/402 - Leistung (M5): Simulationsschritt 0,259 ms statt 1,587 ms in GDScript - Referenzfehler R1 im Bot-Gedächtnis bewusst übernommen und dokumentiert - Geplante Helden (Lucy, Ghaliga, Siegfried, Feena) im Datenmodell, inaktiv - Doku unter docs/port (U1-RULES, PORT-STATUS, PORT-QA, U2 bis U5) Unity-Editor-Lauf offen: Die Unity-Lizenz ist noch nicht aktiviert; gebaut und getestet mit dem .NET 8 SDK auf Unitys Sprachstand (C# 9, .NET Standard 2.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
3.8 KiB
GDScript
63 lines
3.8 KiB
GDScript
extends Node
|
|
## Exports every rule table of the Godot reference as JSON (insertion order kept, doubles at
|
|
## full precision): DataCatalog units, enemies and sends, LuneContent, LuneBot profiles, the
|
|
## default LuneBalance and the lane geometry. tools/port/gen_content.py turns it into
|
|
## Assets/Luneraria/Core/Content/ContentData.cs once; the C# file is maintained from then on.
|
|
## Run: sh tools/godot-golden/run_godot.sh content_export --out=/tmp/lune_content.json
|
|
|
|
func _ready() -> void:
|
|
var out: String = "/tmp/lune_content.json"
|
|
for arg: String in OS.get_cmdline_user_args():
|
|
if arg.begins_with("--out="):
|
|
out = arg.substr(6)
|
|
var balance := LuneBalance.default_balance()
|
|
var balance_values: Dictionary = {}
|
|
for property: Dictionary in balance.get_property_list():
|
|
if property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE:
|
|
var value: Variant = balance.get(property.name)
|
|
balance_values[property.name] = Array(value) if value is PackedStringArray else value
|
|
var map := LuneMap.new(balance)
|
|
var lanes: Dictionary = {}
|
|
for lane: String in LuneMap.LANES:
|
|
var path: LuneMap.LanePath = map.lanes[lane]
|
|
var points: Array = []
|
|
for p: Vector2 in path.points:
|
|
points.append([p.x, p.y])
|
|
lanes[lane] = {"length": path.length, "points": points, "cumulative": Array(path.cumulative)}
|
|
var buildings: Array = []
|
|
for definition: Dictionary in map.buildings:
|
|
var copy: Dictionary = definition.duplicate()
|
|
copy.position = [definition.position.x, definition.position.y]
|
|
buildings.append(copy)
|
|
var data: Dictionary = {
|
|
"engine": Engine.get_version_info().string,
|
|
"units": _plain(DataCatalog.UNITS), "enemies": _plain(DataCatalog.ENEMIES), "sends": _plain(DataCatalog.SENDS),
|
|
"faction_names": DataCatalog.FACTION_NAMES, "armor_names": DataCatalog.ARMOR_NAMES, "damage_names": DataCatalog.DAMAGE_NAMES,
|
|
"heroes": LuneContent.HEROES, "level_xp": LuneContent.LEVEL_XP, "talent_levels": LuneContent.TALENT_LEVELS,
|
|
"talents": LuneContent.TALENTS, "items": LuneContent.ITEMS, "champions": LuneContent.CHAMPIONS,
|
|
"content_constants": {"HP_PER_LEVEL": LuneContent.HP_PER_LEVEL, "DAMAGE_PER_LEVEL": LuneContent.DAMAGE_PER_LEVEL,
|
|
"XP_PARTICIPATION": LuneContent.XP_PARTICIPATION, "XP_PER_ROUND": LuneContent.XP_PER_ROUND, "XP_RADIUS": LuneContent.XP_RADIUS,
|
|
"ITEM_SLOTS": LuneContent.ITEM_SLOTS, "ITEM_REFUND": LuneContent.ITEM_REFUND, "CHAMPION_HP": LuneContent.CHAMPION_HP,
|
|
"CHAMPION_DAMAGE": LuneContent.CHAMPION_DAMAGE, "CHAMPION_BOUNTY": LuneContent.CHAMPION_BOUNTY,
|
|
"CHAMPION_BOUNTY_PER_ROUND": LuneContent.CHAMPION_BOUNTY_PER_ROUND, "MAX_BOOKINGS": LuneContent.MAX_BOOKINGS,
|
|
"BOSS_MODEL": LuneContent.BOSS_MODEL, "BOSS_GROTTO_HP": LuneContent.BOSS_GROTTO_HP, "BOSS_GROTTO_DAMAGE": LuneContent.BOSS_GROTTO_DAMAGE,
|
|
"BOSS_DEPLOY_HP": LuneContent.BOSS_DEPLOY_HP, "BOSS_DEPLOY_DAMAGE": LuneContent.BOSS_DEPLOY_DAMAGE, "BOSS_BOUNTY": LuneContent.BOSS_BOUNTY,
|
|
"BOSS_BOUNTY_PER_ROUND": LuneContent.BOSS_BOUNTY_PER_ROUND, "BOSS_LOCK_ROUNDS": LuneContent.BOSS_LOCK_ROUNDS,
|
|
"BOSS_XP": LuneContent.BOSS_XP, "BUILDING_XP": LuneContent.BUILDING_XP},
|
|
"match_roles": LuneMatch.ROLES, "hero_roles": LuneMatch.HERO_ROLES, "row_order": LuneMatch.ROW_ORDER, "bot_names": LuneMatch.BOT_NAMES,
|
|
"bot_profiles": LuneBot.PROFILES, "bot_constants": {"BOSS_BAR": LuneBot.BOSS_BAR, "BOSS_BAR_STEP": LuneBot.BOSS_BAR_STEP},
|
|
"waves_melee": LuneWaves.MELEE, "balance": balance_values, "lanes": lanes, "buildings": buildings}
|
|
var file := FileAccess.open(out, FileAccess.WRITE)
|
|
file.store_string(JSON.stringify(data, "\t", false, true))
|
|
file.close()
|
|
print("CONTENT_EXPORT ", out)
|
|
get_tree().quit()
|
|
|
|
## Colours are presentation data; everything else is copied as is.
|
|
func _plain(table: Dictionary) -> Dictionary:
|
|
var result: Dictionary = {}
|
|
for key: String in table:
|
|
var entry: Dictionary = (table[key] as Dictionary).duplicate(true)
|
|
entry.erase("color")
|
|
result[key] = entry
|
|
return result
|