update
This commit is contained in:
160
scripts/cards.gd
160
scripts/cards.gd
@@ -8,7 +8,8 @@ const ELEMENT_COLORS := {
|
||||
"Erde": Color("73552d"),
|
||||
"Luft": Color("78aac8"),
|
||||
}
|
||||
# Jede Paarung ist eine Kombo, auch gleiche Elemente.
|
||||
# Jede Karte hat genau ein Element. Kombos entstehen erst im Spiel: beim Verschmelzen
|
||||
# (der Name des neuen Wesens) und als Feldkombo zwischen benachbarten Elementen.
|
||||
const COMBOS := {
|
||||
"Feuer+Feuer": "Inferno",
|
||||
"Wasser+Wasser": "Tiefsee",
|
||||
@@ -20,11 +21,6 @@ const COMBOS := {
|
||||
"Wasser+Erde": "Schlamm",
|
||||
"Wasser+Luft": "Sturm",
|
||||
"Erde+Luft": "Sand",
|
||||
# Dreifach-Kombos: die mythische Stufe
|
||||
"Feuer+Feuer+Feuer": "Sonne",
|
||||
"Wasser+Wasser+Wasser": "Ozean",
|
||||
"Erde+Erde+Erde": "Gebirge",
|
||||
"Luft+Luft+Luft": "Himmel",
|
||||
}
|
||||
# Feldkombos: entstehen, wenn ein Wesen neben einem Wesen mit anderem Element ausgespielt wird.
|
||||
const FIELD_COMBO_TEXT := {
|
||||
@@ -32,10 +28,21 @@ const FIELD_COMBO_TEXT := {
|
||||
"Lava": "1 Schaden an das stärkste gegnerische Wesen",
|
||||
"Blitz": "2 Schaden an den schwächsten Gegner",
|
||||
"Schlamm": "Heile dich um 3",
|
||||
"Sturm": "Ziehe 1 Karte",
|
||||
"Sturm": "Erhalte 1 Mana",
|
||||
"Sand": "Das neue Wesen erhält +1/+1",
|
||||
}
|
||||
const RESONANCE_TEXT := "Resonanz: Ein Wesen bekommt Angriff für eigene Wesen mit gleichem Element: +1, +2, +3, +3 bei ein bis vier Partnern."
|
||||
# Elementkreis: Wasser löscht Feuer, Feuer verbrennt Luft, Luft trägt Erde ab, Erde saugt Wasser auf.
|
||||
const BEATS := {"Wasser": "Feuer", "Feuer": "Luft", "Luft": "Erde", "Erde": "Wasser"}
|
||||
# Das Feld gehört allen am Tisch: jede gespielte Karte lädt ihre Elemente, bei FIELD_CHARGE kippt es.
|
||||
const FIELD_CHARGE := 4
|
||||
const FIELD_NAMES := {"Feuer": "Vulkan", "Wasser": "Flut", "Erde": "Fels", "Luft": "Wirbel"}
|
||||
const FIELD_TEXT := {
|
||||
"Feuer": "Feuerwesen haben +1 Angriff",
|
||||
"Wasser": "Wasserwesen haben +1 Angriff",
|
||||
"Erde": "Erdwesen haben +1 Angriff",
|
||||
"Luft": "Luftwesen haben +1 Angriff",
|
||||
}
|
||||
# Seltenheit ist reine Optik: jede Karte gibt es in jeder Stufe, die Werte bleiben gleich.
|
||||
const RARITIES: Array[String] = ["gewöhnlich", "selten", "episch", "legendär", "mythisch"]
|
||||
const RARITY_COLORS := {
|
||||
"gewöhnlich": Color("aaaaaa"),
|
||||
@@ -45,15 +52,7 @@ const RARITY_COLORS := {
|
||||
"mythisch": Color("ff5a8c"),
|
||||
}
|
||||
const KEYWORDS: Array[String] = ["Wächter", "Flink", "Schild", "Doppelschlag"]
|
||||
const KEYWORD_HELP := {
|
||||
"Wächter": "Muss zuerst angegriffen werden.",
|
||||
"Flink": "Kann sofort angreifen.",
|
||||
"Schild": "Blockt den nächsten Schaden.",
|
||||
"Doppelschlag": "Greift zweimal pro Zug an.",
|
||||
}
|
||||
const CHOSEN_TARGETS: Array[String] = ["enemy_creature", "enemy_any", "friendly_creature"]
|
||||
# Näherung der Kartenstärke: Seltenheit plus Zuschläge für Schlüsselwörter und Effekte.
|
||||
const RARITY_POINTS := {"gewöhnlich": 1.0, "selten": 2.0, "episch": 3.0, "legendär": 4.0, "mythisch": 5.0}
|
||||
const DATA_PATH := "res://data/karten.json"
|
||||
|
||||
|
||||
@@ -63,10 +62,54 @@ static func combo_key(elements: Array) -> String:
|
||||
return "+".join(sorted)
|
||||
|
||||
|
||||
## Name der Elementkombination eines verschmolzenen Wesens; drei oder vier Elemente sind eine Chimäre.
|
||||
static func combo_name(elements: Array) -> String:
|
||||
if elements.size() == 1:
|
||||
return elements[0]
|
||||
return COMBOS[combo_key(elements)]
|
||||
return COMBOS.get(combo_key(elements), "Chimäre")
|
||||
|
||||
|
||||
## Elementvorteil aus Sicht von own: +1 im Vorteil, -1 im Nachteil, sonst 0.
|
||||
## Mehrfachelemente nehmen den für sie besseren Wert.
|
||||
static func matchup(own: Array, other: Array) -> int:
|
||||
for e in own:
|
||||
if BEATS.get(e, "") in other:
|
||||
return 1
|
||||
for e in other:
|
||||
if BEATS.get(e, "") in own:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
## Mana für eine Verschmelzung: Durchschnitt der Kartenkosten, aufgerundet.
|
||||
static func fusion_cost(a: Card, b: Card) -> int:
|
||||
return ceili((a.cost + b.cost) / 2.0)
|
||||
|
||||
|
||||
## Name des verschmolzenen Wesens: die Kombo seiner Elemente (ein Element zählt doppelt).
|
||||
static func fusion_name(elements: Array) -> String:
|
||||
return combo_name(elements if elements.size() >= 2 else [elements[0], elements[0]])
|
||||
|
||||
|
||||
## Karte des verschmolzenen Wesens: Elemente und Schlüsselwörter beider Karten,
|
||||
## Werte kommen vom Spiel (Durchschnitt plus 1, siehe Game.fuse).
|
||||
static func fused(a: Card, b: Card, attack: int, health: int) -> Card:
|
||||
var els := []
|
||||
for e in a.elements + b.elements:
|
||||
if not (e in els):
|
||||
els.append(e)
|
||||
els.sort_custom(func(x, y): return ELEMENTS.find(x) < ELEMENTS.find(y))
|
||||
var c := Card.new()
|
||||
c.id = "fus_%s_%s" % [a.id, b.id]
|
||||
c.name = fusion_name(els)
|
||||
c.elements = els
|
||||
c.cost = fusion_cost(a, b)
|
||||
c.attack = attack
|
||||
c.health = health
|
||||
for k in KEYWORDS:
|
||||
if k in a.keywords or k in b.keywords:
|
||||
c.keywords.append(k)
|
||||
return c
|
||||
|
||||
|
||||
static func field_combo_effect(name: String) -> Effect:
|
||||
@@ -80,31 +123,23 @@ static func field_combo_effect(name: String) -> Effect:
|
||||
"Schlamm":
|
||||
return Effect.new("heal", "self_hero", 3)
|
||||
"Sturm":
|
||||
return Effect.new("draw", "none", 1)
|
||||
return Effect.new("mana", "none", 1)
|
||||
"Sand":
|
||||
return Effect.new("buff", "self_creature", 1, 1)
|
||||
return null
|
||||
|
||||
|
||||
const TRIGGER_TEXT := {
|
||||
"attacked": "Wenn ein gegnerisches Wesen dich oder eines deiner Wesen angreift",
|
||||
"creature_played": "Wenn ein Gegner ein Wesen ausspielt",
|
||||
}
|
||||
|
||||
|
||||
class Effect extends RefCounted:
|
||||
var kind: String # damage, heal, draw, freeze, bounce, buff, shield, summon, cancel
|
||||
var kind: String # damage, heal, mana, freeze, bounce, buff, shield
|
||||
var target: String # siehe CHOSEN_TARGETS und Game.resolve()
|
||||
var amount: int
|
||||
var amount2: int
|
||||
var card_id: String # für summon
|
||||
|
||||
func _init(k: String, t: String, a: int = 0, a2: int = 0, cid: String = "") -> void:
|
||||
func _init(k: String, t: String, a: int = 0, a2: int = 0) -> void:
|
||||
kind = k
|
||||
target = t
|
||||
amount = a
|
||||
amount2 = a2
|
||||
card_id = cid
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
var d := {"kind": kind, "target": target}
|
||||
@@ -112,34 +147,15 @@ class Effect extends RefCounted:
|
||||
d["amount"] = amount
|
||||
if amount2 != 0:
|
||||
d["amount2"] = amount2
|
||||
if card_id != "":
|
||||
d["card_id"] = card_id
|
||||
return d
|
||||
|
||||
static func from_dict(d: Dictionary) -> Effect:
|
||||
return Effect.new(d.kind, d.get("target", "none"), int(d.get("amount", 0)), int(d.get("amount2", 0)), str(d.get("card_id", "")))
|
||||
return Effect.new(d.kind, d.get("target", "none"), int(d.get("amount", 0)), int(d.get("amount2", 0)))
|
||||
|
||||
## Kartentext in der aktuellen Sprache. trigger: bei Fallen der Auslöser (Wortwahl).
|
||||
func text(trigger: String = "") -> String:
|
||||
## Kartentext in der aktuellen Sprache.
|
||||
func text() -> String:
|
||||
var n := str(amount)
|
||||
if target == "trigger_source":
|
||||
var played := trigger == "creature_played"
|
||||
match kind:
|
||||
"damage":
|
||||
if played:
|
||||
return tr("%s Schaden an dieses Wesen") % n
|
||||
return tr("%s Schaden an den Angreifer") % n
|
||||
"freeze":
|
||||
if played:
|
||||
return tr("Es wird eingefroren")
|
||||
return tr("Der Angreifer wird eingefroren")
|
||||
"bounce":
|
||||
return tr("Es geht zurück auf die Hand")
|
||||
match kind:
|
||||
"cancel":
|
||||
return tr("Der Angriff verpufft")
|
||||
"summon":
|
||||
return tr("Beschwöre einen %s") % Cards.by_id(card_id).title()
|
||||
"damage":
|
||||
match target:
|
||||
"enemy_creature":
|
||||
@@ -154,10 +170,10 @@ class Effect extends RefCounted:
|
||||
return tr("%s Schaden an alle Gegner und ihre Wesen") % n
|
||||
"heal":
|
||||
return tr("Heile dich um %s") % n
|
||||
"draw":
|
||||
"mana":
|
||||
if amount == 1:
|
||||
return tr("Ziehe 1 Karte")
|
||||
return tr("Ziehe %s Karten") % n
|
||||
return tr("Erhalte 1 Mana")
|
||||
return tr("Erhalte %s Mana") % n
|
||||
"freeze":
|
||||
if target == "enemy_creature":
|
||||
return tr("Friere ein gegnerisches Wesen ein")
|
||||
@@ -188,31 +204,18 @@ class Card extends RefCounted:
|
||||
var name_en := "" # englischer Name
|
||||
var elements: Array
|
||||
var cost: int
|
||||
var rarity: String
|
||||
var kind: String # "Wesen", "Zauber" oder "Falle"
|
||||
var attack: int = 0
|
||||
var health: int = 0
|
||||
var keywords: Array = []
|
||||
var effects: Array = []
|
||||
var trigger: String = "" # nur Fallen: attacked, creature_played
|
||||
|
||||
func is_creature() -> bool:
|
||||
return kind == "Wesen"
|
||||
|
||||
func is_trap() -> bool:
|
||||
return kind == "Falle"
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
var d := {"id": id, "name": name, "name_en": name_en, "elements": elements, "cost": cost, "rarity": rarity, "kind": kind}
|
||||
if is_creature():
|
||||
d["attack"] = attack
|
||||
d["health"] = health
|
||||
var d := {"id": id, "name": name, "name_en": name_en, "elements": elements, "cost": cost,
|
||||
"attack": attack, "health": health}
|
||||
if not keywords.is_empty():
|
||||
d["keywords"] = keywords
|
||||
if not effects.is_empty():
|
||||
d["effects"] = effects.map(func(e): return e.to_dict())
|
||||
if trigger != "":
|
||||
d["trigger"] = trigger
|
||||
return d
|
||||
|
||||
static func from_dict(d: Dictionary) -> Card:
|
||||
@@ -222,14 +225,11 @@ class Card extends RefCounted:
|
||||
c.name_en = str(d.get("name_en", ""))
|
||||
c.elements = Array(d.elements)
|
||||
c.cost = int(d.cost)
|
||||
c.rarity = d.rarity
|
||||
c.kind = d.kind
|
||||
c.attack = int(d.get("attack", 0))
|
||||
c.health = int(d.get("health", 0))
|
||||
c.keywords = Array(d.get("keywords", []))
|
||||
for e in d.get("effects", []):
|
||||
c.effects.append(Effect.from_dict(e))
|
||||
c.trigger = str(d.get("trigger", ""))
|
||||
return c
|
||||
|
||||
## Anzeigename in der aktuellen Sprache.
|
||||
@@ -261,14 +261,8 @@ class Card extends RefCounted:
|
||||
if not effects.is_empty():
|
||||
var fx: Array[String] = []
|
||||
for f in effects:
|
||||
fx.append(f.text(trigger))
|
||||
var s := ", ".join(fx) + "."
|
||||
if is_trap():
|
||||
parts.append(tr("Falle. %s: %s") % [tr(Cards.TRIGGER_TEXT[trigger]), s])
|
||||
elif is_creature():
|
||||
parts.append(tr("Beim Ausspielen: %s") % s)
|
||||
else:
|
||||
parts.append(s)
|
||||
fx.append(f.text())
|
||||
parts.append(tr("Beim Ausspielen: %s") % (", ".join(fx) + "."))
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
@@ -294,14 +288,6 @@ static func has_id(id) -> bool:
|
||||
return _by_id.has(id)
|
||||
|
||||
|
||||
static func card_value(c: Card) -> float:
|
||||
return RARITY_POINTS[c.rarity] + 0.3 * c.keywords.size() + 0.3 * c.effects.size()
|
||||
|
||||
|
||||
static func of_rarity(r: String) -> Array:
|
||||
return all().filter(func(c): return c.rarity == r)
|
||||
|
||||
|
||||
## Lädt den Kartenpool aus data/karten.json.
|
||||
static func _build() -> void:
|
||||
var json := JSON.new()
|
||||
|
||||
Reference in New Issue
Block a user