Randoloot/scripts/levels/exploration_poi.gd
2026-10-17 18:28:34 +02:00

217 lines
8.1 KiB
GDScript

extends StaticBody2D
const GENERATOR := preload("res://scripts/items/item_generator.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
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 = ""
_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 _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 := _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