130 lines
6.9 KiB
GDScript
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
|