add ai, photo_mode and radio

This commit was merged in pull request #11.
This commit is contained in:
2026-05-05 20:33:00 +00:00
parent 72d06560b1
commit 70d3dfe15c
66 changed files with 1531 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
class_name CollectibleLibrary
extends Resource
@export var collectibles: Array[CollectibleResource]

View File

@@ -0,0 +1 @@
uid://hhuufh87skq5

View File

@@ -0,0 +1,31 @@
[gd_resource type="Resource" script_class="CollectibleLibrary" format=3 uid="uid://c6pkpvsvimafb"]
[ext_resource type="Script" uid="uid://bj34o0org55ei" path="res://core/photo_mode/data/collectible_resource.gd" id="1_3cgvf"]
[ext_resource type="Script" uid="uid://hhuufh87skq5" path="res://core/photo_mode/data/collectible_library.gd" id="2_ggu10"]
[ext_resource type="Texture2D" uid="uid://cx8d233y32kmu" path="res://icon.svg" id="2_pxmdy"]
[sub_resource type="Resource" id="Resource_3cgvf"]
script = ExtResource("1_3cgvf")
id = &"cane"
title = "Cane"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[sub_resource type="Resource" id="Resource_ggu10"]
script = ExtResource("1_3cgvf")
id = &"gatto"
title = "Gatto"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[sub_resource type="Resource" id="Resource_pxmdy"]
script = ExtResource("1_3cgvf")
id = &"farfalla"
title = "Farfalla"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[resource]
script = ExtResource("2_ggu10")
collectibles = Array[ExtResource("1_3cgvf")]([SubResource("Resource_3cgvf"), SubResource("Resource_ggu10"), SubResource("Resource_pxmdy")])
metadata/_custom_type_script = "uid://hhuufh87skq5"

View File

@@ -0,0 +1,6 @@
class_name CollectibleResource
extends Resource
@export var id: StringName
@export var title: String
@export var image: Texture2D

View File

@@ -0,0 +1 @@
uid://bj34o0org55ei

View File

@@ -0,0 +1,60 @@
extends Node
signal on_collectible_unlocked(collectible_id: StringName)
signal on_photo_saved(filePath: String)
var collectibles_library: CollectibleLibrary = preload("res://core/photo_mode/data/collectible_library.tres")
var unlocked_collectibles_ids: Array[StringName] = []
var saved_photos: Array[String] = []
func _ready() -> void:
load_save_data()
func unlock_collectible(collectible_id: StringName) -> void:
if not collectible_id in unlocked_collectibles_ids:
var new_collectible = get_collectible_by_id(collectible_id)
if new_collectible:
unlocked_collectibles_ids.append(collectible_id)
sync_save_data()
on_collectible_unlocked.emit(collectible_id)
func get_collectible_by_id(id: StringName) -> CollectibleResource:
for collectible in collectibles_library.collectibles:
if collectible.id == id:
return collectible
return null
func get_all_collectibles() -> Array[CollectibleResource]:
return collectibles_library.collectibles
func get_unlocked_collectible_ids() -> Array[StringName]:
return unlocked_collectibles_ids
func save_photo_to_disk_async() -> void:
await RenderingServer.frame_post_draw
var image = get_viewport().get_texture().get_image()
if not DirAccess.dir_exists_absolute("user://photos"):
DirAccess.make_dir_absolute("user://photos")
var timestamp = str(Time.get_unix_time_from_system())
var file_path = "user://photos/photo_" + timestamp + ".png"
image.save_png(file_path)
saved_photos.append(file_path)
sync_save_data()
on_photo_saved.emit(file_path)
func sync_save_data() -> void:
GameState.save_data.unlocked_collectibles_ids = unlocked_collectibles_ids.duplicate()
GameState.save_data.saved_photos = saved_photos.duplicate()
func load_save_data() -> void:
unlocked_collectibles_ids = GameState.save_data.unlocked_collectibles_ids.duplicate()
saved_photos = GameState.save_data.saved_photos.duplicate()
if unlocked_collectibles_ids.is_empty() and saved_photos.is_empty():
unlocked_collectibles_ids.append(&"cane")
sync_save_data()

