Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
571f01dc31 | ||
|
|
1cb8fc54e9 |
11 changed files with 212 additions and 23 deletions
|
|
@ -11,6 +11,9 @@ extends "res://controllers/AimedFleetAbilityController.gd"
|
|||
# while they aim it.
|
||||
|
||||
const BEAM_EFFECT = preload("res://vfx/LaserBeamEffect.gd")
|
||||
# #201: the beam borrows the fire stick's aim helper, so it is loaded here
|
||||
# for its static assist rather than for anything a laser hull carries.
|
||||
const WEAPON_COMPONENT = preload("res://vessels/VesselWeaponComponent.gd")
|
||||
const BEAM_COLOR := BEAM_EFFECT.BEAM_COLOR
|
||||
const TARGET_COLOR := Color(0.6, 0.95, 1.0)
|
||||
const BEAM_HALF_WIDTH := BEAM_EFFECT.BEAM_HALF_WIDTH
|
||||
|
|
@ -51,7 +54,25 @@ func _aim_max_range() -> float:
|
|||
func _calculate_aim_target(drag: Vector2, origin: Vector2) -> Vector2:
|
||||
# A beam always reaches full range; dragging chooses its direction only.
|
||||
var direction: Vector2 = drag.normalized() if drag.length() > 0.001 else Vector2.UP
|
||||
return origin + direction * _aim_max_range()
|
||||
return _assisted_target(origin, origin + direction * _aim_max_range())
|
||||
|
||||
|
||||
# The mouse path (#80) names a world point directly instead of dragging one
|
||||
# into existence. It has a real cursor, so it is assisted from that rather
|
||||
# than from a virtual one at the end of the beam - otherwise the crosshair
|
||||
# would be the only aim in the game the helper does not touch.
|
||||
func fire_at(world_target: Vector2) -> bool:
|
||||
return super(_assisted_target(_aim_origin(), world_target))
|
||||
|
||||
|
||||
# #201: ordinary fire's aim helper, applied to the beam. It answers with a
|
||||
# PLACE, not a heading - the same thing the fire stick resolves and every
|
||||
# hull then converges on - so a drag that ends near an enemy, or merely
|
||||
# inside the assist cone, passes through that enemy instead of a few degrees
|
||||
# past it. The beam still runs on to its full range from there.
|
||||
func _assisted_target(origin: Vector2, point: Vector2) -> Vector2:
|
||||
return WEAPON_COMPONENT.assisted_point(get_tree(), origin, point,
|
||||
_aim_max_range())
|
||||
|
||||
|
||||
func _consume_shot(component: Node) -> bool:
|
||||
|
|
@ -62,7 +83,12 @@ func _perform_shot(ship: Node2D, component: Node) -> void:
|
|||
# The cooldown was already spent by _consume_shot() above, so this fires
|
||||
# the effect directly rather than going through fire_ability() and
|
||||
# spending it twice.
|
||||
var direction: Vector2 = aim_target - _aim_origin()
|
||||
# Aimed from the HULL, not from the fleet centre. aim_target is a place
|
||||
# now that the assist can move it onto an enemy (#201), and ordinary fire
|
||||
# converges on such a place by letting each hull find its own heading to
|
||||
# it - a shared heading measured at the centroid would walk this beam off
|
||||
# the very target the assist just picked, by the ship's formation offset.
|
||||
var direction: Vector2 = aim_target - ship.global_position
|
||||
direction = direction.normalized() if direction.length() > 0.001 else Vector2.UP
|
||||
var effect: Node2D = BEAM_EFFECT.fire(get_tree().root, ship.global_position,
|
||||
direction, component.beam_range, int(component.damage))
|
||||
|
|
|
|||
|
|
@ -190,11 +190,17 @@ var research_points: int = 0
|
|||
# place to set it - assigning dev_mode anywhere, tests included, reaches
|
||||
# world generation.
|
||||
var dev_unlimited_resources: bool = false
|
||||
# #202: every research and production price answers zero while this is on.
|
||||
# A separate switch from the one above rather than a stronger version of it -
|
||||
# unlimited resources tests whether a price can be PAID, and this tests what
|
||||
# happens when there is no price at all, which is a different play-test.
|
||||
var dev_free_costs: bool = false
|
||||
var dev_mode: bool = false:
|
||||
set(value):
|
||||
dev_mode = value
|
||||
if not value:
|
||||
dev_unlimited_resources = false
|
||||
dev_free_costs = false
|
||||
WORLD_GEN.dev_planets_enabled = value
|
||||
|
||||
# Explored-chunk history for the world map's fog-of-war reveal. Lives
|
||||
|
|
@ -676,7 +682,7 @@ func prerequisite_met(id: String) -> bool:
|
|||
# ever compared two nodes by price - keeps working on a plain number.
|
||||
func skill_costs(id: String) -> Dictionary:
|
||||
var node: Dictionary = skill_node(id)
|
||||
if node.is_empty():
|
||||
if node.is_empty() or costs_are_free():
|
||||
return {}
|
||||
var costs: Dictionary = node.get("costs", {})
|
||||
if not costs.is_empty():
|
||||
|
|
@ -692,7 +698,7 @@ func can_unlock(id: String, payment_position: Variant = null) -> bool:
|
|||
|
||||
|
||||
func skill_research_point_cost(id: String) -> int:
|
||||
return int(skill_node(id).get("research_points", 0))
|
||||
return 0 if costs_are_free() else int(skill_node(id).get("research_points", 0))
|
||||
|
||||
|
||||
func unlock(id: String, payment_position: Variant = null) -> bool:
|
||||
|
|
@ -792,6 +798,7 @@ func reset_new_game() -> void:
|
|||
boost_depleted_notice = false
|
||||
research_points = 0
|
||||
dev_unlimited_resources = false
|
||||
dev_free_costs = false
|
||||
dev_mode = false
|
||||
explored_chunks.clear()
|
||||
scanner_target.clear()
|
||||
|
|
@ -954,6 +961,21 @@ func set_dev_unlimited_resources(enabled: bool) -> bool:
|
|||
return true
|
||||
|
||||
|
||||
func set_dev_free_costs(enabled: bool) -> bool:
|
||||
if enabled and not dev_mode:
|
||||
return false
|
||||
dev_free_costs = enabled
|
||||
return true
|
||||
|
||||
|
||||
# Asked by every price below, and by the menus that display them. The dev_mode
|
||||
# term is not redundant: the setter refuses outside a dev game and the dev_mode
|
||||
# setter clears the flag, so this is the third guard rather than the first, and
|
||||
# it is the one that holds if a save or a test sets the field directly.
|
||||
func costs_are_free() -> bool:
|
||||
return dev_mode and dev_free_costs
|
||||
|
||||
|
||||
func _refill_dev_resources() -> void:
|
||||
for resource_type in resource_types():
|
||||
if bank_inventory.amount(resource_type) < DEV_UNLIMITED_AMOUNT:
|
||||
|
|
@ -1052,7 +1074,7 @@ func find_building(id: int) -> Dictionary:
|
|||
# there is one question with one answer whatever is being bought.
|
||||
func building_costs(type: String) -> Dictionary:
|
||||
var definition: Resource = building_definition(type)
|
||||
if definition == null:
|
||||
if definition == null or costs_are_free():
|
||||
return {}
|
||||
if not definition.build_resource_costs.is_empty():
|
||||
return definition.build_resource_costs.duplicate()
|
||||
|
|
@ -1494,7 +1516,10 @@ func unlock_vessel(vessel: String, payment_position: Variant = null) -> bool:
|
|||
if definition == null or definition.unlock_id == "" or vessel_is_unlocked(vessel) or not has_ship_lab():
|
||||
return false
|
||||
var costs: Dictionary = research_costs(definition.unlock_id)
|
||||
var points_needed: int = int(definition.research_point_cost)
|
||||
# Through the accessor, not off the definition: this used to be the one
|
||||
# place a research price was read straight from its file, which #202's
|
||||
# free-costs switch would have left charging full price here alone.
|
||||
var points_needed: int = research_point_cost(definition.unlock_id)
|
||||
if research_points < points_needed or not spend_resources(costs, payment_position):
|
||||
return false
|
||||
if points_needed > 0:
|
||||
|
|
@ -1540,7 +1565,7 @@ func upgrade_maxed(id: String) -> bool:
|
|||
|
||||
func upgrade_cost(id: String) -> int:
|
||||
var d: Dictionary = upgrade_def(id)
|
||||
if d.is_empty():
|
||||
if d.is_empty() or costs_are_free():
|
||||
return 0
|
||||
return int(d.cost) * (upgrade_level(id) + 1)
|
||||
|
||||
|
|
@ -1631,7 +1656,7 @@ func product_cost(product: String) -> int:
|
|||
|
||||
func product_costs(product: String) -> Dictionary:
|
||||
var definition: Resource = vessel_definition(product)
|
||||
if definition == null:
|
||||
if definition == null or costs_are_free():
|
||||
return {}
|
||||
if not definition.build_resource_costs.is_empty():
|
||||
return definition.build_resource_costs.duplicate()
|
||||
|
|
@ -2332,6 +2357,8 @@ func research_cost(id: String) -> int:
|
|||
|
||||
|
||||
func research_point_cost(id: String) -> int:
|
||||
if costs_are_free():
|
||||
return 0
|
||||
var unlock_definition: Resource = vessel_for_unlock(id)
|
||||
if unlock_definition != null:
|
||||
return int(unlock_definition.research_point_cost)
|
||||
|
|
@ -2340,6 +2367,8 @@ func research_point_cost(id: String) -> int:
|
|||
|
||||
|
||||
func research_costs(id: String) -> Dictionary:
|
||||
if costs_are_free():
|
||||
return {}
|
||||
var unlock_definition: Resource = vessel_for_unlock(id)
|
||||
if unlock_definition != null:
|
||||
if not unlock_definition.unlock_resource_costs.is_empty():
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ extends SceneTree
|
|||
# the live fleet, not only GameState bookkeeping.
|
||||
|
||||
const VESSEL_DEFINITIONS = preload("res://vessels/VesselDefinitions.gd")
|
||||
const BUILDING_DEFINITIONS = preload("res://buildings/BuildingDefinitions.gd")
|
||||
|
||||
var state: Node
|
||||
var scene: Node
|
||||
|
|
@ -73,6 +74,39 @@ func _process(delta: float) -> bool:
|
|||
return _fail("unlimited mode did not replenish research points")
|
||||
print("skill unlock and unlimited resources operate on persistent GameState")
|
||||
|
||||
# --- #202: every research and production price reads zero -------
|
||||
# Unlimited resources goes off first: a bottomless bank and a free
|
||||
# price are indistinguishable while both are on, and it is the
|
||||
# price this checks.
|
||||
menu.unlimited_button.pressed.emit()
|
||||
if state.dev_unlimited_resources:
|
||||
return _fail("the unlimited-resources toggle did not turn back off")
|
||||
var charged: int = _priced_things()
|
||||
if charged == 0:
|
||||
return _fail("nothing in the game carries a price to make free")
|
||||
menu.free_costs_button.pressed.emit()
|
||||
if not state.dev_free_costs:
|
||||
return _fail("the free-costs toggle did not turn on")
|
||||
if _priced_things() != 0:
|
||||
return _fail("%d prices survived the free-costs toggle" % _priced_things())
|
||||
# The accessors are what every purchase, queue and price label
|
||||
# reads, so an empty bank must now afford anything.
|
||||
for resource_type in state.resource_types():
|
||||
state.bank_inventory.set_amount(resource_type, 0)
|
||||
state.fleet_inventory.set_amount(resource_type, 0)
|
||||
state.research_points = 0
|
||||
for definition in BUILDING_DEFINITIONS.all():
|
||||
if not state.can_afford_resources(state.building_costs(String(definition.definition_id))):
|
||||
return _fail("a broke fleet could not afford the free %s" % definition.definition_id)
|
||||
print("free costs zeroed every research and production price")
|
||||
|
||||
menu.free_costs_button.pressed.emit()
|
||||
if state.dev_free_costs:
|
||||
return _fail("the free-costs toggle did not turn back off")
|
||||
if _priced_things() != charged:
|
||||
return _fail("turning free costs off did not restore every price")
|
||||
print("prices come back exactly as they were when the toggle goes off")
|
||||
|
||||
var wave_before: int = state.wave_number
|
||||
menu.wave_button.pressed.emit()
|
||||
if menu.visible:
|
||||
|
|
@ -81,7 +115,7 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the dev menu did not retain the immediate wave tool")
|
||||
|
||||
state.reset_new_game()
|
||||
if state.dev_mode or state.dev_unlimited_resources:
|
||||
if state.dev_mode or state.dev_unlimited_resources or state.dev_free_costs:
|
||||
return _fail("dev cheats leaked into a normal new game")
|
||||
print("TEST_DEV_MENU: PASS")
|
||||
quit(0)
|
||||
|
|
@ -89,6 +123,30 @@ func _process(delta: float) -> bool:
|
|||
return false
|
||||
|
||||
|
||||
# How many prices in the whole game are currently non-zero: skills, vessel
|
||||
# unlocks and upgrades on the research side, buildings and vessel production
|
||||
# on the other. Counted rather than sampled so a price added later is covered
|
||||
# here without anyone remembering to add it.
|
||||
func _priced_things() -> int:
|
||||
var total: int = 0
|
||||
for node in GameState.SKILL_TREE:
|
||||
var id: String = String(node.id)
|
||||
total += state.skill_costs(id).size() + state.skill_research_point_cost(id)
|
||||
for definition in BUILDING_DEFINITIONS.all():
|
||||
total += state.building_costs(String(definition.definition_id)).size()
|
||||
for definition in VESSEL_DEFINITIONS.all():
|
||||
total += state.product_costs(String(definition.definition_id)).size()
|
||||
var unlock_id: String = String(definition.unlock_id)
|
||||
if unlock_id != "":
|
||||
total += state.research_costs(unlock_id).size() \
|
||||
+ state.research_point_cost(unlock_id)
|
||||
for upgrade in definition.upgrades:
|
||||
var upgrade_id: String = String(upgrade.upgrade_id)
|
||||
total += state.upgrade_cost(upgrade_id) \
|
||||
+ state.research_point_cost(upgrade_id)
|
||||
return total
|
||||
|
||||
|
||||
func _fail(message: String) -> bool:
|
||||
print("TEST_DEV_MENU: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
|
|
|
|||
|
|
@ -144,6 +144,12 @@ func _process(delta: float) -> bool:
|
|||
main._handle_touch(release)
|
||||
if not skill.projectile_active:
|
||||
return _fail_now("release did not throw a gas bomb")
|
||||
# #201 doubled the blast. Read off the bomb actually in flight, so
|
||||
# a constant that stops reaching the effect fails here rather than
|
||||
# passing on the strength of its own declaration.
|
||||
if int(skill.projectiles[0].damage) != 6:
|
||||
return _fail_now("gas bomb carries %d damage, expected the doubled 6" \
|
||||
% int(skill.projectiles[0].damage))
|
||||
elapsed = 0.0
|
||||
stage = 1
|
||||
1:
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ func _process(delta: float) -> bool:
|
|||
return _fail_now("no laser ship is ready right after spawning")
|
||||
if absf(first_shooter.get_component("laser").beam_range - 2900.0) > 0.1:
|
||||
return _fail_now("laser ship beam range was not doubled to 2900")
|
||||
# #201 tripled the beam's damage. Read off the COMPONENT rather
|
||||
# than off the definition, so a stat that stops reaching the hull
|
||||
# fails here instead of passing on the strength of its own file.
|
||||
if first_shooter.get_component("laser").damage != 6:
|
||||
return _fail_now("laser damage is %d, expected the tripled 6" \
|
||||
% first_shooter.get_component("laser").damage)
|
||||
|
||||
var press := InputEventScreenTouch.new()
|
||||
press.index = 7
|
||||
|
|
@ -203,6 +209,37 @@ func _process(delta: float) -> bool:
|
|||
if absf(main.get_node("Camera2D").zoom.x - original_zoom) > 0.03:
|
||||
return _fail_now("camera did not restore its pre-aim zoom")
|
||||
print("camera zoom restored")
|
||||
|
||||
# --- E. #201: the beam gets ordinary fire's aim helper ----------
|
||||
# Checked on the resolution itself rather than by firing again:
|
||||
# both hulls are still cooling down, and what the ticket asks for
|
||||
# is exactly that a drag resolves onto a nearby enemy.
|
||||
for spent in on_line_enemies + [off_line_enemy, beyond_range_enemy]:
|
||||
if is_instance_valid(spent):
|
||||
spent.free()
|
||||
var origin: Vector2 = skill._aim_origin()
|
||||
var in_cone: Node = load("res://enemies/Enemy.tscn").instantiate()
|
||||
in_cone.position = origin + Vector2(800.0, 0.0).rotated(deg_to_rad(20.0))
|
||||
main.add_child(in_cone)
|
||||
in_cone.speed = 0.0
|
||||
# Nearer than the one in the cone, and deliberately outside it:
|
||||
# the assist must not treat "closest" as "aimed at".
|
||||
var out_of_cone: Node = load("res://enemies/Enemy.tscn").instantiate()
|
||||
out_of_cone.position = origin + Vector2(600.0, 0.0).rotated(deg_to_rad(60.0))
|
||||
main.add_child(out_of_cone)
|
||||
out_of_cone.speed = 0.0
|
||||
|
||||
var assisted: Vector2 = skill._calculate_aim_target(Vector2(300.0, 0.0), origin)
|
||||
if assisted.distance_to(in_cone.global_position) > 1.0:
|
||||
return _fail_now("a drag past an enemy inside the assist cone did not snap onto it")
|
||||
print("aiming snapped onto the enemy inside the assist cone")
|
||||
|
||||
in_cone.free()
|
||||
var unassisted: Vector2 = skill._calculate_aim_target(Vector2(300.0, 0.0), origin)
|
||||
if unassisted.distance_to(origin + Vector2(2900.0, 0.0)) > 1.0:
|
||||
return _fail_now("the aim bent toward an enemy outside the assist cone")
|
||||
print("an enemy outside the cone left the aim where it was pointed")
|
||||
|
||||
print("TEST_LASER_VESSEL: PASS")
|
||||
quit(0)
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ func _ready() -> void:
|
|||
menu.fleet_remove_requested.connect(_on_fleet_remove_requested)
|
||||
menu.unlock_skills_requested.connect(_on_unlock_skills_requested)
|
||||
menu.unlimited_resources_requested.connect(_on_unlimited_resources_requested)
|
||||
menu.free_costs_requested.connect(_on_free_costs_requested)
|
||||
menu.wave_requested.connect(_on_wave_requested)
|
||||
visible = false
|
||||
|
||||
|
|
@ -70,6 +71,13 @@ func _on_unlimited_resources_requested(enabled: bool) -> void:
|
|||
menu.set_status("Unlimited resources %s" % ("enabled" if enabled else "disabled"))
|
||||
|
||||
|
||||
func _on_free_costs_requested(enabled: bool) -> void:
|
||||
if not GameState.set_dev_free_costs(enabled):
|
||||
return
|
||||
menu.set_status("Research and production costs %s" \
|
||||
% ("are now free" if enabled else "are charged again"))
|
||||
|
||||
|
||||
func _on_wave_requested() -> void:
|
||||
GameState.force_wave_now()
|
||||
menu.close_menu()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ signal fleet_add_requested(vessel_type: String)
|
|||
signal fleet_remove_requested(vessel_type: String)
|
||||
signal unlock_skills_requested
|
||||
signal unlimited_resources_requested(enabled: bool)
|
||||
signal free_costs_requested(enabled: bool)
|
||||
signal wave_requested
|
||||
|
||||
const ICON_BUTTON = preload("res://ui/IconButton.gd")
|
||||
|
|
@ -16,8 +17,10 @@ const DESIGN_SIZE := Vector2(720.0, 1280.0)
|
|||
@onready var fleet_rows: VBoxContainer = $Content/FleetRows
|
||||
@onready var unlock_button: IconButton = $Content/ToolButtons/UnlockSkills
|
||||
@onready var unlimited_button: IconButton = $Content/ToolButtons/UnlimitedResources
|
||||
@onready var free_costs_button: IconButton = $Content/ToolButtons/FreeCosts
|
||||
@onready var wave_button: IconButton = $Content/ToolButtons/StartWave
|
||||
@onready var unlimited_label: Label = $Content/ToolLabels/UnlimitedLabel
|
||||
@onready var free_costs_label: Label = $Content/ToolLabels/FreeCostsLabel
|
||||
@onready var status: Label = $Content/StatusLabel
|
||||
|
||||
var fleet: Node = null
|
||||
|
|
@ -31,6 +34,8 @@ func _ready() -> void:
|
|||
unlock_button.pressed.connect(func(): unlock_skills_requested.emit())
|
||||
unlimited_button.pressed.connect(func():
|
||||
unlimited_resources_requested.emit(not GameState.dev_unlimited_resources))
|
||||
free_costs_button.pressed.connect(func():
|
||||
free_costs_requested.emit(not GameState.dev_free_costs))
|
||||
wave_button.pressed.connect(func(): wave_requested.emit())
|
||||
_layout_content()
|
||||
|
||||
|
|
@ -80,8 +85,11 @@ func refresh() -> void:
|
|||
(controls.remove as IconButton).enabled = count > 0 and total > 1
|
||||
unlock_button.highlighted = _all_skills_unlocked()
|
||||
unlimited_button.highlighted = GameState.dev_unlimited_resources
|
||||
unlimited_label.text = "UNLIMITED RESOURCES\n%s" \
|
||||
unlimited_label.text = "UNLIMITED\nRESOURCES\n%s" \
|
||||
% ("ON" if GameState.dev_unlimited_resources else "OFF")
|
||||
free_costs_button.highlighted = GameState.dev_free_costs
|
||||
free_costs_label.text = "FREE\nCOSTS\n%s" \
|
||||
% ("ON" if GameState.dev_free_costs else "OFF")
|
||||
|
||||
|
||||
func _all_skills_unlocked() -> bool:
|
||||
|
|
|
|||
|
|
@ -105,11 +105,11 @@ horizontal_alignment = 1
|
|||
|
||||
[node name="ToolButtons" type="HBoxContainer" parent="Content"]
|
||||
anchor_right = 1.0
|
||||
offset_left = 70.0
|
||||
offset_left = 24.0
|
||||
offset_top = 896.0
|
||||
offset_right = -70.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = 1012.0
|
||||
theme_override_constants/separation = 56
|
||||
theme_override_constants/separation = 52
|
||||
alignment = 1
|
||||
|
||||
[node name="UnlockSkills" type="Control" parent="Content/ToolButtons"]
|
||||
|
|
@ -126,6 +126,13 @@ script = ExtResource("icon_button")
|
|||
icon = 29
|
||||
glow_color = Color(1, 0.78, 0.25, 1)
|
||||
|
||||
[node name="FreeCosts" type="Control" parent="Content/ToolButtons"]
|
||||
custom_minimum_size = Vector2(116, 116)
|
||||
layout_mode = 2
|
||||
script = ExtResource("icon_button")
|
||||
icon = 28
|
||||
glow_color = Color(0.55, 0.75, 1, 1)
|
||||
|
||||
[node name="StartWave" type="Control" parent="Content/ToolButtons"]
|
||||
custom_minimum_size = Vector2(116, 116)
|
||||
layout_mode = 2
|
||||
|
|
@ -135,15 +142,15 @@ glow_color = Color(1, 0.45, 0.2, 1)
|
|||
|
||||
[node name="ToolLabels" type="HBoxContainer" parent="Content"]
|
||||
anchor_right = 1.0
|
||||
offset_left = 28.0
|
||||
offset_left = 24.0
|
||||
offset_top = 1018.0
|
||||
offset_right = -28.0
|
||||
offset_bottom = 1098.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = 1114.0
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
|
||||
[node name="UnlockLabel" type="Label" parent="Content/ToolLabels"]
|
||||
custom_minimum_size = Vector2(210, 76)
|
||||
custom_minimum_size = Vector2(160, 92)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "UNLOCK ALL\nSKILLS"
|
||||
|
|
@ -151,15 +158,23 @@ horizontal_alignment = 1
|
|||
vertical_alignment = 1
|
||||
|
||||
[node name="UnlimitedLabel" type="Label" parent="Content/ToolLabels"]
|
||||
custom_minimum_size = Vector2(210, 76)
|
||||
custom_minimum_size = Vector2(160, 92)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "UNLIMITED RESOURCES\nOFF"
|
||||
text = "UNLIMITED\nRESOURCES\nOFF"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="FreeCostsLabel" type="Label" parent="Content/ToolLabels"]
|
||||
custom_minimum_size = Vector2(160, 92)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "FREE\nCOSTS\nOFF"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="WaveLabel" type="Label" parent="Content/ToolLabels"]
|
||||
custom_minimum_size = Vector2(210, 76)
|
||||
custom_minimum_size = Vector2(160, 92)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "START ENEMY\nWAVE"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ hull_points = PackedVector2Array(0, -26, -9, 14, -4, 10, 4, 10, 9, 14)
|
|||
fleet_eligible = true
|
||||
capabilities = PackedStringArray("laser")
|
||||
component_scenes = Array[PackedScene]([ExtResource("2")])
|
||||
base_stats = {"max_speed": 660.0, "acceleration": 1890.0, "delivery_speed": 180.0, "damage": 2.0, "beam_range": 2900.0, "beam_cooldown": 4.0}
|
||||
base_stats = {"max_speed": 660.0, "acceleration": 1890.0, "delivery_speed": 180.0, "damage": 6.0, "beam_range": 2900.0, "beam_cooldown": 4.0}
|
||||
minimum_stats = {"beam_cooldown": 1.5}
|
||||
upgrades = Array[Resource]([ExtResource("3"), ExtResource("4"), ExtResource("5")])
|
||||
unlock_id = "unlock_laser"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ func _init() -> void:
|
|||
const BOMB_EFFECT = preload("res://vfx/GasBombEffect.gd")
|
||||
const GAS_PER_SHOT: int = 5
|
||||
const MAX_RANGE: float = 950.0
|
||||
const BLAST_DAMAGE: int = 3
|
||||
# #201: doubled. The bomb is the fleet's only area weapon and one slow,
|
||||
# expensive throw was worth less than a couple of seconds of ordinary fire.
|
||||
const BLAST_DAMAGE: int = 6
|
||||
const SHOT_COOLDOWN: float = 5.0
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ class_name VesselLaserComponent
|
|||
extends "res://vessels/VesselCooldownFillComponent.gd"
|
||||
|
||||
var capability_id := "laser"
|
||||
var damage: int = 2
|
||||
var damage: int = 6
|
||||
var beam_range: float = 2900.0
|
||||
|
||||
const READY_COLOR := Color(0.3, 0.9, 1.0)
|
||||
|
|
|
|||
Loading…
Reference in a new issue