extends Node ## Loads modifier definitions and serves them by id. ## ## ⚠️ PROTOTYPE. This is scaffolding, not the real mod loader. ## ## What's wrong with it, to be fixed in Phase 3: ## - DirAccess cannot walk into a PCK. This works in the editor and silently ## returns NOTHING in an exported build. Needs a build-time manifest. ## - No namespacing. Ids are bare strings, so two mods can collide. ## - No mod.json, no api_version, no dependency resolution. ## - Only reads content/official/. Doesn't touch user://mods/. ## ## Autoload name is ModRegistry (PascalCase), so call sites read as ## ModRegistry.get_by_id(...) rather than looking like a local variable. const MODIFIER_DIR := "res://content/official/modifiers/" var modifiers: Array[Modifier] = [] var _by_id: Dictionary = {} func _ready() -> void: _load_from_folder(MODIFIER_DIR) _reindex() print("ModRegistry: loaded %d modifiers." % modifiers.size()) ## Recursive, because each modifier now lives in its own folder: ## modifiers/mjolnir/mjolnir.tres func _load_from_folder(path: String) -> void: var dir := DirAccess.open(path) if dir == null: push_warning("ModRegistry: can't open %s" % path) return dir.list_dir_begin() var fname := dir.get_next() while fname != "": var full := path.path_join(fname) if dir.current_is_dir(): if not fname.begins_with("."): _load_from_folder(full) elif fname.ends_with(".tres"): var res := load(full) if res is Modifier: modifiers.append(res) else: push_warning("ModRegistry: %s is not a Modifier" % full) fname = dir.get_next() dir.list_dir_end() func _reindex() -> void: _by_id.clear() for m in modifiers: if m.id.is_empty(): push_warning("ModRegistry: modifier with empty id, skipping.") continue if _by_id.has(m.id): push_warning("ModRegistry: duplicate id '%s'." % m.id) _by_id[m.id] = m func get_by_id(id: String) -> Modifier: return _by_id.get(id) ## Pick `count` distinct modifiers, weighted by rarity. func pick_random(count: int) -> Array[Modifier]: var pool := modifiers.duplicate() var chosen: Array[Modifier] = [] count = mini(count, pool.size()) for _i in count: var total := 0.0 for m in pool: total += maxf(m.weight, 0.0001) var r := randf() * total var acc := 0.0 var picked := 0 for j in pool.size(): acc += maxf(pool[j].weight, 0.0001) if r <= acc: picked = j break chosen.append(pool[picked]) pool.remove_at(picked) # distinct — don't offer the same one twice return chosen