View File

@@ -0,0 +1 @@
uid://c3kq1qddpm8tf

View File

@@ -0,0 +1,6 @@
extends Area3D
@export var collectible_data: CollectibleResource
func _ready() -> void:
add_to_group("collectible")

View File

@@ -0,0 +1 @@
uid://62d14boivr3g

View File

@@ -0,0 +1,11 @@
[gd_scene format=3 uid="uid://bmkxt6btcx8qr"]
[ext_resource type="Script" uid="uid://62d14boivr3g" path="res://core/photo_mode/runtime/collectible.gd" id="1_xse8c"]
[sub_resource type="BoxShape3D" id="BoxShape3D_xse8c"]
[node name="Collectible" type="Area3D" unique_id=1229019813]
script = ExtResource("1_xse8c")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1479480920]
shape = SubResource("BoxShape3D_xse8c")

View File

@@ -0,0 +1,94 @@
extends Node3D
@export_group("Speed")
@export var pan_speed: float = 5.0
@export var rotation_speed: float = 0.003
@export_group("Bounds")
@export var movement_bounds: AABB
@export_group("Ref")
@export var rotation_target: Node3D
@onready var camera: Camera3D = $%Camera3D
var total_yaw: float = 0.0
var total_pitch: float = 0.0
var orbit_distance: float = 0.0
var current_pan: Vector2 = Vector2.ZERO
var is_active: bool = false
func _ready() -> void:
total_yaw = global_rotation.y
total_pitch = global_rotation.x
if rotation_target:
orbit_distance = global_position.distance_to(rotation_target.global_position)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_photo_mode"):
is_active = !is_active
get_tree().paused = is_active
if !is_active:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
if event is InputEventMouseMotion and is_active:
total_yaw -= event.relative.x * rotation_speed
if event.is_action_pressed("take_photo"):
take_photo_async()
func _process(delta: float) -> void:
if not rotation_target:
return
var pan_input = Input.get_vector("photo_pan_left", "photo_pan_right", "photo_pan_up", "photo_pan_down")
var rot_basis = Basis.from_euler(Vector3(total_pitch, total_yaw, 0))
if pan_input != Vector2.ZERO and is_active:
current_pan.x += pan_input.x * pan_speed * delta
current_pan.y += -pan_input.y * pan_speed * delta
if movement_bounds.size != Vector3.ZERO:
current_pan.x = clampf(current_pan.x, movement_bounds.position.x, movement_bounds.position.x + movement_bounds.size.x)
current_pan.y = clampf(current_pan.y, movement_bounds.position.y, movement_bounds.position.y + movement_bounds.size.y)
var right_dir = rot_basis.x.normalized()
var up_dir = rot_basis.y.normalized()
var final_pan_offset = (right_dir * current_pan.x) + (up_dir * current_pan.y)
var orbit_pos = rotation_target.global_position + (rot_basis * Vector3(0, 0, orbit_distance))
global_position = orbit_pos + final_pan_offset
global_basis = rot_basis
func take_photo_async() -> void:
if !is_active:
return
var targets = get_tree().get_nodes_in_group("collectible")
var space_state = get_world_3d().direct_space_state
await CollectionManager.save_photo_to_disk_async()
for target in targets:
if not target.collectible_data:
continue
if camera.is_position_in_frustum(target.global_position):
var query = PhysicsRayQueryParameters3D.create(camera.global_position, target.global_position)
query.collide_with_areas = true
query.collide_with_bodies = true
var result = space_state.intersect_ray(query)
if result and result.collider == target:
CollectionManager.unlock_collectible(target.collectible_data.id)
GameState.save_game()

View File

@@ -0,0 +1 @@
uid://d7ln6iru6mq6

