136 lines
7.3 KiB
GDScript
136 lines
7.3 KiB
GDScript
extends Resource
|
||
class_name ItemInstance
|
||
|
||
const ENERGY_REGENERATION_BONUS_MULTIPLIER := 0.5
|
||
|
||
@export var definition: ItemDefinition
|
||
@export_range(1, 1000, 1) var item_level: int = 1
|
||
@export var additional_modifiers: Array[ItemStatModifier] = []
|
||
@export var primary_value_override: float = -1.0
|
||
@export var energy_regeneration_override: float = -1.0
|
||
@export_range(0, 4) var rarity: int = 0
|
||
@export var damage_min: float = -1.0
|
||
@export var damage_max: float = -1.0
|
||
|
||
func get_stat_total(stat_key: String) -> float:
|
||
if stat_key == "damage" and definition and definition.category == ItemDefinition.Category.WEAPON:
|
||
var damage_range := get_damage_range()
|
||
return (damage_range.x + damage_range.y) * 0.5
|
||
var total := 0.0
|
||
if definition and definition.primary_stat == stat_key:
|
||
total += primary_value_override if primary_value_override >= 0.0 else definition.primary_value
|
||
if definition and stat_key == "energy_regeneration":
|
||
total += energy_regeneration_override if energy_regeneration_override >= 0.0 else definition.energy_regeneration_bonus
|
||
for modifier in additional_modifiers:
|
||
if modifier and modifier.stat_key == stat_key:
|
||
total += modifier.value
|
||
return total * ENERGY_REGENERATION_BONUS_MULTIPLIER if stat_key == "energy_regeneration" else total
|
||
|
||
func get_damage_range() -> Vector2:
|
||
if definition == null or definition.category != ItemDefinition.Category.WEAPON:
|
||
return Vector2.ZERO
|
||
var lower := damage_min
|
||
var upper := damage_max
|
||
if lower < 0.0 or upper < 0.0:
|
||
if primary_value_override >= 0.0:
|
||
# Older item instances retain their previously rolled damage.
|
||
lower = primary_value_override
|
||
upper = primary_value_override
|
||
else:
|
||
# Starter weapons use the middle of the definition's roll settings.
|
||
lower = roundf((definition.damage_lower_roll_min + definition.damage_lower_roll_max) * 0.5)
|
||
upper = lower + roundf((definition.damage_spread_roll_min + definition.damage_spread_roll_max) * 0.5)
|
||
var flat_bonus := 0.0
|
||
for modifier in additional_modifiers:
|
||
if modifier and modifier.stat_key == "damage":
|
||
flat_bonus += modifier.value
|
||
return Vector2(maxf(0.0, minf(lower, upper) + flat_bonus), maxf(0.0, maxf(lower, upper) + flat_bonus))
|
||
|
||
func roll_damage() -> float:
|
||
var damage_range := get_damage_range()
|
||
# Inclusive integer rolls, preserving fractional flat bonuses on both bounds.
|
||
var whole_span := floori(damage_range.y - damage_range.x)
|
||
return damage_range.x + randi_range(0, whole_span)
|
||
|
||
func get_energy_cost() -> float:
|
||
if definition == null or definition.category != ItemDefinition.Category.WEAPON:
|
||
return 0.0
|
||
if definition.weapon_kind in [ItemDefinition.WeaponKind.MACHINE_GUN, ItemDefinition.WeaponKind.MISSILE, ItemDefinition.WeaponKind.GAMMA]:
|
||
return 0.0
|
||
# Compare this weapon with its starter damage. Captain/global gear buffs are free.
|
||
var base_lower := roundf((definition.damage_lower_roll_min + definition.damage_lower_roll_max) * 0.5)
|
||
var base_spread := roundf((definition.damage_spread_roll_min + definition.damage_spread_roll_max) * 0.5)
|
||
var base_average := maxf(1.0, base_lower + base_spread * 0.5)
|
||
var damage_range := get_damage_range()
|
||
var average := maxf(0.1, (damage_range.x + damage_range.y) * 0.5)
|
||
var damage_factor := pow(average / base_average, definition.energy_damage_exponent)
|
||
var addon_factor := 1.0 + get_addon_count() * definition.energy_per_addon
|
||
return snappedf(maxf(0.0, definition.energy_cost * damage_factor * addon_factor), 0.1)
|
||
|
||
func get_damage_range_text() -> String:
|
||
var damage_range := get_damage_range()
|
||
return "%s–%s DMG" % [_format_damage(damage_range.x), _format_damage(damage_range.y)]
|
||
|
||
static func _format_damage(value: float) -> String:
|
||
return str(roundi(value)) if is_equal_approx(value, roundf(value)) else "%.1f" % value
|
||
|
||
func get_rarity_name() -> String:
|
||
return ["Zwykły", "Niepospolity", "Rzadki", "Epicki", "Legendarny"][clampi(rarity, 0, 4)]
|
||
|
||
func get_rarity_color() -> Color:
|
||
return get_background_color().lightened(0.4)
|
||
|
||
func get_addon_count() -> int:
|
||
var count := 0
|
||
for modifier in additional_modifiers:
|
||
if modifier != null:
|
||
count += 1
|
||
return count
|
||
|
||
func get_background_color() -> Color:
|
||
return [Color("101925"), Color("414b58"), Color("65432d"), Color("244c79"), Color("796025")][mini(get_addon_count(), 4)]
|
||
|
||
func get_type_color() -> Color:
|
||
if definition == null:
|
||
return Color("354455")
|
||
match definition.category:
|
||
ItemDefinition.Category.WEAPON:
|
||
return Color("ae74ef")
|
||
ItemDefinition.Category.ACTIVE_EXTENSION:
|
||
return Color("f39c4d")
|
||
return Color("65ce8b")
|
||
|
||
func get_tooltip_text() -> String:
|
||
if definition == null:
|
||
return ""
|
||
var primary_text := "%s: +%.1f%s" % [stat_name(definition.primary_stat), get_stat_total(definition.primary_stat), definition.stat_suffix]
|
||
if definition.category == ItemDefinition.Category.WEAPON:
|
||
primary_text = "Obrażenia: %s" % get_damage_range_text()
|
||
elif definition.active_ability != null:
|
||
primary_text = definition.active_ability.description
|
||
var lines: Array[String] = [definition.display_name, "%s • Poziom %d" % [get_rarity_name(), item_level], "", definition.description, "", "CECHA GŁÓWNA", primary_text]
|
||
if definition.energy_regeneration_bonus > 0.0:
|
||
lines.append("Regeneracja generatora: +%.1f /s" % ((energy_regeneration_override if energy_regeneration_override >= 0.0 else definition.energy_regeneration_bonus) * ENERGY_REGENERATION_BONUS_MULTIPLIER))
|
||
if definition.category == ItemDefinition.Category.WEAPON:
|
||
lines.append("Typ obrażeń: %s" % DamageTypes.NAMES[definition.get_damage_type()])
|
||
lines.append("Każdy strzał / impuls losuje obrażenia z tego zakresu.")
|
||
if definition.weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN:
|
||
lines.append("Zasięg: %.0f px • Amunicja: 1 / strzał" % definition.weapon_range)
|
||
elif definition.weapon_kind == ItemDefinition.WeaponKind.GAMMA:
|
||
lines.append("Zasięg: %.0f px • Napromieniowana amunicja: 1 / strzał" % definition.weapon_range)
|
||
elif definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE:
|
||
lines.append("Amunicja: 1 rakieta / strzał • Cooldown bazowy: 5 s • Bez kosztu energii")
|
||
else:
|
||
lines.append("Zasięg: %.0f px • Energia: %.1f / strzał" % [definition.weapon_range, get_energy_cost()])
|
||
lines.append("Odstęp strzałów: %.2f s" % maxf(definition.fire_interval, definition.weapon_cooldown))
|
||
if not additional_modifiers.is_empty():
|
||
lines.append("\nADDONY (aktywne po zamontowaniu)")
|
||
for modifier in additional_modifiers:
|
||
if modifier == null:
|
||
continue
|
||
lines.append(modifier.get_display_text())
|
||
return "\n".join(lines)
|
||
|
||
static func stat_name(key: String) -> String:
|
||
return str({"kinetic_absorption": "Absorpcja kinetyczna", "energy_absorption": "Absorpcja energetyczna", "radiation_absorption": "Absorpcja radiacyjna", "missile_cooldown_reduction": "Skrócenie cooldownu rakiet", "scanner_range": "Zasięg skanera", "shield_on_damage_percent": "Odzysk tarcz z obrażeń", "damage": "Obrażenia", "max_hull": "Kadłub", "max_shields": "Tarcze", "max_energy": "Energia", "max_speed": "Prędkość", "energy_regeneration": "Regeneracja energii", "shield_regeneration": "Regeneracja tarcz", "hull_percent": "Kadłub", "shield_percent": "Tarcze", "damage_percent": "Obrażenia broni", "speed_percent": "Prędkość statku", "experience_percent": "Doświadczenie (EXP)", "credits_percent": "Kredyty", "critical_chance_percent": "Szansa na trafienie krytyczne", "critical_damage_percent": "Obrażenia krytyczne"}.get(key, key))
|