Randoloot/tests/items/combat_orbs_checks.gd
2026-10-25 22:38:13 +01:00

130 lines
6.9 KiB
GDScript

extends RefCounted
const ORBS := preload("res://scripts/items/special_orbs.gd")
const LOOT := preload("res://scripts/game/distant_loot.gd")
const GENERATOR := preload("res://scripts/items/item_generator.gd")
var failures: Array[String] = []
class HitTarget extends Node2D:
var damage_type := -1
var damage := 0.0
func take_piercing_weapon_hit(amount: float, _critical: bool, kind: int, _piercing: float) -> void:
damage_type = kind
damage = amount
func check(value: bool, message: String) -> void:
if not value: failures.append(message)
func run(game: Node) -> Array[String]:
var player: Node = game.player
var inventory: ShipInventory = player.get_node("ShipInventory")
var progress: Resource = GameState.captain.expedition
player.is_docked = true
for id in ORBS.IDS:
progress.orbs[id] = 10
check(ORBS.icon(id) != null, "Missing orb icon: " + id)
var weapon := GENERATOR.generate_for_definition(preload("res://resources/items/plasma_cannon.tres"),1)
var addon := ItemStatModifier.new()
addon.stat_key = "attack_speed_percent"
addon.value = 25.0
addon.affix = preload("res://resources/items/affixes/attack_speed_percent.tres")
weapon.additional_modifiers = [addon]
inventory.weapon_slots[0] = weapon
check(is_equal_approx(weapon.get_fire_interval(), weapon.definition.fire_interval / 1.25), "Attack speed interval")
var cost := weapon.get_energy_cost()
addon.value = 500.0
check(is_equal_approx(weapon.attack_speed_multiplier(),1.25), "Attack speed capped")
addon.value = 25.0
check(is_equal_approx(cost, weapon.get_energy_cost()), "Attack speed does not reduce shot cost")
for level in [1,120,1000]:
var bounds := addon.affix.get_value_range(level)
check(bounds.x >= 2.0 and bounds.y <= 25.0, "Bounded attack speed rolls")
check(not addon.affix.can_roll(preload("res://resources/items/reinforced_shield.tres"),1), "Attack speed only rolls on weapons")
var slow_dps := inventory.get_weapon_dps(0) / 1.25
addon.value = 0.0
check(is_equal_approx(inventory.get_weapon_dps(0),slow_dps), "DPS reflects attack speed")
addon.value = 25.0
var shield := GENERATOR.generate_for_definition(preload("res://resources/items/reinforced_shield.tres"),1)
var engine := GENERATOR.generate_for_definition(preload("res://resources/items/engine_extension.tres"),1)
shield.additional_modifiers = []
engine.additional_modifiers = []
inventory.module_slots[0] = shield
inventory.module_slots[2] = engine
check(not ORBS.accepts(weapon,"polarization") and not ORBS.accepts(shield,"atomic_engine"), "Orb category restrictions")
check(not ORBS.accepts(engine,"electricity"), "Damage orb rejects engine")
var detached := GENERATOR.generate_for_definition(weapon.definition,1)
check(not ORBS.apply(progress,inventory,detached,"replication"), "Cannot replicate unowned equipment")
player.is_docked = false
check(ORBS.apply(progress,inventory,weapon,"electricity"), "Orbs work on owned equipment during flight")
weapon.special_orbs.erase("electricity")
player.is_docked = true
var original_damage := weapon.get_damage_range()
for id in ORBS.DAMAGE_ORBS:
check(ORBS.apply(progress,inventory,weapon,id), "Apply damage orb: " + id)
var kind: int = {"electricity":DamageTypes.Type.ENERGY,"radiation":DamageTypes.Type.RADIATION,"kinetic":DamageTypes.Type.KINETIC}[id]
check(weapon.get_damage_type() == kind, "Converted weapon damage type")
check(weapon.get_damage_range() == original_damage, "Conversion preserves damage")
check(not ORBS.apply(progress,inventory,weapon,id), "Same damage orb cannot stack")
var target := HitTarget.new()
game.add_child(target)
player.get_node("WeaponController")._apply_damage(target,100,false,0,weapon.get_damage_type())
check(target.damage_type == kind, "Laser uses converted type")
var projectile := preload("res://scenes/game/weapon_projectile.tscn").instantiate()
projectile.damage = 100.0
projectile.damage_type_override = weapon.get_damage_type()
projectile._apply_damage_once(target)
check(target.damage_type == kind and target.damage == 100.0, "Projectile uses converted type")
projectile.free()
target.free()
check(weapon.special_orbs.size() == 1, "Damage orbs replace each other")
var normal_boost_cost: float = player.get_mini_boost_energy_cost()
check(ORBS.apply(progress,inventory,engine,"atomic_engine"), "Atomic engine applied")
check(is_equal_approx(player.get_mini_boost_energy_cost(),normal_boost_cost*0.5), "Atomic engine halves TAB cost")
player.energy = 50.0
Input.action_press("ship_mini_boost")
player._update_mini_boost(1.0)
Input.action_release("ship_mini_boost")
check(is_equal_approx(player.energy,50.0-normal_boost_cost*0.5), "Actual atomic boost consumes half energy")
check(ORBS.apply(progress,inventory,engine,"booster"), "Free booster stacks by precedence")
check(is_zero_approx(player.get_mini_boost_energy_cost()), "Free booster remains free")
check(ORBS.apply(progress,inventory,shield,"polarization"), "Polarization applied")
player.is_docked = false
player.absorption = Vector3.ZERO
player.max_energy = 10000.0
player.energy = 0.0
player.max_shields = 1000.0
player.shields = 1000.0
for attempt in 100:
player.take_damage(1.0)
if player.shields < 1000.0: break
check(is_equal_approx(player.energy,1.0), "Polarization cannot turn a tiny hit into excess energy")
player.energy = player.max_energy - 1.0
player.shields = 1000.0
for attempt in 100:
player.take_damage(1.0)
if player.shields < 1000.0: break
check(is_equal_approx(player.energy,player.max_energy), "Polarization respects generator capacity")
player.shields = 0.0
player.energy = 0.0
player.hull = 10000.0
player.take_damage(1.0)
check(is_zero_approx(player.energy), "Hull-only hit gives no polarization energy")
player.is_docked = true
check(ORBS.apply(progress,inventory,weapon,"replication"), "Replication applied")
var copy: ItemInstance = progress.freighter_storage.back()
check(copy != weapon and copy.definition == weapon.definition, "Replica is distinct equipment")
check(copy.get_damage_range() == weapon.get_damage_range() and copy.special_orbs == weapon.special_orbs, "Replica preserves rolls and orbs")
copy.additional_modifiers[0].value = 1.0
copy.special_orbs.clear()
check(is_equal_approx(weapon.additional_modifiers[0].value,25.0) and weapon.special_orbs.size()==1, "Replica changes cannot mutate original")
var missile := GENERATOR.generate_for_definition(preload("res://resources/items/missile_launcher.tres"),1)
missile.additional_modifiers = [addon]
check(is_equal_approx(inventory.get_missile_cooldown(missile),4.0), "Attack speed affects missiles")
addon.value = 0.0
for seed_value in 100:
var no_rare := LOOT.roll(120,true,seed_value,1.0,0.05)
var rare := LOOT.roll(120,true,seed_value,1.0,0.049)
check(no_rare.size()==2 and no_rare[1].orb != "replication", "Replication excluded from guaranteed pool")
check(rare.size()==3 and rare[2].orb == "replication", "Separate five percent replication drop")
check(LOOT.roll(120,false,1,1.0,0.0).is_empty(), "Regular enemies cannot drop replication")
return failures