class_name Data extends RefCounted ## Statische Spieldaten: Elemente, Rollen, Basiswerte, Traits, Preise. enum Element { FEUER, WASSER, WIND, BLITZ, ERDE, EIS } enum Role { TANK, DPS, HEILER } const ELEMENT_COUNT := 6 const ELEMENT_NAMES := ["Feuer", "Wasser", "Wind", "Blitz", "Erde", "Eis"] const ELEMENT_COLORS := [ Color(0.92, 0.31, 0.16), Color(0.18, 0.52, 0.90), Color(0.48, 0.85, 0.60), Color(0.96, 0.84, 0.22), Color(0.62, 0.45, 0.24), Color(0.66, 0.92, 0.98), ] const NONE_COLOR := Color(0.62, 0.62, 0.66) const ROLE_NAMES := ["Tank", "DPS", "Heiler"] const ROLE_SHORT := ["T", "D", "H"] # Basiswerte auf 1-Star. const ROLE_STATS := { Role.TANK: {"hp": 700.0, "atk": 45.0, "armor": 0.40, "aspd": 0.60, "range": 1, "mana": 80.0}, Role.DPS: {"hp": 450.0, "atk": 65.0, "armor": 0.18, "aspd": 0.80, "range": 3, "mana": 100.0}, Role.HEILER: {"hp": 520.0, "atk": 38.0, "armor": 0.22, "aspd": 0.70, "range": 3, "mana": 90.0}, } # Das Hauptelement prägt die Werte: Feuer schlägt hart, Erde hält aus, Wind ist # schnell, Blitz trifft kritisch, Wasser zaubert öfter, Eis ist zäh und langsam. const ELEMENT_PROFILE := { Element.FEUER: {"hp": 0.90, "atk": 1.18, "armor": 0.0, "aspd": 1.0, "mana": 1.0, "crit": 0.0}, Element.WASSER: {"hp": 1.0, "atk": 0.95, "armor": 0.0, "aspd": 1.0, "mana": 0.75, "crit": 0.0}, Element.WIND: {"hp": 0.92, "atk": 0.95, "armor": 0.0, "aspd": 1.20, "mana": 1.0, "crit": 0.0}, Element.BLITZ: {"hp": 0.95, "atk": 1.0, "armor": 0.0, "aspd": 1.05, "mana": 1.0, "crit": 0.15}, Element.ERDE: {"hp": 1.22, "atk": 0.92, "armor": 0.08, "aspd": 0.90, "mana": 1.0, "crit": 0.0}, Element.EIS: {"hp": 1.08, "atk": 0.95, "armor": 0.05, "aspd": 0.95, "mana": 0.9, "crit": 0.0}, } const PROFILE_HINT := { Element.FEUER: "hart, aber weich", Element.WASSER: "zaubert oft", Element.WIND: "schnell", Element.BLITZ: "kritisch", Element.ERDE: "zäh und langsam", Element.EIS: "robust", } # Multiplikator pro Sternstufe (Index = star - 1). const STAR_MULT := [1.0, 1.8, 3.24] const MAX_STAR := 3 # Jedes zusätzliche Element gibt einen kleinen Grundwert-Bonus. const ELEMENT_STAT_BONUS := 0.06 # --- Oekonomie ------------------------------------------------------------- const START_HP := 100 const BASE_INCOME := 5 const WIN_GOLD := 1 const REROLL_COST := 2 const PRICE_NONE := 1 const PRICE_ELEMENT := 2 ## Zinsen: je 10 Gold ein Gold dazu, gedeckelt bei 50 Gold. const INTEREST_STEP := 10 const INTEREST_MAX := 5 const SHOP_SLOTS := 5 ## Je aufgestelltem Element wird ein Shop-Platz zu 25% seltener neutral. ## Multiplikativ, damit Neutral nie ganz verschwindet. const NEUTRAL_FALLOFF := 0.75 const BENCH_SLOTS := 9 const START_GOLD := 4 # --- Geteilter Pool -------------------------------------------------------- ## Alle 8 Spieler teilen sich diesen Vorrat pro Element. Ein 3-Star bindet ## allein 9 Stück, dazu kommt was auf Bänken und Brettern liegt. ## Bei 100 hält der Vorrat eine ganze Partie, geht im Endspiel aber auf rund ein Fünftel. const POOL_PER_ELEMENT := 100 # --- Board ----------------------------------------------------------------- const BOARD_COLS := 7 const BOARD_ROWS := 4 # --- Level und Erfahrung --------------------------------------------------- ## Das Spielerlevel ist die Anzahl der Kaderplätze. Jede Runde gibt XP, ## weitere XP kauft man. Level gegen Würfeln ist die TFT-Kernentscheidung. const START_LEVEL := 3 const MAX_LEVEL := 9 const XP_PER_ROUND := 2 const XP_BUY_COST := 4 const XP_BUY_AMOUNT := 4 const XP_TABLE := {3: 4, 4: 8, 5: 12, 6: 20, 7: 32, 8: 48} # --- Planung --------------------------------------------------------------- const PLAN_SECONDS := 30.0 # --- Kampf ----------------------------------------------------------------- ## Heiler heilen nur Verbündete in dieser Hex-Reichweite. const HEAL_RANGE := 2 # --- Element-Traits -------------------------------------------------------- # steps = Anzahl aufgestellter Einheiten mit diesem Element. const ELEMENT_TRAITS := { Element.FEUER: { "steps": [2, 4, 6], "desc": ["+12% Angriff", "+26% Angriff", "+48% Angriff"], "values": [0.12, 0.26, 0.48], }, Element.WASSER: { "steps": [2, 4, 6], "desc": ["+20% Mana", "+40% Mana", "+70% Mana"], "values": [0.20, 0.40, 0.70], }, Element.WIND: { "steps": [2, 4, 6], "desc": ["+10% Tempo", "+22% Tempo", "+40% Tempo"], "values": [0.10, 0.22, 0.40], }, Element.BLITZ: { "steps": [2, 4, 6], "desc": ["+15% Krit", "+30% Krit", "+55% Krit"], "values": [0.15, 0.30, 0.55], }, Element.ERDE: { "steps": [2, 4, 6], "desc": ["+12% Leben", "+26% Leben", "+48% Leben"], "values": [0.12, 0.26, 0.48], }, Element.EIS: { "steps": [2, 4, 6], "desc": ["Treffer -12% Tempo", "Treffer -22% Tempo", "Treffer -35% Tempo"], "values": [0.12, 0.22, 0.35], }, } # --- Kombi-Traits ---------------------------------------------------------- # Aktiv, wenn beide Elemente mindestens COMBO_MIN Einheiten haben. const COMBO_MIN := 2 const COMBO_TRAITS := [ {"a": Element.FEUER, "b": Element.WIND, "key": "feuersturm", "name": "Feuersturm", "desc": "Fähigkeiten setzen in Brand"}, {"a": Element.WASSER, "b": Element.EIS, "key": "frost", "name": "Frost", "desc": "Gegner starten 15% langsamer"}, {"a": Element.BLITZ, "b": Element.ERDE, "key": "magnetsturm","name": "Magnetsturm", "desc": "Fähigkeiten betäuben 0.6s"}, {"a": Element.FEUER, "b": Element.ERDE, "key": "magma", "name": "Magma", "desc": "20% Schaden reflektiert"}, {"a": Element.WIND, "b": Element.BLITZ, "key": "sturmfront", "name": "Sturmfront", "desc": "Angriffe springen auf ein 2. Ziel"}, {"a": Element.WASSER, "b": Element.FEUER, "key": "dampf", "name": "Dampf", "desc": "18% Lebensraub"}, ] static func element_name(e: int) -> String: return ELEMENT_NAMES[e] static func element_color(e: int) -> Color: return ELEMENT_COLORS[e] static func role_name(r: int) -> String: return ROLE_NAMES[r] ## Aktive Stufe eines Element-Traits (0 = inaktiv, sonst 1..3). static func trait_tier(element: int, count: int) -> int: var steps: Array = ELEMENT_TRAITS[element]["steps"] var tier := 0 for i in steps.size(): if count >= steps[i]: tier = i + 1 return tier ## XP bis zum nächsten Level, 0 auf Maximalstufe. static func xp_needed(level: int) -> int: return XP_TABLE.get(level, 0) ## Grundschaden einer verlorenen Runde, steigt alle vier Runden. static func round_base_damage(round_no: int) -> int: return 2 + int(float(round_no - 1) / 4.0) ## Wahrscheinlichkeit, dass ein Shop-Platz einen neutralen Avatar zeigt. ## Ohne eigene Elemente gibt es nur Neutral. static func neutral_chance(unlocked_count: int) -> float: return pow(NEUTRAL_FALLOFF, float(unlocked_count)) ## Zinsen auf das gehaltene Gold. static func interest(gold: int) -> int: return mini(INTEREST_MAX, int(float(gold) / float(INTEREST_STEP))) ## Streak-Bonusgold (Sieg- oder Niederlagenserie). static func streak_gold(streak: int) -> int: var s := absi(streak) if s >= 5: return 3 if s == 4: return 2 if s == 3: return 1 return 0