View File

@@ -0,0 +1,10 @@
[gd_scene format=3 uid="uid://vni5kjalum6d"]
[ext_resource type="Script" uid="uid://d7ln6iru6mq6" path="res://core/photo_mode/runtime/photo_mode_controller.gd" id="1_uhpya"]
[node name="PhotoModeController" type="Node3D" unique_id=695158870]
process_mode = 3
script = ExtResource("1_uhpya")
[node name="Camera3D" type="Camera3D" parent="." unique_id=1369039645]
unique_name_in_owner = true

View File

@@ -0,0 +1,34 @@
extends Control
@export var collectible_ui_scene: PackedScene
@onready var grid_container: GridContainer = $%CollectibleGrid
func _ready() -> void:
CollectionManager.on_collectible_unlocked.connect(_unlock_collectible)
setup()
func setup() -> void:
var unlocked_collectible_ids = CollectionManager.get_unlocked_collectible_ids()
var collectibles = CollectionManager.get_all_collectibles()
for child in grid_container.get_children():
child.queue_free()
for collectible in collectibles:
_add_collectible(collectible)
if unlocked_collectible_ids.has(collectible.id):
_unlock_collectible(collectible.id)
func on_collectible_unlocked(collectible_id: StringName) -> void:
_unlock_collectible(collectible_id)
func _add_collectible(collectible: CollectibleResource) -> void:
var collectible_ui: CollectibleUI = collectible_ui_scene.instantiate()
collectible_ui.setup(collectible)
grid_container.add_child(collectible_ui)
func _unlock_collectible(collectible_id: StringName) -> void:
for collectible_ui in grid_container.get_children():
if collectible_ui.collectible_resource.id == collectible_id:
collectible_ui.unlock()

View File

@@ -0,0 +1 @@
uid://dq3qtcrdnikl7

View File

