Randoloot/scripts/items/item_generator.gd
2026-10-20 19:53:03 +02:00

98 lines
4.9 KiB
GDScript

extends RefCounted
const DEFINITIONS := [
preload("res://resources/items/recycler.tres"),
preload("res://resources/items/virus.tres"),
preload("res://resources/items/titanium_armor.tres"),
preload("res://resources/items/combat_drones.tres"),
preload("res://resources/items/teleport.tres"),
preload("res://resources/items/cloak.tres"),
preload("res://resources/items/mine_layer.tres"),
preload("res://resources/items/flares.tres"),
preload("res://resources/items/solar_shield_charge.tres"),
preload("res://resources/items/gamma_cannon.tres"),
preload("res://resources/items/plasma_cannon.tres"),
preload("res://resources/items/beam_laser.tres"),
preload("res://resources/items/machine_gun.tres"),
preload("res://resources/items/missile_launcher.tres"),
preload("res://resources/items/unguided_missile.tres"),
preload("res://resources/items/reinforced_shield.tres"),
preload("res://resources/items/hull_plating.tres"),
preload("res://resources/items/compact_generator.tres"),
preload("res://resources/items/engine_extension.tres"),
preload("res://resources/items/shield_recharger.tres"),
preload("res://resources/items/afterburner.tres"),
preload("res://resources/items/shield_charge.tres"),
preload("res://resources/items/repair_drones.tres"),
preload("res://resources/items/ship_computer.tres"),
]
const AFFIX_DATABASE := preload("res://resources/items/affix_database.tres")
static func generate(level: int, enemy_tier: int = 0) -> ItemInstance:
return generate_for_definition(DEFINITIONS.pick_random(), level, enemy_tier)
static func generate_loot(level: int, enemy_tier: int = 0, definition: ItemDefinition = null, equipment_luck_percent: float = 0.0) -> ItemInstance:
var loot_level := GameState.captain.roll_loot_level(level, -1.0, equipment_luck_percent)
return generate_for_definition(definition if definition != null else DEFINITIONS.pick_random(), loot_level, enemy_tier)
static func generate_for_definition(definition: ItemDefinition, level: int, enemy_tier: int = 0) -> ItemInstance:
var item := ItemInstance.new()
item.definition = definition
roll_base_values(item, level)
if definition.category == ItemDefinition.Category.ACTIVE_EXTENSION:
if item.is_passive_extension():
item.passive_value = preload("res://scripts/items/passive_extension_rolls.gd").roll_value(definition.passive_effect)
else:
item.extension_cooldown = ItemInstance.EXTENSION_COOLDOWNS.roll_seconds()
return item
var roll := randf()
var count := 1 if roll < 0.5 else (2 if roll < 0.75 else (3 if roll < 0.89 else (4 if roll < 0.97 else 5)))
if enemy_tier == 1:
count = mini(5, count + 1)
elif enemy_tier >= 2:
count = maxi(4, count)
item.additional_modifiers = AFFIX_DATABASE.roll_affixes(item.definition, item.item_level, count)
item.rarity = clampi(item.additional_modifiers.size() - 1, 0, 4)
return item
static func roll_base_values(item: ItemInstance, level: int) -> void:
var definition := item.definition
item.item_level = clampi(level, 1, 1000)
var scale_factor := 1.0 + 0.05 * (item.item_level - 1)
item.primary_value_override = definition.primary_value * scale_factor * randf_range(0.8, 1.2)
if definition.primary_stat == "max_speed":
item.primary_value_override = randf_range(1.0, 5.0) * scale_factor
if definition.category == ItemDefinition.Category.WEAPON:
var lower := randi_range(mini(definition.damage_lower_roll_min, definition.damage_lower_roll_max), maxi(definition.damage_lower_roll_min, definition.damage_lower_roll_max))
var spread := randi_range(mini(definition.damage_spread_roll_min, definition.damage_spread_roll_max), maxi(definition.damage_spread_roll_min, definition.damage_spread_roll_max))
item.damage_min = maxf(1.0, roundf(lower * scale_factor))
item.damage_max = maxf(item.damage_min + 1.0, roundf((lower + spread) * scale_factor))
item.primary_value_override = (item.damage_min + item.damage_max) * 0.5
item.energy_regeneration_override = definition.energy_regeneration_bonus * scale_factor * randf_range(0.8, 1.2)
static func can_advance(item: ItemInstance) -> bool:
return item != null and item.definition != null and item.definition.category != ItemDefinition.Category.CONSUMABLE and item.item_level >= 1 and item.item_level < 1000
static func advance(item: ItemInstance) -> bool:
if not can_advance(item): return false
var level := item.item_level + 1
var updated: Array[ItemStatModifier] = []
for modifier in item.additional_modifiers:
if modifier == null:
updated.append(null)
continue
# Keep shared modifiers on other items untouched.
var copy := modifier.duplicate() as ItemStatModifier
if copy.affix == null:
for candidate in AFFIX_DATABASE.affixes:
if candidate != null and candidate.stat_key == copy.stat_key and candidate.can_roll(item.definition,level):
copy.affix = candidate
break
if copy.affix != null: copy.value = copy.affix.roll_value(level)
# Fixed legacy bonuses without a defined roll range stay fixed.
updated.append(copy)
roll_base_values(item,level)
item.additional_modifiers = updated
return true