228 lines
8.6 KiB
GDScript
228 lines
8.6 KiB
GDScript
extends StaticBody2D
|
|
|
|
const GENERATOR := preload("res://scripts/items/item_generator.gd")
|
|
const RECIPES := preload("res://scripts/items/crafting_recipes.gd")
|
|
const ASTEROID_TEXTURES := {
|
|
"iron": preload("res://assets/icons/resources/iron.png"),
|
|
"titanium": preload("res://assets/icons/resources/titanium.png"),
|
|
"uranium": preload("res://assets/icons/resources/uranium.png"),
|
|
"aluminium": preload("res://assets/icons/resources/aluminium.png"),
|
|
"sulfur": preload("res://assets/icons/resources/sulfur.png"),
|
|
}
|
|
const WRECK_TEXTURES := [preload("res://assets/wrecks/wreck_frigate.png"), preload("res://assets/wrecks/wreck_freighter.png"), preload("res://assets/wrecks/wreck_interceptor.png")]
|
|
const LOOT := preload("res://scripts/items/loot_pickup.gd")
|
|
const ASTEROID_DESTRUCTION := preload("res://scripts/game/asteroid_destruction_effect.gd")
|
|
var kind := "wreck"
|
|
var poi_id := ""
|
|
var reward_seed := 1
|
|
var player: CharacterBody2D
|
|
var approach_radius := 115.0
|
|
var hull := 20.0
|
|
var _claimed := false
|
|
var _module_left_outside := false
|
|
var _label: Label
|
|
var _rng := RandomNumberGenerator.new()
|
|
var _mineral := "iron"
|
|
var _points := PackedVector2Array()
|
|
var _asteroid_sprite: Sprite2D
|
|
var _asteroid_spin := 0.0
|
|
|
|
func _ready() -> void:
|
|
add_to_group("exploration_pois")
|
|
_rng.seed = reward_seed
|
|
_mineral = ["iron", "aluminium", "titanium", "uranium", "sulfur"][_rng.randi_range(0, 4)]
|
|
hull = 20.0 * reward_scale()
|
|
collision_layer = (4 | 32) if kind == "asteroid" else 0
|
|
collision_mask = 0
|
|
if kind == "asteroid":
|
|
add_to_group("asteroids")
|
|
var shape := CollisionShape2D.new()
|
|
var circle := CircleShape2D.new()
|
|
circle.radius = 30
|
|
shape.shape = circle
|
|
add_child(shape)
|
|
for index in 10:
|
|
_points.append(Vector2.RIGHT.rotated(TAU * index / 10) * _rng.randf_range(23, 35))
|
|
_asteroid_sprite = Sprite2D.new()
|
|
_asteroid_sprite.name = "AsteroidSprite"
|
|
_asteroid_sprite.texture = ASTEROID_TEXTURES[_mineral]
|
|
var texture_size := _asteroid_sprite.texture.get_size()
|
|
_asteroid_sprite.scale = Vector2.ONE * (78.0 / maxf(texture_size.x, texture_size.y))
|
|
_asteroid_sprite.rotation = deg_to_rad(posmod(reward_seed, 360))
|
|
_asteroid_spin = (0.025 + posmod(reward_seed, 12) * 0.005) * (1.0 if reward_seed % 2 == 0 else -1.0)
|
|
add_child(_asteroid_sprite)
|
|
if kind == "wreck":
|
|
var sprite := Sprite2D.new()
|
|
sprite.name = "WreckSprite"
|
|
sprite.texture = WRECK_TEXTURES[posmod(reward_seed, WRECK_TEXTURES.size())]
|
|
var texture_size := sprite.texture.get_size()
|
|
sprite.scale = Vector2.ONE * (92.0 / maxf(texture_size.x, texture_size.y))
|
|
sprite.rotation = deg_to_rad(posmod(reward_seed, 360))
|
|
add_child(sprite)
|
|
_label = Label.new()
|
|
_label.light_mask = 4
|
|
_label.position = Vector2(-160, 40)
|
|
_label.size = Vector2(320, 70)
|
|
_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
_label.add_theme_font_size_override("font_size", 12)
|
|
if kind == "ruin":
|
|
z_index = 20
|
|
_label.position = Vector2(-40, -35)
|
|
_label.size = Vector2(80, 70)
|
|
_label.add_theme_font_size_override("font_size", 52)
|
|
_label.add_theme_color_override("font_color", Color("ffd166"))
|
|
_label.add_theme_color_override("font_outline_color", Color("151625"))
|
|
_label.add_theme_constant_override("outline_size", 8)
|
|
add_child(_label)
|
|
_update_label(false)
|
|
queue_redraw()
|
|
|
|
func _process(delta: float) -> void:
|
|
if is_instance_valid(_asteroid_sprite) and not _claimed:
|
|
_asteroid_sprite.rotation += _asteroid_spin * delta
|
|
|
|
# One mutually exclusive reward: 65% minerals, 20% ammunition/rockets, 15% module.
|
|
static func wreck_reward_from_roll(roll: int) -> String:
|
|
if roll < 65:
|
|
return "minerals"
|
|
if roll < 75:
|
|
return "ammunition"
|
|
if roll < 85:
|
|
return "missile"
|
|
return "module"
|
|
|
|
func reward_scale() -> float:
|
|
return preload("res://scripts/levels/level_balance.gd").item_stat_scale(clampi(GameState.system_level, 1, 1000))
|
|
|
|
func _update_label(near: bool) -> void:
|
|
if kind == "ruin":
|
|
_label.text = "?"
|
|
elif kind == "asteroid":
|
|
_label.text = ""
|
|
_label.hide()
|
|
else:
|
|
_label.text = ("Wrak statku" if kind == "wreck" else "Antyczne znalezisko") + ("\n[E] Przeszukaj" if near else "\nPodleć, aby przeszukać")
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if _claimed or not is_instance_valid(player) or bool(player.get("_is_destroyed")):
|
|
return
|
|
var near := global_position.distance_to(player.global_position) <= approach_radius
|
|
_update_label(near)
|
|
if near and (kind == "ruin" or (kind == "wreck" and Input.is_physical_key_pressed(KEY_E))):
|
|
claim()
|
|
|
|
func take_weapon_hit(amount: float, _critical: bool = false, _damage_type: int = 0) -> void:
|
|
take_damage(amount)
|
|
|
|
func take_damage(amount: float) -> void:
|
|
if kind != "asteroid" or _claimed:
|
|
return
|
|
hull -= maxf(0, amount)
|
|
if hull <= 0:
|
|
claim()
|
|
else:
|
|
_update_label(false)
|
|
|
|
func claim() -> bool:
|
|
if _claimed or not is_instance_valid(player) or bool(player.get("_is_destroyed")):
|
|
return false
|
|
_claimed = true
|
|
GameState.explored_pois[poi_id] = true
|
|
var inventory := player.get_node("ShipInventory") as ShipInventory
|
|
var summary := ""
|
|
var scale := reward_scale()
|
|
if kind == "asteroid":
|
|
var amount := _rng.randi_range(5, 7) + maxi(0, roundi(GameState.captain.get_skill_bonus("miner")))
|
|
var debris := Node2D.new()
|
|
debris.set_script(ASTEROID_DESTRUCTION)
|
|
debris.name = "AsteroidDebris"
|
|
debris.call("configure", _asteroid_sprite, amount, _mineral)
|
|
get_parent().add_child.call_deferred(debris)
|
|
set_physics_process(false)
|
|
queue_free()
|
|
return true
|
|
elif kind == "ruin":
|
|
var xp := GameState.captain.encounter_xp(_rng.randi_range(10, 20) * scale, GameState.system_level)
|
|
GameState.captain.add_rewards(xp, 0)
|
|
_drop_module(true)
|
|
summary = "+%d doświadczenia · +1 moduł" % xp
|
|
_show_discovery(summary)
|
|
else:
|
|
match wreck_reward_from_roll(_rng.randi_range(0, 99)):
|
|
"minerals":
|
|
var amount := _rng.randi_range(3, 8)
|
|
GameState.captain.minerals[_mineral] = int(GameState.captain.minerals.get(_mineral, 0)) + amount
|
|
summary = "+%d · %s" % [amount, RECIPES.NAMES[_mineral]]
|
|
"module":
|
|
_drop_module(false)
|
|
summary = "Znaleziono moduł"
|
|
"missile":
|
|
var amount := maxi(1, roundi(scale))
|
|
inventory.missile_ammunition += amount
|
|
summary = "+%d rakiet" % amount
|
|
"ammunition":
|
|
var amount := _rng.randi_range(40, 60)
|
|
inventory.ammunition += amount
|
|
summary = "+%d amunicji" % amount
|
|
inventory.ammunition_changed.emit()
|
|
GameState.captain.emit_changed()
|
|
if kind != "ruin":
|
|
var popup := preload("res://scenes/game/damage_popup.tscn").instantiate() as Label
|
|
popup.text = summary
|
|
popup.set("text_color", Color("c9e8ff"))
|
|
popup.position = position + Vector2(-80, -65)
|
|
get_parent().add_child.call_deferred(popup)
|
|
set_physics_process(false)
|
|
queue_free()
|
|
return true
|
|
|
|
func _show_discovery(reward: String) -> void:
|
|
var layer := CanvasLayer.new()
|
|
layer.layer = preload("res://scripts/ui/render_layers.gd").DISCOVERY
|
|
layer.name = "AncientDiscoveryMessage"
|
|
get_parent().add_child(layer)
|
|
var label := Label.new()
|
|
layer.add_child(label)
|
|
label.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP)
|
|
label.offset_left = -300
|
|
label.offset_right = 300
|
|
label.offset_top = 145
|
|
label.offset_bottom = 181
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
label.add_theme_font_size_override("font_size", 15)
|
|
label.add_theme_color_override("font_color", Color("d8d4b7"))
|
|
label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.85))
|
|
label.add_theme_constant_override("shadow_offset_x", 1)
|
|
label.add_theme_constant_override("shadow_offset_y", 2)
|
|
label.text = reward
|
|
if _module_left_outside:
|
|
label.text += " (obok planety)"
|
|
label.modulate.a = 0.0
|
|
var tween := layer.create_tween()
|
|
tween.tween_property(label, "modulate:a", 1.0, 0.35)
|
|
tween.tween_interval(3.5)
|
|
tween.tween_property(label, "modulate:a", 0.0, 0.8)
|
|
tween.tween_callback(layer.queue_free)
|
|
|
|
func _drop_module(ancient: bool) -> ItemInstance:
|
|
var definitions: Array[ItemDefinition] = []
|
|
for definition in GENERATOR.DEFINITIONS:
|
|
if definition.category != ItemDefinition.Category.WEAPON:
|
|
definitions.append(definition)
|
|
var item := GENERATOR.generate_loot(mini(1000, GameState.system_level + (1 if ancient else 0)), 2 if ancient else 0, definitions[_rng.randi_range(0, definitions.size() - 1)], float(player.get("luck_percent")))
|
|
if ancient:
|
|
for modifier in item.additional_modifiers:
|
|
var bounds := modifier.affix.get_value_range(item.item_level)
|
|
modifier.value = lerpf(bounds.x, bounds.y, _rng.randf_range(0.9, 1.0))
|
|
if player.get_node("ShipInventory").add_item(item):
|
|
return item
|
|
_module_left_outside = true
|
|
var loot := Node2D.new()
|
|
loot.set_script(LOOT)
|
|
loot.set("item", item)
|
|
loot.position = position + Vector2(0, 70)
|
|
get_parent().add_child.call_deferred(loot)
|
|
return item
|