@@ -0,0 +1,50 @@
[gd_scene format=3 uid="uid://bvw086glfpcba"]
[ext_resource type="Script" uid="uid://dq3qtcrdnikl7" path="res://core/photo_mode/ui/collectible_gallery.gd" id="1_67tug"]
[ext_resource type="PackedScene" uid="uid://dp7dvfauh5rpx" path="res://core/photo_mode/ui/collectible_ui.tscn" id="2_or234"]
[node name="CollectibleGallery" type="Control" unique_id=354419843]
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_67tug")
collectible_ui_scene = ExtResource("2_or234")
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=640179265]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -350.0
offset_top = -350.0
offset_right = 350.0
offset_bottom = 350.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=125147979]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=348576028]
layout_mode = 2
draw_focus_border = true
[node name="CollectibleGrid" type="GridContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1187865988]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 2
theme_override_constants/h_separation = 12
theme_override_constants/v_separation = 12
columns = 3

View File

@@ -0,0 +1,22 @@
extends Control
class_name CollectibleUI
@onready var label: Label = $%CollectibleName
@onready var texture_rect: TextureRect = $%CollectibleTexture
var collectible_resource: CollectibleResource
func setup(collectible: CollectibleResource) -> void:
collectible_resource = collectible
if !label:
label = $%CollectibleName
label.text = collectible_resource.title
if collectible.image:
if !texture_rect:
texture_rect = $%CollectibleTexture
texture_rect.texture = collectible_resource.image
func unlock() -> void:
if !texture_rect:
texture_rect = $%CollectibleTexture
texture_rect.set_modulate(Color(1,1,1,1))

View File

@@ -0,0 +1 @@
uid://dxh8e1n16qjpp

View File

@@ -0,0 +1,49 @@
[gd_scene format=3 uid="uid://dp7dvfauh5rpx"]
[ext_resource type="Script" uid="uid://dxh8e1n16qjpp" path="res://core/photo_mode/ui/collectible_ui.gd" id="1_tu5nx"]
[ext_resource type="Texture2D" uid="uid://cx8d233y32kmu" path="res://icon.svg" id="1_x3wje"]
[node name="CollectibleUI" type="Control" unique_id=201865221]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
script = ExtResource("1_tu5nx")
[node name="Panel" type="PanelContainer" parent="." unique_id=1372516531]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="Panel" unique_id=1141643525]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer" unique_id=1841409877]
layout_mode = 2
alignment = 1
[node name="CollectibleName" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=117774359]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
text = "Collectible"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer" unique_id=1345822244]
layout_mode = 2
[node name="CollectibleTexture" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer" unique_id=506844785]
unique_name_in_owner = true
modulate = Color(0.16206557, 0.1620656, 0.16206557, 1)
layout_mode = 2
size_flags_vertical = 3
texture = ExtResource("1_x3wje")
stretch_mode = 3

View File

@@ -0,0 +1,11 @@
extends Control
class_name Photo
@onready var texture_rect: TextureRect = $%PhotoTexture
func setup(texture: Texture) -> void:
if texture:
if !texture_rect:
texture_rect = $%PhotoTexture
texture_rect.texture = texture

View File

@@ -0,0 +1 @@
uid://dsey5dvc11vpq

View File

@@ -0,0 +1,38 @@
[gd_scene format=3 uid="uid://cvus62qkop3qi"]
[ext_resource type="Script" uid="uid://dsey5dvc11vpq" path="res://core/photo_mode/ui/photo.gd" id="1_u0arp"]
[node name="Photo" type="Control" unique_id=201865221]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
script = ExtResource("1_u0arp")
[node name="ColorRect" type="ColorRect" parent="." unique_id=1208008621]
layout_mode = 0
offset_right = 200.0
offset_bottom = 200.0
[node name="MarginContainer" type="MarginContainer" parent="ColorRect" unique_id=1141643525]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="Panel" type="PanelContainer" parent="ColorRect/MarginContainer" unique_id=1372516531]
layout_mode = 2
[node name="PhotoTexture" type="TextureRect" parent="ColorRect/MarginContainer/Panel" unique_id=506844785]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
expand_mode = 1
stretch_mode = 6

View File

@@ -0,0 +1,27 @@
extends Control
@export var photo_scene: PackedScene
@onready var grid_container: GridContainer = $%PhotoGrid
func _ready() -> void:
CollectionManager.on_photo_saved.connect(_create_photo_thumbnail)
load_gallery()
func load_gallery() -> void:
for child in grid_container.get_children():
child.queue_free()
var saved_photos = CollectionManager.saved_photos
for file_path in saved_photos:
if FileAccess.file_exists(file_path):
_create_photo_thumbnail(file_path)
func _create_photo_thumbnail(file_path: String) -> void:
var image = Image.load_from_file(file_path)
if image:
var photo: Photo = photo_scene.instantiate()
var texture = ImageTexture.create_from_image(image)
photo.setup(texture)
grid_container.add_child(photo)

View File

@@ -0,0 +1 @@
uid://k4iqrlkbjppl

View File

@@ -0,0 +1,50 @@
[gd_scene format=3 uid="uid://b6r787sik5yil"]
[ext_resource type="Script" uid="uid://k4iqrlkbjppl" path="res://core/photo_mode/ui/photo_gallery.gd" id="1_bp5uf"]
[ext_resource type="PackedScene" uid="uid://cvus62qkop3qi" path="res://core/photo_mode/ui/photo.tscn" id="2_45wok"]
[node name="PhotoGallery" type="Control" unique_id=354419843]
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_bp5uf")
photo_scene = ExtResource("2_45wok")
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=640179265]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -350.0
offset_top = -350.0
offset_right = 350.0
offset_bottom = 350.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=125147979]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=348576028]
layout_mode = 2
draw_focus_border = true
[node name="PhotoGrid" type="GridContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1187865988]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 2
theme_override_constants/h_separation = 12
theme_override_constants/v_separation = 12
columns = 3