Initial commit — Godot space roguelite source

- Touch controls: direct InputEventScreenTouch in shop_ui (bypass relay)
- ItemDB: static preload list instead of DirAccess scan (export fix)
- All 18 items with EN localisation (name_en, desc_en, category_en)
- Ship playstyles: NOVA-1 shield, INFERNO ram, AURORA agile/tank
- Quasar: SMBH visual, jet boost, merge, push, BH-eating
- Atlas & UI text updated EN+DE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 14:38:09 +02:00
commit edc40f9008
108 changed files with 10068 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
extends RefCounted
class_name ItemDB
# ─── Legacy-System (alter Shop / Enemy-Upgrades in game_world.gd) ──────────────
# Bleibt unverändert für Backward-Compat. Wird nur von game_world.gd für
# Enemy-Stat-Boosts ab Welle 10 verwendet.
enum Rarity { COMMON, UNCOMMON, RARE, EPIC }
const RARITY_WEIGHTS: Array = [60, 25, 12, 3]
const RARITY_COLORS: Dictionary = {
0: Color("#aaaaaa"), # COMMON
1: Color("#44ff88"), # UNCOMMON
2: Color("#4488ff"), # RARE
3: Color("#cc44ff") # EPIC
}
const RARITY_NAMES: Dictionary = {
0: "STANDARD",
1: "SELTEN",
2: "EPISCH",
3: "LEGENDÄR"
}
const ITEMS: Array = [
{
"id": "thrust_1", "name": "Schubverstärker", "rarity": 0, "cost": 50,
"desc": "+12% Geschwindigkeit",
"effects": { "speed_mult": 1.12 }
},
{
"id": "firerate_1", "name": "Schnellfeuer I", "rarity": 0, "cost": 50,
"desc": "+15% Feuerrate",
"effects": { "fire_rate_mult": 1.15 }
},
{
"id": "damage_1", "name": "Schwere Ladung I", "rarity": 0, "cost": 50,
"desc": "+15% Trefferzone",
"effects": { "damage_mult": 1.15 }
},
{
"id": "shield_1", "name": "Panzerplatte", "rarity": 0, "cost": 55,
"desc": "+1 Schutzladung",
"effects": { "shield_charges": 1 }
},
{
"id": "firerate_2", "name": "Schnellfeuer II", "rarity": 1, "cost": 90,
"desc": "+22% Feuerrate",
"effects": { "fire_rate_mult": 1.22 }
},
{
"id": "damage_2", "name": "Schwere Ladung II", "rarity": 1, "cost": 100,
"desc": "+22% Trefferzone +10% Projektilgeschwindigkeit",
"effects": { "damage_mult": 1.22, "bullet_speed_mult": 1.10 }
},
]
# Weighted random roll — Legacy-Shop (wird nicht mehr für Werkstatt verwendet)
static func roll_shop(count: int, _exclude_ids: Array = []) -> Array:
var pool: Array = []
for item: Dictionary in ITEMS:
var w: int = RARITY_WEIGHTS[int(item["rarity"])]
for _i in w:
pool.append(item)
pool.shuffle()
var result: Array = []
var seen_ids: Dictionary = {}
for item: Dictionary in pool:
var iid: String = item["id"]
if iid in seen_ids: continue
seen_ids[iid] = true
result.append(item)
if result.size() >= count:
break
return result
# ─── Werkstatt-System (Plugin-basiert) ─────────────────────────────────────────
# Auto-Discovery: alle *.gd unter res://items/ die ItemDef erweitern werden
# beim ersten Zugriff registriert.
static var _registry: Array = []
static var _registry_loaded: bool = false
# Static preload list — DirAccess doesn't work in exported builds (compiled scripts
# aren't raw files in the PCK). preload() is resolved at compile time and always works.
const _ITEM_SCRIPTS: Array = [
preload("res://items/weapons/wk_burst.gd"),
preload("res://items/weapons/wk_charge.gd"),
preload("res://items/weapons/wk_ion.gd"),
preload("res://items/weapons/wk_laser.gd"),
preload("res://items/weapons/wk_plasma.gd"),
preload("res://items/weapons/wk_rail.gd"),
preload("res://items/weapons/wk_scatter.gd"),
preload("res://items/weapons/wk_shotgun.gd"),
preload("res://items/weapons/wk_sniper.gd"),
preload("res://items/hull/hull_giant.gd"),
preload("res://items/hull/hull_nullfeld.gd"),
preload("res://items/hull/hull_plating.gd"),
preload("res://items/hull/hull_reaktor.gd"),
preload("res://items/drives/drive_overdrive.gd"),
preload("res://items/drives/drive_quantum.gd"),
preload("res://items/drives/drive_steer.gd"),
preload("res://items/special/special_credit_mag.gd"),
preload("res://items/special/special_wipe_core.gd"),
]
static func _ensure_loaded() -> void:
if _registry_loaded: return
_registry_loaded = true
for script: Script in _ITEM_SCRIPTS:
if script == null or not script.can_instantiate(): continue
var inst = script.new()
if inst is ItemDef and (inst as ItemDef).id != "":
_registry.append(inst)
static func get_all_defs() -> Array:
_ensure_loaded()
return _registry
static func get_def_by_id(id: String) -> ItemDef:
_ensure_loaded()
for item in _registry:
if (item as ItemDef).id == id:
return item
return null
# Rolls `count` random items from the Werkstatt pool, excluding given IDs.
static func roll_werkstatt(count: int, exclude_ids: Array = []) -> Array:
_ensure_loaded()
var pool: Array = []
for item in _registry:
var def := item as ItemDef
if exclude_ids.has(def.id):
continue
pool.append(def)
pool.shuffle()
var n: int = min(count, pool.size())
return pool.slice(0, n)
# ─── Effect-Klassifizierung (für UI-Farbkodierung) ────────────────────────────
static func is_positive_effect(key: String, val) -> bool:
match key:
"speed_mult", "turn_mult", "fire_rate_mult", "damage_mult", \
"bullet_speed_mult", "invuln_mult", "credit_bonus":
return float(val) >= 1.0
"bullet_count", "shield_charges":
return int(val) > 0
"bh_resist":
return float(val) > 0.0
"wipe_mult":
return float(val) <= 1.0 # kleiner = schneller = besser
return true
# Menschlich lesbarer Stat-Name für die Vorschau.
static func stat_display_name(key: String) -> String:
match key:
"speed_mult": return "Speed"
"turn_mult": return "Wendung"
"fire_rate_mult": return "Feuerrate"
"damage_mult": return "Schaden"
"bullet_speed_mult": return "Proj.-Speed"
"bullet_count": return "Projektile"
"shield_charges": return "Schilde"
"invuln_mult": return "Unverwundbar"
"bh_resist": return "BH-Resistenz"
"wipe_mult": return "Wipe-Ladezeit"
"credit_bonus": return "Kreditgewinn"
return key
static func get_by_id(id: String) -> Dictionary:
for item: Dictionary in ITEMS:
if item["id"] == id:
return item
return {}