Randoloot/scripts/levels/exploration_poi.gd
2026-10-14 22:30:56 +02:00

209 lines
8.3 KiB
GDScript

extends StaticBody2D
const GENERATOR := preload("res://scripts/items/item_generator.gd")
const ASTEROID_TEXTURES := [preload("res://assets/asteroids/asteroid_rock.png"), preload("res://assets/asteroids/asteroid_metal.png"), preload("res://assets/asteroids/asteroid_ice.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"][_rng.randi_range(0, 3)]
hull = 20.0 * reward_scale()
collision_layer = 4 if kind == "asteroid" else 0
collision_mask = 0
if kind == "asteroid":
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[posmod(reward_seed, ASTEROID_TEXTURES.size())]
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
func reward_scale() -> float:
return 1.0 + 0.05 * (clampi(GameState.system_level, 1, 1000) - 1)
func _update_label(near: bool) -> void:
if kind == "ruin":
_label.text = "?"
elif kind == "asteroid":
_label.text = "Asteroida · %s\nHP: %.0f" % [{"iron": "Żelazo", "aluminium": "Aluminium", "titanium": "Tytan", "uranium": "Uran"}[_mineral], hull]
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 := maxi(1, roundi(_rng.randi_range(1, 3) * scale))
GameState.captain.minerals[_mineral] = int(GameState.captain.minerals.get(_mineral, 0)) + amount
summary = "+%d %s" % [amount, {"iron": "żelaza", "aluminium": "aluminium", "titanium": "tytanu", "uranium": "uranu"}[_mineral]]
elif kind == "ruin":
var xp := roundi(_rng.randi_range(10, 20) * scale)
GameState.captain.add_rewards(xp, 0)
var module := _drop_module(true)
summary = "%d XP oraz %s (poziom %d)" % [xp, module.definition.display_name, module.item_level]
_show_discovery(summary)
else:
match _rng.randi_range(0, 3):
0:
var amount := roundi(_rng.randi_range(3, 8) * scale)
GameState.captain.parts += amount
summary = "+%d części" % amount
1:
_drop_module(false)
summary = "Znaleziono moduł"
2:
var amount := maxi(1, roundi(scale))
inventory.missile_ammunition += amount
summary = "+%d rakiet" % amount
3:
var amount := roundi(_rng.randi_range(5, 15) * scale)
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)
if kind == "asteroid" and is_instance_valid(_asteroid_sprite):
var debris := Node2D.new()
debris.set_script(ASTEROID_DESTRUCTION)
debris.name = "AsteroidDebris"
debris.call("configure", _asteroid_sprite)
# The POI may be destroyed during a physics query; add debris outside it.
get_parent().add_child.call_deferred(debris)
queue_free()
return true
func _show_discovery(reward: String) -> void:
var layer := CanvasLayer.new()
layer.layer = 30
layer.name = "AncientDiscoveryMessage"
get_parent().add_child(layer)
var panel := PanelContainer.new()
panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
layer.add_child(panel)
panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP)
panel.offset_left = -300
panel.offset_right = 300
panel.offset_top = 70
var label := Label.new()
label.custom_minimum_size = Vector2(600, 95)
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
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", 18)
label.add_theme_color_override("font_color", Color("ffd166"))
label.text = "Znalazłeś ślady starożytnej upadłej cywilizacji!\nZdobyłeś: " + reward
if _module_left_outside:
label.text += "\nMagazyn pełny — moduł czeka obok planety."
panel.add_child(label)
var tween := layer.create_tween()
tween.tween_interval(7.0)
tween.tween_property(panel, "modulate:a", 0.0, 0.5)
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_for_definition(definitions[_rng.randi_range(0, definitions.size() - 1)], mini(1000, GameState.system_level + (1 if ancient else 0)), 2 if ancient else 0)
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