Zmiany w skilach
This commit is contained in:
parent
2a57f0face
commit
897123c7c8
@ -71,6 +71,13 @@ theme_override_font_sizes/font_size = 12
|
|||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
text = "KUP 100 AMUNICJI — 100 KREDYTÓW"
|
text = "KUP 100 AMUNICJI — 100 KREDYTÓW"
|
||||||
|
|
||||||
|
[node name="ResetSkillsButton" type="Button" parent="Center/Panel/Contents"]
|
||||||
|
custom_minimum_size = Vector2(0, 36)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
focus_mode = 0
|
||||||
|
text = "RESET SKILLI — 100 KREDYTÓW"
|
||||||
|
|
||||||
[node name="NextButton" type="Button" parent="Center/Panel/Contents"]
|
[node name="NextButton" type="Button" parent="Center/Panel/Contents"]
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|||||||
@ -3,7 +3,8 @@ extends RefCounted
|
|||||||
const XP_BASE := 100
|
const XP_BASE := 100
|
||||||
const XP_PER_LEVEL := 50
|
const XP_PER_LEVEL := 50
|
||||||
const LEARNING_POINTS_PER_LEVEL := 1
|
const LEARNING_POINTS_PER_LEVEL := 1
|
||||||
const MAX_SKILL_RANK := 100
|
const MAX_SKILL_RANK := 20
|
||||||
|
const SKILL_RESET_COST_PER_LEVEL := 100
|
||||||
const BASE_CRITICAL_MULTIPLIER := 2.0
|
const BASE_CRITICAL_MULTIPLIER := 2.0
|
||||||
const KILL_XP := [20, 50, 150]
|
const KILL_XP := [20, 50, 150]
|
||||||
const KILL_CREDITS := [15, 40, 120]
|
const KILL_CREDITS := [15, 40, 120]
|
||||||
@ -12,11 +13,24 @@ const SKILL_NAMES := {
|
|||||||
"mechanic": "Mechanik", "sharp_eye": "Celne oko",
|
"mechanic": "Mechanik", "sharp_eye": "Celne oko",
|
||||||
}
|
}
|
||||||
const SKILL_HELP := {
|
const SKILL_HELP := {
|
||||||
"piloting": "Każdy poziom: +1 punkt procentowy szansy na unik. Unik anuluje całe trafienie.",
|
"piloting": "Malejące korzyści: 37,5% × punkty / (punkty + 10). Maksymalnie 25% uniku przy 20 punktach. Unik anuluje całe trafienie.",
|
||||||
"gunner": "Każdy poziom: +1% obrażeń wszystkich broni.",
|
"gunner": "Każdy punkt: +2% obrażeń wszystkich broni. Maksymalnie +40% przy 20 punktach.",
|
||||||
"mechanic": "Każdy poziom: otrzymywane obrażenia mniejsze o 1% (przed tarczami i kadłubem).",
|
"mechanic": "Malejące korzyści: 50% × punkty / (punkty + 10). Maksymalnie 33,3% redukcji przy 20 punktach, przed tarczami i kadłubem.",
|
||||||
"sharp_eye": "Każdy poziom: +1 punkt procentowy szansy na trafienie krytyczne.",
|
"sharp_eye": "Każdy punkt: +1 punkt procentowy szansy na trafienie krytyczne. Maksymalnie +20 przy 20 punktach.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func skill_bonus(skill: String, rank: int) -> float:
|
||||||
|
var points := float(clampi(rank, 0, MAX_SKILL_RANK))
|
||||||
|
match skill:
|
||||||
|
"piloting":
|
||||||
|
return 0.375 * points / (points + 10.0)
|
||||||
|
"mechanic":
|
||||||
|
return 0.5 * points / (points + 10.0)
|
||||||
|
"gunner":
|
||||||
|
return 0.02 * points
|
||||||
|
"sharp_eye":
|
||||||
|
return 0.01 * points
|
||||||
|
return 0.0
|
||||||
|
|
||||||
static func xp_required(level: int) -> int:
|
static func xp_required(level: int) -> int:
|
||||||
return XP_BASE + XP_PER_LEVEL * (maxi(1, level) - 1)
|
return XP_BASE + XP_PER_LEVEL * (maxi(1, level) - 1)
|
||||||
|
|||||||
@ -18,6 +18,43 @@ func get_xp_required() -> int:
|
|||||||
func get_skill_rank(skill: String) -> int:
|
func get_skill_rank(skill: String) -> int:
|
||||||
return clampi(int(skills.get(skill, 0)), 0, BALANCE.MAX_SKILL_RANK)
|
return clampi(int(skills.get(skill, 0)), 0, BALANCE.MAX_SKILL_RANK)
|
||||||
|
|
||||||
|
func get_skill_bonus(skill: String) -> float:
|
||||||
|
return BALANCE.skill_bonus(skill, get_skill_rank(skill))
|
||||||
|
|
||||||
|
func normalize_skill_ranks() -> void:
|
||||||
|
var changed_ranks := false
|
||||||
|
for skill in BALANCE.SKILL_NAMES:
|
||||||
|
var previous := maxi(0, int(skills.get(skill, 0)))
|
||||||
|
var rank := mini(previous, BALANCE.MAX_SKILL_RANK)
|
||||||
|
learning_points += previous - rank
|
||||||
|
if int(skills.get(skill, 0)) != rank:
|
||||||
|
changed_ranks = true
|
||||||
|
skills[skill] = rank
|
||||||
|
if changed_ranks:
|
||||||
|
emit_changed()
|
||||||
|
|
||||||
|
func get_spent_learning_points() -> int:
|
||||||
|
var spent := 0
|
||||||
|
for skill in BALANCE.SKILL_NAMES:
|
||||||
|
spent += maxi(0, int(skills.get(skill, 0)))
|
||||||
|
return spent
|
||||||
|
|
||||||
|
func get_skill_reset_cost() -> int:
|
||||||
|
return BALANCE.SKILL_RESET_COST_PER_LEVEL * maxi(1, level)
|
||||||
|
|
||||||
|
func can_reset_skills() -> bool:
|
||||||
|
return get_spent_learning_points() > 0 and credits >= get_skill_reset_cost()
|
||||||
|
|
||||||
|
func reset_skills() -> bool:
|
||||||
|
if not can_reset_skills():
|
||||||
|
return false
|
||||||
|
credits -= get_skill_reset_cost()
|
||||||
|
learning_points += get_spent_learning_points()
|
||||||
|
for skill in BALANCE.SKILL_NAMES:
|
||||||
|
skills[skill] = 0
|
||||||
|
emit_changed()
|
||||||
|
return true
|
||||||
|
|
||||||
func can_train(skill: String) -> bool:
|
func can_train(skill: String) -> bool:
|
||||||
return skills.has(skill) and learning_points > 0 and get_skill_rank(skill) < BALANCE.MAX_SKILL_RANK
|
return skills.has(skill) and learning_points > 0 and get_skill_rank(skill) < BALANCE.MAX_SKILL_RANK
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ var _thrust_strength := 0.0
|
|||||||
var _engine_phase := 0.0
|
var _engine_phase := 0.0
|
||||||
var _trail_emission_timer := 0.0
|
var _trail_emission_timer := 0.0
|
||||||
var _trail_particles: Array[Dictionary] = []
|
var _trail_particles: Array[Dictionary] = []
|
||||||
|
var _mini_boost := false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_ship = get_parent() as CharacterBody2D
|
_ship = get_parent() as CharacterBody2D
|
||||||
@ -24,7 +25,8 @@ func _process(delta: float) -> void:
|
|||||||
global_position = _ship.global_position
|
global_position = _ship.global_position
|
||||||
global_rotation = _ship.global_rotation
|
global_rotation = _ship.global_rotation
|
||||||
_engine_phase += delta
|
_engine_phase += delta
|
||||||
var thrust_active := Input.is_action_pressed("ship_thrust")
|
_mini_boost = bool(_ship.call("is_mini_boosting"))
|
||||||
|
var thrust_active := Input.is_action_pressed("ship_thrust") or _mini_boost
|
||||||
if bool(_ship.get("_is_destroyed")):
|
if bool(_ship.get("_is_destroyed")):
|
||||||
thrust_active = false
|
thrust_active = false
|
||||||
var target_strength := 1.0 if thrust_active else 0.0
|
var target_strength := 1.0 if thrust_active else 0.0
|
||||||
@ -52,6 +54,7 @@ func _spawn_trail_particle() -> void:
|
|||||||
"age": 0.0,
|
"age": 0.0,
|
||||||
"life": life,
|
"life": life,
|
||||||
"size": randf_range(1.5, 3.0),
|
"size": randf_range(1.5, 3.0),
|
||||||
|
"boost": _mini_boost,
|
||||||
})
|
})
|
||||||
|
|
||||||
func _update_trail_particles(delta: float) -> void:
|
func _update_trail_particles(delta: float) -> void:
|
||||||
@ -69,7 +72,10 @@ func _draw() -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
var pulse := 0.82 + 0.18 * sin(_engine_phase * 25.0)
|
var pulse := 0.82 + 0.18 * sin(_engine_phase * 25.0)
|
||||||
var length := (28.0 + pulse * 14.0) * _thrust_strength
|
var length := (28.0 + pulse * 14.0) * _thrust_strength * (1.35 if _mini_boost else 1.0)
|
||||||
|
var outer_color := Color(1.0, 0.28, 0.03) if _mini_boost else Color(0.04, 0.58, 1.0)
|
||||||
|
var inner_color := Color(1.0, 0.62, 0.12) if _mini_boost else Color(0.24, 0.9, 1.0)
|
||||||
|
var core_color := Color(1.0, 0.92, 0.7) if _mini_boost else Color(0.88, 0.98, 1.0)
|
||||||
var base_y := 22.0
|
var base_y := 22.0
|
||||||
var outer_tip := base_y + length
|
var outer_tip := base_y + length
|
||||||
var inner_tip := base_y + length * 0.76
|
var inner_tip := base_y + length * 0.76
|
||||||
@ -78,15 +84,15 @@ func _draw() -> void:
|
|||||||
draw_colored_polygon(PackedVector2Array([
|
draw_colored_polygon(PackedVector2Array([
|
||||||
Vector2(-5.2, base_y), Vector2(-3.2, base_y + 6.0), Vector2(0.0, outer_tip),
|
Vector2(-5.2, base_y), Vector2(-3.2, base_y + 6.0), Vector2(0.0, outer_tip),
|
||||||
Vector2(3.2, base_y + 6.0), Vector2(5.2, base_y),
|
Vector2(3.2, base_y + 6.0), Vector2(5.2, base_y),
|
||||||
]), Color(0.04, 0.58, 1.0, 0.48 * _thrust_strength))
|
]), Color(outer_color, 0.48 * _thrust_strength))
|
||||||
draw_colored_polygon(PackedVector2Array([
|
draw_colored_polygon(PackedVector2Array([
|
||||||
Vector2(-2.8, base_y), Vector2(-1.8, base_y + 4.0), Vector2(0.0, inner_tip),
|
Vector2(-2.8, base_y), Vector2(-1.8, base_y + 4.0), Vector2(0.0, inner_tip),
|
||||||
Vector2(1.8, base_y + 4.0), Vector2(2.8, base_y),
|
Vector2(1.8, base_y + 4.0), Vector2(2.8, base_y),
|
||||||
]), Color(0.24, 0.9, 1.0, 0.78 * _thrust_strength))
|
]), Color(inner_color, 0.78 * _thrust_strength))
|
||||||
draw_colored_polygon(PackedVector2Array([
|
draw_colored_polygon(PackedVector2Array([
|
||||||
Vector2(-1.0, base_y), Vector2(0.0, core_tip), Vector2(1.0, base_y),
|
Vector2(-1.0, base_y), Vector2(0.0, core_tip), Vector2(1.0, base_y),
|
||||||
]), Color(0.88, 0.98, 1.0, 0.92 * _thrust_strength))
|
]), Color(core_color, 0.92 * _thrust_strength))
|
||||||
draw_circle(Vector2(0.0, base_y), 2.3, Color(0.42, 0.88, 1.0, 0.82 * _thrust_strength))
|
draw_circle(Vector2(0.0, base_y), 2.3, Color(inner_color, 0.82 * _thrust_strength))
|
||||||
|
|
||||||
func _draw_trail() -> void:
|
func _draw_trail() -> void:
|
||||||
for particle in _trail_particles:
|
for particle in _trail_particles:
|
||||||
@ -98,5 +104,7 @@ func _draw_trail() -> void:
|
|||||||
var particle_size := float(particle["size"])
|
var particle_size := float(particle["size"])
|
||||||
var current_local := to_local(current_position)
|
var current_local := to_local(current_position)
|
||||||
var tail_local := to_local(current_position - particle_velocity.normalized() * particle_size * 3.5)
|
var tail_local := to_local(current_position - particle_velocity.normalized() * particle_size * 3.5)
|
||||||
draw_line(tail_local, current_local, Color(0.22, 0.78, 1.0, fade * 0.52), particle_size * 0.72, true)
|
var trail_color := Color(1.0, 0.42, 0.06) if bool(particle.get("boost", false)) else Color(0.22, 0.78, 1.0)
|
||||||
draw_circle(current_local, particle_size * fade, Color(0.62, 0.94, 1.0, fade * 0.72))
|
var particle_color := Color(1.0, 0.76, 0.3) if bool(particle.get("boost", false)) else Color(0.62, 0.94, 1.0)
|
||||||
|
draw_line(tail_local, current_local, Color(trail_color, fade * 0.52), particle_size * 0.72, true)
|
||||||
|
draw_circle(current_local, particle_size * fade, Color(particle_color, fade * 0.72))
|
||||||
|
|||||||
@ -72,6 +72,7 @@ func _process(_delta: float) -> void:
|
|||||||
var to_planet := goal.global_position - player.global_position
|
var to_planet := goal.global_position - player.global_position
|
||||||
debug_info.text = """STEROWANIE
|
debug_info.text = """STEROWANIE
|
||||||
W: ciąg S: hamulec / wsteczny
|
W: ciąg S: hamulec / wsteczny
|
||||||
|
Tab: mini boost (+100%% prędkości, 20 energii/s)
|
||||||
Q / E: strafe A / D: obrót
|
Q / E: strafe A / D: obrót
|
||||||
LPM / PPM: broń 1 / broń 2
|
LPM / PPM: broń 1 / broń 2
|
||||||
1–6: aktywne rozszerzenia
|
1–6: aktywne rozszerzenia
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const COMBAT_ROLLS := preload("res://scripts/game/combat_rolls.gd")
|
|||||||
const CAPTAIN_BALANCE := preload("res://scripts/captain/captain_balance.gd")
|
const CAPTAIN_BALANCE := preload("res://scripts/captain/captain_balance.gd")
|
||||||
const SHIP_CATALOG := preload("res://scripts/game/ship_catalog.gd")
|
const SHIP_CATALOG := preload("res://scripts/game/ship_catalog.gd")
|
||||||
const COMBAT_FLASH := preload("res://scripts/game/combat_flash.gd")
|
const COMBAT_FLASH := preload("res://scripts/game/combat_flash.gd")
|
||||||
|
const MINI_BOOST_ENERGY_PER_SECOND := 20.0
|
||||||
|
|
||||||
@export var max_speed := 76.0
|
@export var max_speed := 76.0
|
||||||
@export var reverse_max_speed := 210.0
|
@export var reverse_max_speed := 210.0
|
||||||
@ -51,6 +52,9 @@ var _boost_speed := 0.0
|
|||||||
var _boost_target := 0.0
|
var _boost_target := 0.0
|
||||||
var _boost_acceleration := 0.0
|
var _boost_acceleration := 0.0
|
||||||
var _boost_velocity_acceleration := 0.0
|
var _boost_velocity_acceleration := 0.0
|
||||||
|
var _mini_boost_active := false
|
||||||
|
var _mini_boost_exhausted := false
|
||||||
|
var _mini_boost_coast_speed := 0.0
|
||||||
@onready var _shield_visual: Node2D = $ShieldVisual
|
@onready var _shield_visual: Node2D = $ShieldVisual
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
@ -84,6 +88,7 @@ func _ready() -> void:
|
|||||||
for index in 6:
|
for index in 6:
|
||||||
_ensure_input_action(StringName("ability_%d" % (index + 1)), (KEY_1 + index) as Key)
|
_ensure_input_action(StringName("ability_%d" % (index + 1)), (KEY_1 + index) as Key)
|
||||||
_ensure_input_action(&"ship_thrust", KEY_W)
|
_ensure_input_action(&"ship_thrust", KEY_W)
|
||||||
|
_ensure_input_action(&"ship_mini_boost", KEY_TAB)
|
||||||
_ensure_input_action(&"ship_brake", KEY_S)
|
_ensure_input_action(&"ship_brake", KEY_S)
|
||||||
_ensure_input_action(&"ship_turn_left", KEY_A)
|
_ensure_input_action(&"ship_turn_left", KEY_A)
|
||||||
_ensure_input_action(&"ship_turn_right", KEY_D)
|
_ensure_input_action(&"ship_turn_right", KEY_D)
|
||||||
@ -144,16 +149,16 @@ func take_damage(amount: float) -> void:
|
|||||||
_destroy_ship()
|
_destroy_ship()
|
||||||
|
|
||||||
func get_evasion_chance() -> float:
|
func get_evasion_chance() -> float:
|
||||||
return GameState.captain.get_skill_rank("piloting") / 100.0
|
return GameState.captain.get_skill_bonus("piloting")
|
||||||
|
|
||||||
func get_damage_reduction() -> float:
|
func get_damage_reduction() -> float:
|
||||||
return GameState.captain.get_skill_rank("mechanic") / 100.0
|
return GameState.captain.get_skill_bonus("mechanic")
|
||||||
|
|
||||||
func get_weapon_damage_multiplier() -> float:
|
func get_weapon_damage_multiplier() -> float:
|
||||||
return damage_multiplier * (1.0 + GameState.captain.get_skill_rank("gunner") / 100.0)
|
return damage_multiplier * (1.0 + GameState.captain.get_skill_bonus("gunner"))
|
||||||
|
|
||||||
func get_critical_chance() -> float:
|
func get_critical_chance() -> float:
|
||||||
return clampf(critical_chance_bonus + GameState.captain.get_skill_rank("sharp_eye") / 100.0, 0.0, 1.0)
|
return clampf(critical_chance_bonus + GameState.captain.get_skill_bonus("sharp_eye"), 0.0, 1.0)
|
||||||
|
|
||||||
func get_critical_multiplier() -> float:
|
func get_critical_multiplier() -> float:
|
||||||
return CAPTAIN_BALANCE.BASE_CRITICAL_MULTIPLIER * (1.0 + maxf(0.0, critical_damage_bonus))
|
return CAPTAIN_BALANCE.BASE_CRITICAL_MULTIPLIER * (1.0 + maxf(0.0, critical_damage_bonus))
|
||||||
@ -201,10 +206,19 @@ func _restart_level_after_destruction() -> void:
|
|||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
if _is_destroyed or is_docked:
|
if _is_destroyed or is_docked:
|
||||||
|
_mini_boost_active = false
|
||||||
return
|
return
|
||||||
_contact_damage_timer = maxf(0.0, _contact_damage_timer - delta)
|
_contact_damage_timer = maxf(0.0, _contact_damage_timer - delta)
|
||||||
energy = minf(max_energy, energy + (energy_regeneration + get_solar_regeneration()) * delta)
|
energy = minf(max_energy, energy + (energy_regeneration + get_solar_regeneration()) * delta)
|
||||||
var was_boosting := is_boosting()
|
var was_mini_boosting := _mini_boost_active
|
||||||
|
_update_mini_boost(delta)
|
||||||
|
if _mini_boost_active:
|
||||||
|
_mini_boost_coast_speed = 0.0
|
||||||
|
else:
|
||||||
|
if was_mini_boosting:
|
||||||
|
_mini_boost_coast_speed = maxf(0.0, velocity.length() - max_speed)
|
||||||
|
_mini_boost_coast_speed = move_toward(_mini_boost_coast_speed, 0.0, space_drag * delta)
|
||||||
|
var was_boosting := _boost_target > 0.0 or _boost_speed > 0.0
|
||||||
if _boost_target > 0.0:
|
if _boost_target > 0.0:
|
||||||
_boost_speed = move_toward(_boost_speed, _boost_target, _boost_acceleration * delta)
|
_boost_speed = move_toward(_boost_speed, _boost_target, _boost_acceleration * delta)
|
||||||
velocity = velocity.move_toward(Vector2.UP.rotated(rotation) * (max_speed + _boost_target), _boost_velocity_acceleration * delta)
|
velocity = velocity.move_toward(Vector2.UP.rotated(rotation) * (max_speed + _boost_target), _boost_velocity_acceleration * delta)
|
||||||
@ -240,11 +254,12 @@ func _physics_process(delta: float) -> void:
|
|||||||
var reverse_speed := velocity.dot(forward)
|
var reverse_speed := velocity.dot(forward)
|
||||||
if reverse_speed < -reverse_max_speed:
|
if reverse_speed < -reverse_max_speed:
|
||||||
velocity += forward * (-reverse_max_speed - reverse_speed)
|
velocity += forward * (-reverse_max_speed - reverse_speed)
|
||||||
elif Input.is_action_pressed("ship_thrust"):
|
elif Input.is_action_pressed("ship_thrust") or _mini_boost_active:
|
||||||
velocity += forward * thrust_acceleration * delta
|
velocity += forward * thrust_acceleration * (2.0 if _mini_boost_active else 1.0) * delta
|
||||||
var forward_speed := velocity.dot(forward)
|
var forward_speed := velocity.dot(forward)
|
||||||
if forward_speed > max_speed and not was_boosting:
|
var forward_limit := _get_allowed_speed()
|
||||||
velocity += forward * (max_speed - forward_speed)
|
if forward_speed > forward_limit and not was_boosting:
|
||||||
|
velocity += forward * (forward_limit - forward_speed)
|
||||||
|
|
||||||
var strafe_input := Input.get_axis("ship_strafe_left", "ship_strafe_right")
|
var strafe_input := Input.get_axis("ship_strafe_left", "ship_strafe_right")
|
||||||
var right := Vector2.RIGHT.rotated(rotation)
|
var right := Vector2.RIGHT.rotated(rotation)
|
||||||
@ -255,6 +270,7 @@ func _physics_process(delta: float) -> void:
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
not was_boosting
|
not was_boosting
|
||||||
|
and not _mini_boost_active
|
||||||
and
|
and
|
||||||
not Input.is_action_pressed("ship_brake")
|
not Input.is_action_pressed("ship_brake")
|
||||||
and not Input.is_action_pressed("ship_thrust")
|
and not Input.is_action_pressed("ship_thrust")
|
||||||
@ -269,13 +285,38 @@ func _physics_process(delta: float) -> void:
|
|||||||
|
|
||||||
func _clamp_velocity_to_max_speed() -> void:
|
func _clamp_velocity_to_max_speed() -> void:
|
||||||
var speed_squared := velocity.length_squared()
|
var speed_squared := velocity.length_squared()
|
||||||
var allowed_speed := max_speed + _boost_speed
|
var allowed_speed := _get_allowed_speed()
|
||||||
var max_speed_squared := allowed_speed * allowed_speed
|
var max_speed_squared := allowed_speed * allowed_speed
|
||||||
if speed_squared > max_speed_squared:
|
if speed_squared > max_speed_squared:
|
||||||
velocity = velocity.normalized() * allowed_speed
|
velocity = velocity.normalized() * allowed_speed
|
||||||
|
|
||||||
func is_boosting() -> bool:
|
func is_boosting() -> bool:
|
||||||
return _boost_target > 0.0 or _boost_speed > 0.0
|
return _boost_target > 0.0 or _boost_speed > 0.0 or _mini_boost_active
|
||||||
|
|
||||||
|
func is_mini_boosting() -> bool:
|
||||||
|
return _mini_boost_active and not _is_destroyed and not is_docked
|
||||||
|
|
||||||
|
func _get_allowed_speed() -> float:
|
||||||
|
return maxf(max_speed + _boost_speed, max_speed * 2.0 if _mini_boost_active else max_speed + _mini_boost_coast_speed)
|
||||||
|
|
||||||
|
func _update_mini_boost(delta: float) -> void:
|
||||||
|
_mini_boost_active = false
|
||||||
|
if not Input.is_action_pressed("ship_mini_boost"):
|
||||||
|
_mini_boost_exhausted = false
|
||||||
|
return
|
||||||
|
if _mini_boost_exhausted or Input.is_action_pressed("ship_brake"):
|
||||||
|
return
|
||||||
|
var cost := MINI_BOOST_ENERGY_PER_SECOND * delta
|
||||||
|
if energy < cost or energy <= 0.0:
|
||||||
|
_mini_boost_exhausted = true
|
||||||
|
return
|
||||||
|
energy = maxf(0.0, energy - cost)
|
||||||
|
_mini_boost_active = true
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event is InputEventKey and event.is_action("ship_mini_boost"):
|
||||||
|
# Tab controls the engine rather than moving focus between HUD buttons.
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
func get_solar_regeneration() -> float:
|
func get_solar_regeneration() -> float:
|
||||||
if _is_destroyed or is_docked or not is_instance_valid(charging_star):
|
if _is_destroyed or is_docked or not is_instance_valid(charging_star):
|
||||||
|
|||||||
@ -24,7 +24,7 @@ func _process(delta: float) -> void:
|
|||||||
if stream == null or not is_instance_valid(_ship):
|
if stream == null or not is_instance_valid(_ship):
|
||||||
return
|
return
|
||||||
|
|
||||||
var thrust_active := Input.is_action_pressed("ship_thrust")
|
var thrust_active := Input.is_action_pressed("ship_thrust") or bool(_ship.call("is_mini_boosting"))
|
||||||
if bool(_ship.get("_is_destroyed")):
|
if bool(_ship.get("_is_destroyed")):
|
||||||
thrust_active = false
|
thrust_active = false
|
||||||
var target_volume := THRUST_VOLUME_DB if thrust_active else SILENT_VOLUME_DB
|
var target_volume := THRUST_VOLUME_DB if thrust_active else SILENT_VOLUME_DB
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
extends Resource
|
extends Resource
|
||||||
class_name ItemInstance
|
class_name ItemInstance
|
||||||
|
|
||||||
|
const ENERGY_REGENERATION_BONUS_MULTIPLIER := 0.5
|
||||||
|
|
||||||
@export var definition: ItemDefinition
|
@export var definition: ItemDefinition
|
||||||
@export_range(1, 1000, 1) var item_level: int = 1
|
@export_range(1, 1000, 1) var item_level: int = 1
|
||||||
@export var additional_modifiers: Array[ItemStatModifier] = []
|
@export var additional_modifiers: Array[ItemStatModifier] = []
|
||||||
@ -22,7 +24,7 @@ func get_stat_total(stat_key: String) -> float:
|
|||||||
for modifier in additional_modifiers:
|
for modifier in additional_modifiers:
|
||||||
if modifier and modifier.stat_key == stat_key:
|
if modifier and modifier.stat_key == stat_key:
|
||||||
total += modifier.value
|
total += modifier.value
|
||||||
return total
|
return total * ENERGY_REGENERATION_BONUS_MULTIPLIER if stat_key == "energy_regeneration" else total
|
||||||
|
|
||||||
func get_damage_range() -> Vector2:
|
func get_damage_range() -> Vector2:
|
||||||
if definition == null or definition.category != ItemDefinition.Category.WEAPON:
|
if definition == null or definition.category != ItemDefinition.Category.WEAPON:
|
||||||
@ -108,7 +110,7 @@ func get_tooltip_text() -> String:
|
|||||||
primary_text = definition.active_ability.description
|
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]
|
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:
|
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))
|
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:
|
if definition.category == ItemDefinition.Category.WEAPON:
|
||||||
lines.append("Każdy strzał / impuls losuje obrażenia z tego zakresu.")
|
lines.append("Każdy strzał / impuls losuje obrażenia z tego zakresu.")
|
||||||
if definition.weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN:
|
if definition.weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN:
|
||||||
|
|||||||
@ -8,4 +8,5 @@ class_name ItemStatModifier
|
|||||||
func get_display_text() -> String:
|
func get_display_text() -> String:
|
||||||
var label := affix.display_name if affix else ItemInstance.stat_name(stat_key)
|
var label := affix.display_name if affix else ItemInstance.stat_name(stat_key)
|
||||||
var suffix := affix.unit if affix else ("%" if stat_key.ends_with("_percent") else "/s")
|
var suffix := affix.unit if affix else ("%" if stat_key.ends_with("_percent") else "/s")
|
||||||
return "%s: %s%.2f %s" % [label, "+" if value >= 0.0 else "", value, suffix]
|
var displayed_value := value * ItemInstance.ENERGY_REGENERATION_BONUS_MULTIPLIER if stat_key == "energy_regeneration" else value
|
||||||
|
return "%s: %s%.2f %s" % [label, "+" if displayed_value >= 0.0 else "", displayed_value, suffix]
|
||||||
|
|||||||
@ -17,6 +17,7 @@ var _buttons: Dictionary = {}
|
|||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_captain = GameState.captain
|
_captain = GameState.captain
|
||||||
|
_captain.normalize_skill_ranks()
|
||||||
for skill in BALANCE.SKILL_NAMES:
|
for skill in BALANCE.SKILL_NAMES:
|
||||||
var row := HBoxContainer.new()
|
var row := HBoxContainer.new()
|
||||||
row.name = skill
|
row.name = skill
|
||||||
@ -28,13 +29,13 @@ func _ready() -> void:
|
|||||||
title.add_theme_font_size_override("font_size", 11)
|
title.add_theme_font_size_override("font_size", 11)
|
||||||
row.add_child(title)
|
row.add_child(title)
|
||||||
var rank := Label.new()
|
var rank := Label.new()
|
||||||
rank.custom_minimum_size.x = 22
|
rank.custom_minimum_size.x = 40
|
||||||
rank.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
rank.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
rank.add_theme_font_size_override("font_size", 11)
|
rank.add_theme_font_size_override("font_size", 11)
|
||||||
row.add_child(rank)
|
row.add_child(rank)
|
||||||
_rank_labels[skill] = rank
|
_rank_labels[skill] = rank
|
||||||
var effect := Label.new()
|
var effect := Label.new()
|
||||||
effect.custom_minimum_size.x = 58
|
effect.custom_minimum_size.x = 74
|
||||||
effect.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
effect.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
effect.add_theme_font_size_override("font_size", 10)
|
effect.add_theme_font_size_override("font_size", 10)
|
||||||
row.add_child(effect)
|
row.add_child(effect)
|
||||||
@ -65,8 +66,11 @@ func _refresh() -> void:
|
|||||||
points_label.text = "Punkty: %d" % _captain.learning_points
|
points_label.text = "Punkty: %d" % _captain.learning_points
|
||||||
for skill in _buttons:
|
for skill in _buttons:
|
||||||
var rank := _captain.get_skill_rank(skill)
|
var rank := _captain.get_skill_rank(skill)
|
||||||
_rank_labels[skill].text = str(rank)
|
_rank_labels[skill].text = "%d/%d" % [rank, BALANCE.MAX_SKILL_RANK]
|
||||||
var suffix: String = {"piloting": "unik", "gunner": "DMG", "mechanic": "redukcji", "sharp_eye": "kryt"}[skill]
|
var suffix: String = {"piloting": "unik", "gunner": "DMG", "mechanic": "red.", "sharp_eye": "kryt"}[skill]
|
||||||
_effect_labels[skill].text = "%d%% %s" % [rank, suffix]
|
var bonus := _captain.get_skill_bonus(skill)
|
||||||
|
_effect_labels[skill].text = "%.1f%% %s" % [bonus * 100.0, suffix]
|
||||||
|
var next_bonus := BALANCE.skill_bonus(skill, rank + 1)
|
||||||
|
_buttons[skill].tooltip_text = BALANCE.SKILL_HELP[skill] + ("\nOsiągnięto limit 20 punktów." if rank >= BALANCE.MAX_SKILL_RANK else "\nNastępny punkt: %.1f%% → %.1f%%" % [bonus * 100.0, next_bonus * 100.0])
|
||||||
_buttons[skill].disabled = not _captain.can_train(skill)
|
_buttons[skill].disabled = not _captain.can_train(skill)
|
||||||
critical_label.text = "Krytyk: %.1f%% · ×%.2f\nCzęści: %.1f" % [float(player.call("get_critical_chance")) * 100.0, float(player.call("get_critical_multiplier")), _captain.parts]
|
critical_label.text = "Krytyk: %.1f%% · ×%.2f\nCzęści: %.1f" % [float(player.call("get_critical_chance")) * 100.0, float(player.call("get_critical_multiplier")), _captain.parts]
|
||||||
|
|||||||
@ -4,6 +4,7 @@ signal next_level_requested
|
|||||||
|
|
||||||
@onready var next_button: Button = $Center/Panel/Contents/NextButton
|
@onready var next_button: Button = $Center/Panel/Contents/NextButton
|
||||||
@onready var buy_ammunition_button: Button = $Center/Panel/Contents/BuyAmmunitionButton
|
@onready var buy_ammunition_button: Button = $Center/Panel/Contents/BuyAmmunitionButton
|
||||||
|
@onready var reset_skills_button: Button = $Center/Panel/Contents/ResetSkillsButton
|
||||||
@onready var services_label: Label = $Center/Panel/Contents/Services
|
@onready var services_label: Label = $Center/Panel/Contents/Services
|
||||||
@onready var status_label: Label = $Center/Panel/Contents/Status
|
@onready var status_label: Label = $Center/Panel/Contents/Status
|
||||||
@onready var panel: PanelContainer = $Center/Panel
|
@onready var panel: PanelContainer = $Center/Panel
|
||||||
@ -15,6 +16,7 @@ var _open_tween: Tween
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
next_button.pressed.connect(func() -> void: next_level_requested.emit())
|
next_button.pressed.connect(func() -> void: next_level_requested.emit())
|
||||||
buy_ammunition_button.pressed.connect(_buy_ammunition)
|
buy_ammunition_button.pressed.connect(_buy_ammunition)
|
||||||
|
reset_skills_button.pressed.connect(_reset_skills)
|
||||||
_inventory = get_tree().get_first_node_in_group("ship_inventory") as ShipInventory
|
_inventory = get_tree().get_first_node_in_group("ship_inventory") as ShipInventory
|
||||||
if _inventory:
|
if _inventory:
|
||||||
_inventory.ammunition_changed.connect(_refresh_ammunition_service)
|
_inventory.ammunition_changed.connect(_refresh_ammunition_service)
|
||||||
@ -59,6 +61,9 @@ func _position_at_planet() -> void:
|
|||||||
position = Vector2(clampf(target.x, 8.0, maxf(8.0, screen_right - size.x)), clampf(target.y, 8.0, maxf(8.0, viewport_size.y - size.y - 80.0)))
|
position = Vector2(clampf(target.x, 8.0, maxf(8.0, screen_right - size.x)), clampf(target.y, 8.0, maxf(8.0, viewport_size.y - size.y - 80.0)))
|
||||||
|
|
||||||
func _refresh_ammunition_service() -> void:
|
func _refresh_ammunition_service() -> void:
|
||||||
|
reset_skills_button.disabled = not GameState.captain.can_reset_skills()
|
||||||
|
reset_skills_button.text = "RESET SKILLI — %d KREDYTÓW" % GameState.captain.get_skill_reset_cost()
|
||||||
|
reset_skills_button.tooltip_text = "Zwraca wszystkie %d wydanych punktów nauki. Koszt: 100 kredytów × poziom kapitana." % GameState.captain.get_spent_learning_points()
|
||||||
buy_ammunition_button.disabled = _inventory == null or not _inventory.can_buy_ammunition()
|
buy_ammunition_button.disabled = _inventory == null or not _inventory.can_buy_ammunition()
|
||||||
if _inventory:
|
if _inventory:
|
||||||
buy_ammunition_button.text = "KUP %d AMUNICJI — %d KREDYTÓW" % [ShipInventory.AMMUNITION_BUY_AMOUNT, _inventory.get_ammunition_buy_cost()]
|
buy_ammunition_button.text = "KUP %d AMUNICJI — %d KREDYTÓW" % [ShipInventory.AMMUNITION_BUY_AMOUNT, _inventory.get_ammunition_buy_cost()]
|
||||||
@ -67,3 +72,7 @@ func _refresh_ammunition_service() -> void:
|
|||||||
func _buy_ammunition() -> void:
|
func _buy_ammunition() -> void:
|
||||||
if visible and _inventory:
|
if visible and _inventory:
|
||||||
_inventory.buy_ammunition()
|
_inventory.buy_ammunition()
|
||||||
|
|
||||||
|
func _reset_skills() -> void:
|
||||||
|
if visible:
|
||||||
|
GameState.captain.reset_skills()
|
||||||
|
|||||||
72
tests/abilities/mini_boost_test.gd
Normal file
72
tests/abilities/mini_boost_test.gd
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
const PLAYER := preload("res://scenes/game/player_ship.tscn")
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
GameState.start_new_run()
|
||||||
|
var ship := PLAYER.instantiate() as CharacterBody2D
|
||||||
|
add_child(ship)
|
||||||
|
ship.set_physics_process(false)
|
||||||
|
ship.get_node("WeaponController").set_physics_process(false)
|
||||||
|
ship.set("energy_regeneration", 0.0)
|
||||||
|
ship.set("energy", 100.0)
|
||||||
|
var tab := InputEventKey.new()
|
||||||
|
tab.physical_keycode = KEY_TAB
|
||||||
|
assert(InputMap.action_has_event("ship_mini_boost", tab))
|
||||||
|
Input.action_press("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 1.0)
|
||||||
|
assert(ship.call("is_mini_boosting"))
|
||||||
|
assert(is_equal_approx(ship.get("energy"), 80.0))
|
||||||
|
assert(is_equal_approx(ship.velocity.length(), ship.get("max_speed") * 2.0))
|
||||||
|
var engine := ship.get_node("EngineVisual")
|
||||||
|
engine.call("_process", 0.2)
|
||||||
|
assert(engine.get("_mini_boost"))
|
||||||
|
var particles: Array = engine.get("_trail_particles")
|
||||||
|
assert(not particles.is_empty() and particles[0]["boost"])
|
||||||
|
Input.action_release("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting"))
|
||||||
|
var released_speed := ship.velocity.length()
|
||||||
|
assert(released_speed > ship.get("max_speed"))
|
||||||
|
assert(is_equal_approx(released_speed, ship.get("max_speed") * 2.0 - ship.get("space_drag") * 0.1))
|
||||||
|
Input.action_press("ship_thrust")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(is_equal_approx(ship.velocity.length(), released_speed - ship.get("space_drag") * 0.1))
|
||||||
|
Input.action_release("ship_thrust")
|
||||||
|
for frame in 120:
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(ship.velocity.length() <= ship.get("max_speed"))
|
||||||
|
ship.set("energy", 0.0)
|
||||||
|
Input.action_press("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting"))
|
||||||
|
ship.set("energy", 100.0)
|
||||||
|
Input.action_release("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
Input.action_press("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 1.0)
|
||||||
|
ship.set("energy", 0.0)
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting"))
|
||||||
|
assert(is_equal_approx(ship.velocity.length(), ship.get("max_speed") * 2.0 - ship.get("space_drag") * 0.1))
|
||||||
|
ship.set("energy", 20.0)
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting") and ship.get("energy") == 20.0)
|
||||||
|
Input.action_release("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
Input.action_press("ship_mini_boost")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(ship.call("is_mini_boosting") and ship.get("energy") == 18.0)
|
||||||
|
Input.action_press("ship_brake")
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting") and ship.get("energy") == 18.0)
|
||||||
|
Input.action_release("ship_brake")
|
||||||
|
ship.call("start_boost", 2.0, 0.5)
|
||||||
|
ship.set("_boost_speed", ship.get("max_speed") * 2.0)
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(ship.call("_get_allowed_speed") == ship.get("max_speed") * 3.0)
|
||||||
|
ship.set("is_docked", true)
|
||||||
|
ship.call("_physics_process", 0.1)
|
||||||
|
assert(not ship.call("is_mini_boosting"))
|
||||||
|
Input.action_release("ship_mini_boost")
|
||||||
|
print("MINI_BOOST_TEST: PASS (Tab mapping, double speed, energy drain, orange trail, release, exhaustion latch, braking, afterburner and docking)")
|
||||||
1
tests/abilities/mini_boost_test.gd.uid
Normal file
1
tests/abilities/mini_boost_test.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://borjkb545msdf
|
||||||
4
tests/abilities/mini_boost_test.tscn
Normal file
4
tests/abilities/mini_boost_test.tscn
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
[ext_resource type="Script" path="res://tests/abilities/mini_boost_test.gd" id="1"]
|
||||||
|
[node name="MiniBoostTest" type="Node2D"]
|
||||||
|
script = ExtResource("1")
|
||||||
@ -5,6 +5,7 @@ const COMBAT := preload("res://scripts/game/combat_rolls.gd")
|
|||||||
const GAME_SCENE := preload("res://scenes/game/game.tscn")
|
const GAME_SCENE := preload("res://scenes/game/game.tscn")
|
||||||
const GENERATOR := preload("res://scripts/items/item_generator.gd")
|
const GENERATOR := preload("res://scripts/items/item_generator.gd")
|
||||||
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
|
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
|
||||||
|
const BALANCE := preload("res://scripts/captain/captain_balance.gd")
|
||||||
|
|
||||||
class DamageReceiver:
|
class DamageReceiver:
|
||||||
extends Node2D
|
extends Node2D
|
||||||
@ -13,6 +14,7 @@ class DamageReceiver:
|
|||||||
hits.append({"damage": amount, "critical": critical})
|
hits.append({"damage": amount, "critical": critical})
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
_test_skill_balance_and_reset()
|
||||||
var profile := PROGRESS.new()
|
var profile := PROGRESS.new()
|
||||||
assert(not profile.train_skill("gunner"))
|
assert(not profile.train_skill("gunner"))
|
||||||
profile.add_rewards(99, 60)
|
profile.add_rewards(99, 60)
|
||||||
@ -58,20 +60,21 @@ func _ready() -> void:
|
|||||||
assert(panel.get_node("Contents/Header/Level").get("text") == "POZ. 2")
|
assert(panel.get_node("Contents/Header/Level").get("text") == "POZ. 2")
|
||||||
var player := game.get_node("PlayerShip") as CharacterBody2D
|
var player := game.get_node("PlayerShip") as CharacterBody2D
|
||||||
profile.skills["piloting"] = 100
|
profile.skills["piloting"] = 100
|
||||||
var before := Vector2(float(player.get("hull")), float(player.get("shields")))
|
assert(is_equal_approx(float(player.call("get_evasion_chance")), 0.25))
|
||||||
player.call("take_damage", 1000.0)
|
assert(COMBAT.incoming_hit(100, player.call("get_evasion_chance"), 0, 0.0)["evaded"])
|
||||||
assert(before == Vector2(float(player.get("hull")), float(player.get("shields"))))
|
assert(COMBAT.incoming_hit(100, player.call("get_evasion_chance"), 0, 0.25)["damage"] == 100)
|
||||||
profile.skills["piloting"] = 0
|
profile.skills["piloting"] = 0
|
||||||
profile.skills["mechanic"] = 50
|
profile.skills["mechanic"] = 15
|
||||||
player.set("shields", 100.0) # Fixed fixture, independent of selected chassis capacity.
|
player.set("shields", 100.0) # Fixed fixture, independent of selected chassis capacity.
|
||||||
player.call("take_damage", 100.0)
|
player.call("take_damage", 100.0)
|
||||||
assert(float(player.get("shields")) == 50 and float(player.get("hull")) == 100)
|
assert(is_equal_approx(float(player.get("shields")), 30) and float(player.get("hull")) == 100)
|
||||||
player.set("shields", 0.0)
|
player.set("shields", 0.0)
|
||||||
player.call("take_damage", 40.0)
|
player.call("take_damage", 40.0)
|
||||||
assert(float(player.get("hull")) == 80)
|
assert(is_equal_approx(float(player.get("hull")), 72))
|
||||||
profile.skills["mechanic"] = 100
|
profile.skills["mechanic"] = 100
|
||||||
player.call("take_damage", 1000000.0)
|
assert(is_equal_approx(float(player.call("get_damage_reduction")), 1.0 / 3.0))
|
||||||
assert(float(player.get("hull")) == 80)
|
player.call("take_damage", 30.0)
|
||||||
|
assert(is_equal_approx(float(player.get("hull")), 52))
|
||||||
profile.skills["mechanic"] = 0
|
profile.skills["mechanic"] = 0
|
||||||
profile.skills["sharp_eye"] = 1
|
profile.skills["sharp_eye"] = 1
|
||||||
var inventory := player.get_node("ShipInventory") as ShipInventory
|
var inventory := player.get_node("ShipInventory") as ShipInventory
|
||||||
@ -88,20 +91,23 @@ func _ready() -> void:
|
|||||||
assert(inventory.add_item(weapon) and inventory.move_item("cargo", free_slot, "weapon", 0))
|
assert(inventory.add_item(weapon) and inventory.move_item("cargo", free_slot, "weapon", 0))
|
||||||
assert(is_equal_approx(float(player.call("get_critical_chance")), 0.26))
|
assert(is_equal_approx(float(player.call("get_critical_chance")), 0.26))
|
||||||
assert(is_equal_approx(float(player.call("get_critical_multiplier")), 2.4))
|
assert(is_equal_approx(float(player.call("get_critical_multiplier")), 2.4))
|
||||||
assert(is_equal_approx(float(player.call("get_weapon_damage_multiplier")), 1.111))
|
assert(is_equal_approx(float(player.call("get_weapon_damage_multiplier")), 1.122))
|
||||||
profile.skills["sharp_eye"] = 100
|
profile.skills["sharp_eye"] = 20
|
||||||
|
assert(is_equal_approx(float(player.call("get_critical_chance")), 0.45))
|
||||||
|
player.set("critical_chance_bonus", 0.8) # Force this metadata check independently of RNG.
|
||||||
hit = player.call("roll_weapon_hit", weapon)
|
hit = player.call("roll_weapon_hit", weapon)
|
||||||
assert(hit["critical"] and is_equal_approx(hit["damage"], 26.664))
|
assert(hit["critical"] and is_equal_approx(hit["damage"], 26.928))
|
||||||
|
player.set("critical_chance_bonus", 0.25)
|
||||||
var receiver := DamageReceiver.new()
|
var receiver := DamageReceiver.new()
|
||||||
game.add_child(receiver)
|
game.add_child(receiver)
|
||||||
var projectile := PROJECTILE.instantiate()
|
var projectile := PROJECTILE.instantiate()
|
||||||
projectile.call("configure", Vector2.ZERO, Vector2.RIGHT, ItemDefinition.WeaponKind.MISSILE, 400.0, 26.664, 1000.0, null, true)
|
projectile.call("configure", Vector2.ZERO, Vector2.RIGHT, ItemDefinition.WeaponKind.MISSILE, 400.0, 26.928, 1000.0, null, true)
|
||||||
game.add_child(projectile)
|
game.add_child(projectile)
|
||||||
projectile.set_physics_process(false)
|
projectile.set_physics_process(false)
|
||||||
projectile.call("_apply_damage_once", receiver)
|
projectile.call("_apply_damage_once", receiver)
|
||||||
projectile.call("_apply_damage_once", receiver)
|
projectile.call("_apply_damage_once", receiver)
|
||||||
assert(receiver.hits.size() == 1 and receiver.hits[0]["critical"])
|
assert(receiver.hits.size() == 1 and receiver.hits[0]["critical"])
|
||||||
assert(is_equal_approx(receiver.hits[0]["damage"], 26.664))
|
assert(is_equal_approx(receiver.hits[0]["damage"], 26.928))
|
||||||
player.call("take_damage", 1000000.0)
|
player.call("take_damage", 1000000.0)
|
||||||
await get_tree().create_timer(1.3).timeout
|
await get_tree().create_timer(1.3).timeout
|
||||||
game = get_tree().current_scene as Node2D
|
game = get_tree().current_scene as Node2D
|
||||||
@ -125,12 +131,50 @@ func _ready() -> void:
|
|||||||
game = get_tree().current_scene as Node2D
|
game = get_tree().current_scene as Node2D
|
||||||
_freeze(game)
|
_freeze(game)
|
||||||
assert(GameState.captain == profile and GameState.system_level == 2)
|
assert(GameState.captain == profile and GameState.system_level == 2)
|
||||||
assert(profile.get_skill_rank("gunner") == 1 and profile.get_skill_rank("sharp_eye") == 100)
|
assert(profile.get_skill_rank("gunner") == 1 and profile.get_skill_rank("sharp_eye") == 20)
|
||||||
var enemy := get_tree().get_nodes_in_group("enemies")[0] as CharacterBody2D
|
var enemy := get_tree().get_nodes_in_group("enemies")[0] as CharacterBody2D
|
||||||
enemy.call("take_damage", 1000000.0)
|
enemy.call("take_damage", 1000000.0)
|
||||||
assert(profile.level == 5 and profile.experience == 15 and profile.credits == 641)
|
assert(profile.level == 5 and profile.experience == 15 and profile.credits == 641)
|
||||||
print("CAPTAIN_SYSTEM_TEST: PASS (XP, multiple levels, learning UI, rewards once, dodge, reduction, critical addons, projectile metadata, death and next system)")
|
print("CAPTAIN_SYSTEM_TEST: PASS (XP, multiple levels, learning UI, rewards once, dodge, reduction, critical addons, projectile metadata, death and next system)")
|
||||||
|
|
||||||
|
func _test_skill_balance_and_reset() -> void:
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 1), 0.5 / 11.0))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 5), 1.0 / 6.0))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 10), 0.25))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("mechanic", 20), 1.0 / 3.0))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("piloting", 20), 0.25))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("gunner", 20), 0.4))
|
||||||
|
assert(is_equal_approx(BALANCE.skill_bonus("sharp_eye", 20), 0.2))
|
||||||
|
for skill in ["mechanic", "piloting"]:
|
||||||
|
var previous_gain := 1.0
|
||||||
|
for rank in range(1, 21):
|
||||||
|
var gain := BALANCE.skill_bonus(skill, rank) - BALANCE.skill_bonus(skill, rank - 1)
|
||||||
|
assert(gain > 0.0 and gain < previous_gain)
|
||||||
|
previous_gain = gain
|
||||||
|
var captain := PROGRESS.new()
|
||||||
|
captain.learning_points = 100
|
||||||
|
for rank in 20:
|
||||||
|
assert(captain.train_skill("mechanic"))
|
||||||
|
assert(not captain.train_skill("mechanic") and captain.learning_points == 80)
|
||||||
|
captain.level = 3
|
||||||
|
captain.credits = 299
|
||||||
|
assert(not captain.reset_skills())
|
||||||
|
assert(captain.skills["mechanic"] == 20 and captain.credits == 299)
|
||||||
|
captain.credits = 300
|
||||||
|
assert(captain.reset_skills())
|
||||||
|
assert(captain.learning_points == 100 and captain.credits == 0 and captain.skills["mechanic"] == 0)
|
||||||
|
captain.credits = 300
|
||||||
|
assert(not captain.reset_skills() and captain.credits == 300)
|
||||||
|
for skill in BALANCE.SKILL_NAMES:
|
||||||
|
captain.skills[skill] = 100
|
||||||
|
captain.learning_points = 7
|
||||||
|
captain.normalize_skill_ranks()
|
||||||
|
assert(captain.learning_points == 327 and captain.get_spent_learning_points() == 80)
|
||||||
|
captain.normalize_skill_ranks()
|
||||||
|
assert(captain.learning_points == 327)
|
||||||
|
assert(captain.reset_skills())
|
||||||
|
assert(captain.learning_points == 407)
|
||||||
|
|
||||||
func _freeze(game: Node2D) -> void:
|
func _freeze(game: Node2D) -> void:
|
||||||
game.set_physics_process(false)
|
game.set_physics_process(false)
|
||||||
game.get_node("PlayerShip").set_physics_process(false)
|
game.get_node("PlayerShip").set_physics_process(false)
|
||||||
|
|||||||
@ -60,6 +60,15 @@ func _ready() -> void:
|
|||||||
GameState.captain.credits = inventory.get_ammunition_buy_cost()
|
GameState.captain.credits = inventory.get_ammunition_buy_cost()
|
||||||
menu.get_node("Center/Panel/Contents/BuyAmmunitionButton").emit_signal("pressed")
|
menu.get_node("Center/Panel/Contents/BuyAmmunitionButton").emit_signal("pressed")
|
||||||
assert(inventory.ammunition == 400 and GameState.captain.credits == 0)
|
assert(inventory.ammunition == 400 and GameState.captain.credits == 0)
|
||||||
|
assert(GameState.captain.train_skill("gunner"))
|
||||||
|
GameState.captain.credits = GameState.captain.get_skill_reset_cost()
|
||||||
|
GameState.captain.emit_changed()
|
||||||
|
var reset_button: Button = menu.get_node("Center/Panel/Contents/ResetSkillsButton")
|
||||||
|
assert(not reset_button.disabled)
|
||||||
|
reset_button.emit_signal("pressed")
|
||||||
|
assert(GameState.captain.get_skill_rank("gunner") == 0)
|
||||||
|
assert(GameState.captain.learning_points == 1 and GameState.captain.credits == 0)
|
||||||
|
assert(reset_button.disabled)
|
||||||
player.position = initial_data["player_start"]
|
player.position = initial_data["player_start"]
|
||||||
game.call("_physics_process", 0.0)
|
game.call("_physics_process", 0.0)
|
||||||
assert(not menu.visible)
|
assert(not menu.visible)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user