diff --git a/icon.svg b/icon.svg new file mode 100644 index 00000000..9d8b7fa1 --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/log/logger/logs/v-demo-1.json b/log/logger/logs/v-demo-1.json new file mode 100644 index 00000000..76766b98 --- /dev/null +++ b/log/logger/logs/v-demo-1.json @@ -0,0 +1,64 @@ +{ + "items": [ + { + "event_date": "12/22/2024", + "item_id": "876528753689", + "log_content": "* 完成动画导入功能:\n * 自动检测目录下文件,帧排序后,导入到: res://config/animation/player_sprite_frames.tres \n * 文件名 mapping 到动画 animation 名称\n * 取第一帧作动画\n * 镜像动画\n * 网格化检阅", + "tags": [ + "2@code", + "3@art_tool", + "2@animation" + ], + "update_date": "12/22/2024" + }, + { + "event_date": "12/20/2024", + "item_id": "2546254798879", + "log_content": "* 游戏时间戳\n * 全局游戏时长记录\n * 当前存档时间状态:章节进度,游戏时长等\n* 场景管理器:发送消息浮窗\n* 事件系统:\n * 数据格式(四要素):\n * 时间(chapter_section,时间戳,周目状态等)\n * 地点(场景id,坐标)\n * 实体(实体id,状态)\n * 实体动作(action_id)\n * 流程:发送事件流程,消费事件流程\n* 存档:\n * 保存全局配置(如游戏总时长,周目数)\n * 分存档保存当前存档状态:时间、场景、实体\n* 组装实体:\n * 规格配置:进行中\n * 素材填充:进行中", + "tags": [ + "2@code", + "2@design" + ], + "update_date": "12/22/2024" + }, + { + "event_date": "12/19/2024", + "item_id": "52643524662456", + "log_content": "* 角色移动状态机功能:静止/行走/奔跑/躺卧移动/上下爬行\n * 允许锁定移动/锁定奔跑,躺卧移动与上下爬行互斥\n* 规格化调整:\n * UI 规格化:物品HUD,对话浮窗\n * 场景规格化:层次背景,角色\n* 镜头 Marker :随玩家移动方向轻微偏移,优化视野细节", + "tags": [ + "2@design", + "2@code", + "2@animation" + ], + "update_date": "12/22/2024" + }, + { + "event_date": "12/18/2024", + "item_id": "5423765786358", + "log_content": "确定资源类型,围绕规格化的资源\n基本工作流设计\n完善 HUD 功能", + "tags": [ + "2@ui", + "2@design" + ], + "update_date": "12/22/2024" + }, + { + "event_date": "12/17/2024", + "item_id": "11242354", + "log_content": "加入基础的游戏背景、角色行走功能", + "tags": [ + "2@code" + ], + "update_date": "12/22/2024" + }, + { + "event_date": "12/16/2024", + "item_id": "17343458739570", + "log_content": "新建项目等", + "tags": [ + "2@design" + ], + "update_date": "12/22/2024" + } + ] +} diff --git a/log/logger/project_log_panel.gd b/log/logger/project_log_panel.gd new file mode 100644 index 00000000..1f2ba693 --- /dev/null +++ b/log/logger/project_log_panel.gd @@ -0,0 +1,140 @@ +extends CanvasLayer + +class_name ProjectLogPanel + +enum TAG_TYPE{VERSION=0,FIX=1,FEATURE=2,TOOL=3} + +const TAG_TYPE_COLOR_DICT = { + TAG_TYPE.VERSION: Color.SKY_BLUE, + TAG_TYPE.FIX: Color.RED, + TAG_TYPE.FEATURE: Color.AQUAMARINE, + TAG_TYPE.TOOL: Color.ORCHID + } + +const TAG_DICT = { + TAG_TYPE.VERSION:["demo","0.1","1.0"], + TAG_TYPE.FIX:["bugfix","uxfix"], + TAG_TYPE.FEATURE:["code","design","ui","audio","art","animation"], + TAG_TYPE.TOOL:["art_tool","log_tool","manage_tool"], +} + +#const LOG_CONFIG_KEY_prefix = "log_panel_json_" +const LOG_DATA_DIR = "res://log/logger/logs/" +var log_files = [] +var current_log_file + +const IMAGE_DIR = "" + +@onready var add_btn := %Add +@onready var save_btn := %Save +@onready var archives_btn := %Archives as MenuButton +@onready var archives_label := %ArchiveLabel as Label +@onready var log_items_container = %LogItemsContainer + +const LOG_ITEM_GROUP = "log_item" +const LOG_ITEM_GROUP_MENBER = "log_item[{0}]" + +var log_item_scene = preload("res://log/logger/project_log_panel_item.tscn") + +var data = {"items":[]} as Dictionary + +func _ready(): + add_btn.pressed.connect(_on_add) + save_btn.pressed.connect(save_logs) + archives_btn.get_popup().id_pressed.connect(_on_archive_switch) + _load_archives() + _on_archive_switch(log_files.find("v-demo-mountwuwang.json")) + # var quat = Quaternion.from_euler(Vector3(deg_to_rad(10),0,deg_to_rad(10))) + # print(quat) + # quat = quat.normalized() + # print(quat) + # quat = Quaternion(Vector3(1,0,0),deg_to_rad(10)) * Quaternion(Vector3(0,0,1),deg_to_rad(10)) + # print(quat) + # quat = quat.normalized() + # print(quat) + +func _load_archives(): + log_files = DirAccess.get_files_at(LOG_DATA_DIR) as PackedStringArray + if log_files.size() == 0: + printerr("_load_archives no file exists:",LOG_DATA_DIR) + for i in range(0,log_files.size()): + archives_btn.get_popup().add_item(log_files[i],i) + _on_archive_switch(0) + +func _load_logs(): + var path = _get_curr_path() + if !FileAccess.file_exists(path): + printerr("_load_logs no file exists:",path) + return + var json = ResourceLoader.load(path,"",ResourceLoader.CACHE_MODE_IGNORE) + #print("load data=",json.data) + print("loaded data") + data = json.data + if !data.has("items"): + data["items"] = [] + _refresh_items() + +func _get_curr_path(): + return LOG_DATA_DIR + current_log_file + +func _refresh_items(): + var items = data["items"] as Array + get_tree().call_group(LOG_ITEM_GROUP, "queue_free") + for i in range(items.size()): + var item_instance = log_item_scene.instantiate() as Control + item_instance.add_to_group(LOG_ITEM_GROUP) + item_instance.add_to_group(LOG_ITEM_GROUP_MENBER.format([])) + item_instance.update.connect(_on_log_item_update.bind(i)) + #item_instance.save.connect(_on_log_item_save.bind(i)) + item_instance.remove.connect(_on_log_item_remove.bind(i)) + log_items_container.add_child(item_instance) + if items[i] is Dictionary: + item_instance.load_item(items[i]) + elif items[i] is String: + var item_dict = JSON.parse_string(items[i]) + item_instance.load_item(item_dict) + else : + push_error("unknow type items[",i,"]=",items[i]) + +func save_logs(): + var path = _get_curr_path() + if !FileAccess.file_exists(path): + printerr("save_logs no file exists:",path) + return + #print("save logs data=", data) + print("saved logs") + var file := FileAccess.open(path, FileAccess.WRITE_READ) as FileAccess + file.store_string(JSON.stringify(data, "\t")) + file.close() + +func _on_log_item_update(item:LogItem.ItemData, index:int): + var json = item.to_json_dict() + print("_on_log_item_update item=", json) + data["items"][index] = json + +func _on_log_item_save(item:LogItem.ItemData, index:int): + var json = item.to_json_dict() + print("_on_log_item_save item=", json) + data["items"][index] = json + save_logs() + +func _on_log_item_remove(index:int): + (data["items"] as Array).remove_at(index) + save_logs() + # reset index for each item + _refresh_items() + +func _on_add(): + (data["items"] as Array).push_front({}) + save_logs() + # reset index for each item + _refresh_items() + +func _on_archive_switch(id:int): + current_log_file = log_files[id] + archives_label.text = log_files[id] + _load_logs() + +func _unhandled_input(event): + if event.is_action("save"): + save_logs() diff --git a/log/logger/project_log_panel.tscn b/log/logger/project_log_panel.tscn new file mode 100644 index 00000000..bbe4adcc --- /dev/null +++ b/log/logger/project_log_panel.tscn @@ -0,0 +1,119 @@ +[gd_scene load_steps=4 format=3 uid="uid://b3n06jtdpqy5y"] + +[ext_resource type="Script" path="res://log/logger/project_log_panel.gd" id="1_2rhk7"] +[ext_resource type="PackedScene" uid="uid://bdy4u3e7rmo7f" path="res://ui/button/sound_button.tscn" id="2_71u5c"] +[ext_resource type="PackedScene" uid="uid://dhvo15vyxpkja" path="res://log/logger/project_log_panel_item.tscn" id="4_1yhgi"] + +[node name="ProjectLogPanel" type="CanvasLayer"] +script = ExtResource("1_2rhk7") + +[node name="MarginContainer" type="MarginContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 6 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 4 +alignment = 2 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 6 + +[node name="Title" type="Label" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_colors/font_color = Color(0.670588, 0.952941, 0.972549, 1) +theme_override_colors/font_shadow_color = Color(0.180392, 0.180392, 0.180392, 1) +theme_override_colors/font_outline_color = Color(0.0156863, 0.258824, 0.341176, 1) +theme_override_constants/shadow_offset_x = 2 +theme_override_constants/shadow_offset_y = 2 +theme_override_constants/outline_size = 4 +theme_override_constants/shadow_outline_size = 12 +theme_override_font_sizes/font_size = 24 +text = "Project Log" + +[node name="Add" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer" instance=ExtResource("2_71u5c")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +text = "Add" +script = null + +[node name="Save" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer" instance=ExtResource("2_71u5c")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +text = "Save" +script = null + +[node name="Archives" type="MenuButton" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Archives" +flat = false + +[node name="ArchiveLabel" type="Label" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="MarginContainer2" type="MarginContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/MarginContainer2"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/MarginContainer2/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 4 + +[node name="LogItemsContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/MarginContainer2/ScrollContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 +alignment = 1 + +[node name="LogItem" parent="MarginContainer/VBoxContainer/MarginContainer2/ScrollContainer/MarginContainer/LogItemsContainer" instance=ExtResource("4_1yhgi")] +visible = false +layout_mode = 2 + +[node name="LogItem2" parent="MarginContainer/VBoxContainer/MarginContainer2/ScrollContainer/MarginContainer/LogItemsContainer" instance=ExtResource("4_1yhgi")] +visible = false +layout_mode = 2 diff --git a/log/logger/project_log_panel_item.gd b/log/logger/project_log_panel_item.gd new file mode 100644 index 00000000..f78e6e75 --- /dev/null +++ b/log/logger/project_log_panel_item.gd @@ -0,0 +1,198 @@ +extends HBoxContainer + +class_name LogItem + +#signal save(data:ItemData) +signal update(data: ItemData) +signal remove + +const TAG_BTN_GROUP_PREFIX = "tag_btn" + +@onready var log_content := %LogContent as TextEdit +@onready var event_date := %EventDate as Label +#@onready var update_date := %LogUpdateDate as Label +@onready var tags_container := %TagsContainer as GridContainer +@onready var add_tag_btn := %AddTag as MenuButton +@onready var reload_btn = %Reload +@onready var save_btn = %Save +@onready var delete_btn = %Delete + +var original_data := ItemData.new() +var data: ItemData = original_data + + +class ItemData: + var item_id := _generate_random_id() + var log_content := "": + set(new_val): + if new_val != log_content: + update_date = _get_current_time() + log_content = new_val + var tags := []: + set(new_val): + update_date = _get_current_time() + tags = new_val + var event_date := _get_current_time() + var update_date := _get_current_time() + + func _generate_random_id() -> String: + # float time + var timestamp := Time.get_unix_time_from_system() + return str(int(timestamp * 10000 + randi_range(0, 10000))) + + func _get_current_time() -> String: + # Returns the current date as a dictionary of keys: year, month, day, and weekday. + var dict = Time.get_date_dict_from_system() + return str(dict["month"]) + "/" + str(dict["day"]) + "/" + str(dict["year"]) + + func duplicate() -> ItemData: + var item := ItemData.new() + item.item_id = item_id + item.log_content = log_content + item.tags = tags.duplicate() + item.event_date = event_date + item.update_date = update_date + return item + + func to_json_dict() -> Dictionary: + # JSON.stringify(self) doesn't work + return { + "item_id": item_id, + "log_content": log_content, + "tags": tags, + "event_date": event_date, + "update_date": update_date, + } + + static func parse(data: Dictionary) -> ItemData: + var item := ItemData.new() + if data.has("item_id"): + item.item_id = data["item_id"] + if data.has("tags"): + item.tags = data["tags"] + if data.has("log_content"): + item.log_content = data["log_content"] + if data.has("event_date"): + item.event_date = data["event_date"] + if data.has("update_date"): + item.update_date = data["update_date"] + return item + + +# {id:{"btn":btn,"key":key,"name":name,"id":id}} +static var tag_btns = {} +static var tag_key_to_id_map = {} + +static var tag_shadow_color = Color.html("1b1b1b") +static var tag_shadow_outline_size = 1 + + +# id->key +var current_tags = {} + +func _ready(): + _load_tag_popup() + reload_btn.pressed.connect(_on_reload_pressed) + #save_btn.pressed.connect(_save_item) + delete_btn.pressed.connect(_remove_signal) + log_content.text_changed.connect(_on_log_content_changed) + call_deferred("_update_signal") + #load_item(original_data) + + +static func _static_init(): + var tag_id := 0 + for tag_type in ProjectLogPanel.TAG_TYPE.values(): + var tag_color = ProjectLogPanel.TAG_TYPE_COLOR_DICT[tag_type] + for tag in ProjectLogPanel.TAG_DICT[tag_type]: + var tag_btn = Button.new() as Button + tag_btn.add_theme_color_override("font_color", tag_color) + tag_btn.add_theme_color_override("font_shadow_color", tag_shadow_color) + tag_btn.add_theme_constant_override("shadow_outline_size", tag_shadow_outline_size) + tag_btn.text = tag + var tag_key = _get_tag_key(tag_type, tag) + tag_btns[tag_id] = {"btn": tag_btn, "id": tag_id, "name": tag, "key": tag_key} + tag_key_to_id_map[tag_key] = tag_id + tag_id += 1 + + +static func _get_tag_key(tag_type: ProjectLogPanel.TAG_TYPE, tag: String): + return str(tag_type) + "@" + tag + + +func _load_tag_popup(): + var popup = add_tag_btn.get_popup() as PopupMenu + for btn_dict in tag_btns.values(): + popup.add_item(btn_dict["name"], btn_dict["id"]) + popup.id_pressed.connect(_on_popup_menu_pressed) + + +func _reload_tag_btns(): + var btn_group = TAG_BTN_GROUP_PREFIX + data.item_id + get_tree().call_group(btn_group, "queue_free") + current_tags.clear() + for tag_key in data["tags"]: + if !tag_key_to_id_map.has(tag_key): + push_error("tag_key has been removed!! tag_key=", tag_key) + continue + var tag_id = tag_key_to_id_map[tag_key] + current_tags[tag_id] = tag_key + var btn = (tag_btns[tag_id]["btn"] as Button).duplicate() + btn.pressed.connect(_on_tag_btn_pressed.bind(btn, tag_id)) + btn.add_to_group(btn_group) + tags_container.add_child(btn) + + +func load_item(new_data: Dictionary): + if new_data.keys().size() != 0: + original_data = ItemData.parse(new_data) + _reload_original_data() + + +func _reload_original_data(): + data = original_data.duplicate() + #print("_load_item_data data=", data) + log_content.text = data.log_content + event_date.text = data.event_date + #update_date.text = data["update_date"] + _reload_tag_btns() + + +func _on_popup_menu_pressed(id: int): + #{"btn":btn,"key":key,"name":name,"id":id} + if !current_tags.has(id): + current_tags[id] = tag_btns[id]["key"] + data.tags = current_tags.values() + _update_signal() + #_save_item() + _reload_tag_btns() + + +func _on_log_content_changed(): + data["log_content"] = log_content.text + _update_signal() + + +func _on_tag_btn_pressed(btn: Control, id: int): + current_tags.erase(id) + btn.queue_free() + data.tags = current_tags.values() + _update_signal() + #_save_item() + + +#func _save_item(): +#save.emit(data) + + +func _update_signal(): + update.emit(data) + + +func _remove_signal(): + remove.emit() + + +func _on_reload_pressed(): + _reload_original_data() + _update_signal() diff --git a/log/logger/project_log_panel_item.tscn b/log/logger/project_log_panel_item.tscn new file mode 100644 index 00000000..bc84b9c2 --- /dev/null +++ b/log/logger/project_log_panel_item.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=3 format=3 uid="uid://dhvo15vyxpkja"] + +[ext_resource type="Script" path="res://log/logger/project_log_panel_item.gd" id="1_gn7ev"] +[ext_resource type="PackedScene" uid="uid://bdy4u3e7rmo7f" path="res://ui/button/sound_button.tscn" id="2_wb6pu"] + +[node name="LogItem" type="HBoxContainer"] +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -4.0 +offset_bottom = 4.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_gn7ev") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="LogContent" type="TextEdit" parent="MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "123" +wrap_mode = 1 +scroll_fit_content_height = true + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="TagsContainer" type="GridContainer" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +columns = 2 + +[node name="AddTag" type="MenuButton" parent="HBoxContainer/TagsContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Tags" + +[node name="VBoxContainer2" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="EventDate" type="Label" parent="VBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0.929412, 0.929412, 0.929412, 1) +theme_override_colors/font_shadow_color = Color(0.105882, 0.105882, 0.105882, 1) +theme_override_constants/shadow_offset_x = 1 +theme_override_constants/shadow_offset_y = 1 +theme_override_constants/shadow_outline_size = 4 +text = "01/01/24" + +[node name="LogUpdateDate" type="Label" parent="VBoxContainer2"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "01/01/24" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer2"] +layout_mode = 2 + +[node name="Reload" parent="VBoxContainer2/HBoxContainer" instance=ExtResource("2_wb6pu")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Reload" +script = null + +[node name="Save" parent="VBoxContainer2/HBoxContainer" instance=ExtResource("2_wb6pu")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Save" +script = null + +[node name="Delete" parent="VBoxContainer2/HBoxContainer" instance=ExtResource("2_wb6pu")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Delete" +script = null diff --git a/manager/archive_manager/archive_manager.gd b/manager/archive_manager/archive_manager.gd new file mode 100644 index 00000000..365f34e5 --- /dev/null +++ b/manager/archive_manager/archive_manager.gd @@ -0,0 +1,125 @@ +extends Node + +@export var user_archive_root_dir := "user://archive/" # must end with "/" +@export var save_dir_prefix := "save" + +var accessible := false +var archives: Array[int] # archive id list in ascending order + +var managers = { + "TimeManager": TimeManager, + "EntityManager": EntityManager, + "SceneManager": SceneManager, + "InputManager": InputManager, + # "ArchiveManager" : ArchiveManager, #self + # "AudioManager" : AudioManager, + # "EventManager" : EventManager, + # "CameraManager" : CameraManager, + # "DialogManager" : DialogManager, + # "CgManager": CgManager, +} + +var autosave_timer := Timer.new() + +func _ready() -> void: + accessible = _check_archive_dirs() + if !accessible: + return + # _open_log_file() + add_child(autosave_timer) + autosave_timer.wait_time = GlobalConfigManager.config.auto_save_seconds + autosave_timer.one_shot = false + autosave_timer.timeout.connect(_try_auto_save) + autosave_timer.stop() + GlobalConfigManager.config.current_selected_save_changed.connect(_check_autosave_options) + GlobalConfigManager.config.auto_save_seconds_changed.connect(_check_autosave_options) + load_all() + SceneManager.pop_notification("预加载完成") + + + +func _check_autosave_options(): + if ( + GlobalConfigManager.config.auto_save_seconds > 5 + and GlobalConfigManager.config.current_selected_save >= 0 + ): + autosave_timer.start() + else: + autosave_timer.stop() + + +func _try_auto_save(): + if ( + GlobalConfigManager.config.auto_save_seconds > 5 + and GlobalConfigManager.config.current_selected_save >= 0 + ): + save_all() + SceneManager.pop_notification("自动保存成功") + + +func _check_archive_dirs() -> bool: + # Check if the archive directory is accessible + if !DirAccess.dir_exists_absolute(user_archive_root_dir): + DirAccess.make_dir_recursive_absolute(user_archive_root_dir) + print("Create archive directory:", user_archive_root_dir) + var archive_dir_access = DirAccess.open(user_archive_root_dir) + if !archive_dir_access: + printerr("Archive directory is not accessible") + # TODO pop up a dialog to inform the user + return false + var files = archive_dir_access.get_files() + # get archive number + for file in files: + if file.begins_with(save_dir_prefix): + var id_str = file.substr(save_dir_prefix.length()) + var id = int(id_str) + archives.append(id) + archives.sort() + return true + + +func save_all() -> void: + # save config + var config = GlobalConfigManager.config + var path = user_archive_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT + ResourceSaver.save(config, path) + if not accessible or GlobalConfigManager.config.current_selected_save == -1: + return + # Save all managers by the current selected save + for manager in managers: + # .tres + var res = managers[manager].get_data_res() + if !res: + continue + # save to file + path = ( + user_archive_root_dir + + str(config.current_selected_save) + + "/" + + manager + + GlobalConfig.RES_FILE_FORMAT + ) + ResourceSaver.save(res, path) + + +func load_all() -> void: + _check_archive_dirs() + # load config + var path = user_archive_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT + var config + if FileAccess.file_exists(path): + config = ResourceLoader.load(path) + GlobalConfigManager.load_data_res(config) + else: + config = GlobalConfigManager.config + # save config + ResourceSaver.save(config, path) + if not accessible or config.current_selected_save == -1: + return + # Load managers by the current selected save + path = user_archive_root_dir + save_dir_prefix + str(config.current_selected_save) + "/" + for manager in managers: + var manager_path = path + manager + GlobalConfig.RES_FILE_FORMAT + if FileAccess.file_exists(manager_path): + var res = ResourceLoader.load(manager_path) + managers[manager].load_data_res(res) diff --git a/manager/assemble/asset_metadata.gd b/manager/assemble/asset_metadata.gd new file mode 100644 index 00000000..a0e08635 --- /dev/null +++ b/manager/assemble/asset_metadata.gd @@ -0,0 +1 @@ +class_name AssetMetadata extends Resource diff --git a/manager/assemble/dialog/dialog_manager.gd b/manager/assemble/dialog/dialog_manager.gd new file mode 100644 index 00000000..fae2c760 --- /dev/null +++ b/manager/assemble/dialog/dialog_manager.gd @@ -0,0 +1,11 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/manager/assemble/entity/entity.gd b/manager/assemble/entity/entity.gd new file mode 100644 index 00000000..c7309503 --- /dev/null +++ b/manager/assemble/entity/entity.gd @@ -0,0 +1,11 @@ +class_name AssetMetadata extends Resource + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/manager/assemble/entity/entity_manager.gd b/manager/assemble/entity/entity_manager.gd new file mode 100644 index 00000000..c0655266 --- /dev/null +++ b/manager/assemble/entity/entity_manager.gd @@ -0,0 +1,18 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + +func get_data_res() -> Resource: + return null + + +func load_data_res(data: Resource) -> void: + pass \ No newline at end of file diff --git a/manager/assemble/scene/scene_manager.gd b/manager/assemble/scene/scene_manager.gd new file mode 100644 index 00000000..853156e8 --- /dev/null +++ b/manager/assemble/scene/scene_manager.gd @@ -0,0 +1,15 @@ +extends Node + +func pop_notification(msg: String, number := 1) -> void: + var notification_node = get_node_or_null("/root/Main/UILayer/Notification") + if notification_node: + notification_node.show_notification(msg, number) + else: + printerr("Notification node not found") + +func get_data_res() -> Resource: + return null + + +func load_data_res(data: Resource) -> void: + pass \ No newline at end of file diff --git a/manager/audio_manager/audio_manager.gd b/manager/audio_manager/audio_manager.gd new file mode 100644 index 00000000..fae2c760 --- /dev/null +++ b/manager/audio_manager/audio_manager.gd @@ -0,0 +1,11 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/manager/camera_manager/camera_manager.gd b/manager/camera_manager/camera_manager.gd new file mode 100644 index 00000000..fae2c760 --- /dev/null +++ b/manager/camera_manager/camera_manager.gd @@ -0,0 +1,11 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/manager/cg_manager/cg_manager.gd b/manager/cg_manager/cg_manager.gd new file mode 100644 index 00000000..fae2c760 --- /dev/null +++ b/manager/cg_manager/cg_manager.gd @@ -0,0 +1,11 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/manager/config_manager/global_config.gd b/manager/config_manager/global_config.gd new file mode 100644 index 00000000..ad6826ba --- /dev/null +++ b/manager/config_manager/global_config.gd @@ -0,0 +1,34 @@ +class_name GlobalConfig extends Resource + +# .res would be binary encoded, .tres is text encoded +const RES_FILE_FORMAT = ".tres" + +const CONFIG_FILE_NAME = "config" + +const DEBUG = true +#const DEBUG = false + +const CANVAS_LAYER_VIGNETTE = 1000 +const CANVAS_LAYER_PROP_INSPECTOR = 100 +const CANVAS_LAYER_UI = 99 +const CANVAS_LAYER_FG = 50 +const CANVAS_LAYER_BG = -100 + +const COLOR_BOARDER = Color.BLACK + +const _ART_RESOURCE_BASE_DIR = "res://asset/art/" + +signal current_selected_save_changed +signal auto_save_seconds_changed + +# 注意:编辑与新增属性时,需要同时编辑 GlobalConfigManager.gd 中的 load_data_res 方法 +@export var game_total_seconds := 0 # 游戏总时长 +@export var game_rounds := 1 # 当前周目数 +@export var current_selected_save := -1: # 当前选定存档 id, -1 means no save selected + set(val): + current_selected_save = val + current_selected_save_changed.emit() +@export var auto_save_seconds := 60: + set(val): + auto_save_seconds = val + auto_save_seconds_changed.emit() diff --git a/manager/config_manager/global_config_manager.gd b/manager/config_manager/global_config_manager.gd new file mode 100644 index 00000000..99593ec1 --- /dev/null +++ b/manager/config_manager/global_config_manager.gd @@ -0,0 +1,15 @@ +extends Node + +var config = GlobalConfig.new() + + +func get_data_res() -> Resource: + return config + + +func load_data_res(data: Resource) -> void: + var new_config = data as GlobalConfig + config.game_total_seconds = new_config.game_total_seconds + config.game_rounds = new_config.game_rounds + config.current_selected_save = new_config.current_selected_save + config.auto_save_seconds = new_config.auto_save_seconds diff --git a/manager/event_manager/event_manager.gd b/manager/event_manager/event_manager.gd new file mode 100644 index 00000000..93ef25bd --- /dev/null +++ b/manager/event_manager/event_manager.gd @@ -0,0 +1,31 @@ +extends Node + +# 事件注册表 +# { entity: [MatcherConsumer] } +var _event_registry: Dictionary = {} + +class MatcherConsumer extends RefCounted: + var _matcher: GameEventMatcher + var _consumer: Callable + + func _init(matcher: GameEventMatcher, consumer: Callable) -> void: + self._matcher = matcher + self._consumer = consumer + +# 注册事件 +func register_event_matcher(event_matcher: GameEventMatcher, consumer: Callable) -> void: + if !event_matcher.entity: + return + var matcher_consumer = MatcherConsumer.new(event_matcher, consumer) + if event_matcher.entity not in _event_registry: + _event_registry[event_matcher.entity] = [] + _event_registry[event_matcher.entity].append(matcher_consumer) + +# 触发事件 +func trigger_event(event: GameEvent) -> void: + if event.entity not in _event_registry: + return + for matcher_consumer in _event_registry[event.entity]: + # matcher_consumer = matcher_consumer as MatcherConsumer + if matcher_consumer._matcher.match(event): + matcher_consumer._consumer.call(event) diff --git a/manager/event_manager/game_event.gd b/manager/event_manager/game_event.gd new file mode 100644 index 00000000..d1d5ce87 --- /dev/null +++ b/manager/event_manager/game_event.gd @@ -0,0 +1,19 @@ +class_name GameEvent extends Resource + +# * 事件&剧情管理器: player / release / debug 事件,触发者:玩家/系统(系统触发意味着进入 CG) +# * 事件定义:「通变之谓事」 +# * 事件发送机制:玩家进行特定操作后,发送事件进度(开始(注意到),尝试&进度,完成:成功or失败) +# * 发送内容 +# * 时间点:玩家现实时间(系统时间),游戏时长(时间戳),章节剧情时间点(事件编号) +# * 地点:场景编号,位置坐标 +# * 实体:道具/NPC/部署点 +# * 激发器类型:地图区域(手动部署) / 玩家特定操作(自动发送)/ 实体信号(监听玩家特定操作/状态变化) +# * 事件订阅机制:订阅开始/进度/成功/失败,触发特定响应 +# * 消费器能力:执行 CG(锁定/无锁定,剧本/特效) / 执行实体变化脚本(赠送/消耗/状态变化) +# * 保存与加载 +@export var event_id: int +@export var packed_time: PackedTime +@export var scene: String +@export var scene_position: Vector2 +@export var entity: String +@export var entity_action: String diff --git a/manager/event_manager/game_event_matcher.gd b/manager/event_manager/game_event_matcher.gd new file mode 100644 index 00000000..1a831180 --- /dev/null +++ b/manager/event_manager/game_event_matcher.gd @@ -0,0 +1,43 @@ +class_name GameEventMatcher extends Resource + +# match event under certain conditions + +# can't be empty +@export var entity: String +# empty means any action +@export var entity_actions: Array[String] +# empty means any scene +@export var scenes: Array[String] +# regex matcher, "[chapter]_[section]", * means any +# e.g. 3_* means any section in chapter 3 +# empty means any chapter_section +@export var chapter_section: Array[String] + +# check if the event matches the conditions +func match(event: GameEvent) -> bool: + if !entity or entity != event.entity: + return false + if entity_actions.size() > 0 and !entity_actions.has(event.entity_action): + return false + if scenes.size() > 0 and !scenes.has(event.scene): + return false + if chapter_section.size() > 0: + var chapter := str(event.packed_time.chapter) + var section := str(event.packed_time.section) + var matched := false + for chapter_section_matcher in chapter_section: + # regex match + var parts = chapter_section_matcher.split("_") + if parts.size() != 2: + continue + var chapter_matcher = parts[0] + var section_matcher = parts[1] + if chapter_matcher == "*" or chapter_matcher == chapter: + matched = true + break + if section_matcher == "*" or section_matcher == section: + matched = true + break + if !matched: + return false + return true \ No newline at end of file diff --git a/manager/input/input_manager.gd b/manager/input/input_manager.gd new file mode 100644 index 00000000..8daa926c --- /dev/null +++ b/manager/input/input_manager.gd @@ -0,0 +1,19 @@ +extends Node + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + + +func get_data_res() -> Resource: + return null + + +func load_data_res(data: Resource) -> void: + pass \ No newline at end of file diff --git a/manager/time_manager/packed_time.gd b/manager/time_manager/packed_time.gd new file mode 100644 index 00000000..89d8b431 --- /dev/null +++ b/manager/time_manager/packed_time.gd @@ -0,0 +1,12 @@ +class_name PackedTime extends Resource + +# 系统时间,游戏时长,章节时间点 + +@export var game_archive_id := 0 +@export var game_rounds := 0 +@export var timestamp := "2024-12-20 08:00:00" #system +@export var game_seconds_all := 0 #game seconds since current archive start +@export var game_seconds_current := 0 #game seconds since last chapter +@export var chapter := 0 #chapter id +@export var section := 0 #section id + diff --git a/manager/time_manager/time_manager.gd b/manager/time_manager/time_manager.gd new file mode 100644 index 00000000..f959f99c --- /dev/null +++ b/manager/time_manager/time_manager.gd @@ -0,0 +1,63 @@ +extends Node + +@export var current_chapter := 0 +@export var current_section := 0 +@export var game_seconds_all := 0 +@export var game_seconds_current := 0 + +var timer: Timer + +func _ready() -> void: + timer = Timer.new() + timer.wait_time = 1 + timer.one_shot = false + timer.timeout.connect(_on_timer_timeout) + add_child(timer) + timer.start() + + +func _on_timer_timeout(): + game_seconds_all += 1 + game_seconds_current += 1 + GlobalConfigManager.config.game_total_seconds += 1 + + +func pack_current_time(): + var packed_time = PackedTime.new() + packed_time.time = Time.get_datetime_string_from_system(false, true) + packed_time.chapter = current_chapter + packed_time.section = current_section + packed_time.game_archive_id = ArchiveManager.current_archive_id + packed_time.game_seconds_all = game_seconds_all + packed_time.game_seconds_current = game_seconds_current + + +# for log use +func get_concise_timemark() -> String: + var hour := game_seconds_current / 3600 as int + var minute := (game_seconds_current % 3600) / 60 as int + var second := game_seconds_current % 60 + return ( + "r" + + str(ArchiveManager.current_archive_id) + + "_c" + + str(current_chapter) + + "_s" + + str(current_section) + + " " + + str(hour) + + ":" + + str(minute) + + ":" + + str(second) + ) + +func get_data_res() -> Resource: + return pack_current_time() + +func load_data_res(data: Resource) -> void: + var packed_time = data as PackedTime + game_seconds_all = packed_time.game_seconds_all + game_seconds_current = packed_time.game_seconds_current + current_chapter = packed_time.chapter + current_section = packed_time.section diff --git a/project.godot b/project.godot new file mode 100644 index 00000000..4e303293 --- /dev/null +++ b/project.godot @@ -0,0 +1,114 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[addons] + +resources_spreadsheet_view/array_color_tint=100.0 +resources_spreadsheet_view/color_rows=true +resources_spreadsheet_view/array_min_width=128.0 +resources_spreadsheet_view/resource_preview_size=32.0 +resources_spreadsheet_view/clip_headers=false +resources_spreadsheet_view/dupe_arrays=true +resources_spreadsheet_view/context_menu_on_leftclick=false + +[application] + +config/name="xiandie" +run/main_scene="res://scene/main.tscn" +config/features=PackedStringArray("4.3", "Mobile") +config/icon="res://icon.svg" + +[audio] + +buses/default_bus_layout="res://config/default_bus_layout.tres" + +[autoload] + +DebugMenu="*res://addons/debug_menu/debug_menu.tscn" +GlobalConfigManager="*res://manager/config_manager/global_config_manager.gd" +TimeManager="*res://manager/time_manager/time_manager.gd" +EntityManager="*res://manager/assemble/entity/entity_manager.gd" +SceneManager="*res://manager/assemble/scene/scene_manager.gd" +AudioManager="*res://manager/audio_manager/audio_manager.gd" +EventManager="*res://manager/event_manager/event_manager.gd" +DialogManager="*res://manager/assemble/dialog/dialog_manager.gd" +CameraManager="*res://manager/camera_manager/camera_manager.gd" +CgManager="*res://manager/cg_manager/cg_manager.gd" +InputManager="*res://manager/input/input_manager.gd" +ArchiveManager="*res://manager/archive_manager/archive_manager.gd" + +[display] + +window/size/viewport_width=564 +window/size/viewport_height=317 +window/size/window_width_override=1692 +window/size/window_height_override=951 +window/stretch/mode="canvas_items" +window/stretch/scale_mode="integer" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/debug_menu/plugin.cfg", "res://addons/project-statistics/plugin.cfg") + +[gui] + +theme/custom="res://config/default_theme.tres" +theme/custom_font="res://asset/font/zpix-commercial-one-grand.ttf" + +[input] + +save={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +run={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +jump={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +renderer/rendering_method="mobile" + +[statistics] + +force_include=PackedStringArray() diff --git a/scene/camera/camera_focus_marker.gd b/scene/camera/camera_focus_marker.gd new file mode 100644 index 00000000..d7c7ff31 --- /dev/null +++ b/scene/camera/camera_focus_marker.gd @@ -0,0 +1,6 @@ +extends Marker2D + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + diff --git a/scene/camera/camera_focus_marker.tscn b/scene/camera/camera_focus_marker.tscn new file mode 100644 index 00000000..1f0fafd0 --- /dev/null +++ b/scene/camera/camera_focus_marker.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cqkeegrcdjyg4"] + +[ext_resource type="Script" path="res://scene/camera/camera_focus_marker.gd" id="1_7t4e6"] + +[node name="CameraFocusMarker" type="Marker2D"] +script = ExtResource("1_7t4e6") diff --git a/scene/camera/main_camera.gd b/scene/camera/main_camera.gd new file mode 100644 index 00000000..6b14655d --- /dev/null +++ b/scene/camera/main_camera.gd @@ -0,0 +1,9 @@ +extends Camera2D + +class_name MainCamera + +func _ready() -> void: + enabled = true + #position_smoothing_enabled = false + position_smoothing_speed = 10 + position_smoothing_enabled = true diff --git a/scene/camera/main_camera.tscn b/scene/camera/main_camera.tscn new file mode 100644 index 00000000..e52a3773 --- /dev/null +++ b/scene/camera/main_camera.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://ogyvstscr0kx"] + +[ext_resource type="Script" path="res://scene/camera/main_camera.gd" id="1_pwiuw"] + +[node name="MainCamera" type="Camera2D"] +script = ExtResource("1_pwiuw") diff --git a/scene/dialog/dialog_container.gd b/scene/dialog/dialog_container.gd new file mode 100644 index 00000000..e07a0bfc --- /dev/null +++ b/scene/dialog/dialog_container.gd @@ -0,0 +1 @@ +extends Control diff --git a/scene/dialog/dialog_container.tscn b/scene/dialog/dialog_container.tscn new file mode 100644 index 00000000..37b1c84f --- /dev/null +++ b/scene/dialog/dialog_container.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=3 format=3 uid="uid://dmkt1roqc4he7"] + +[ext_resource type="Script" path="res://scene/dialog/dialog_container.gd" id="1_s1ka3"] +[ext_resource type="Theme" uid="uid://j42sexotwnvk" path="res://config/default_theme.tres" id="2_q6yks"] + +[node name="DialogContainer" type="MarginContainer"] +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -86.0 +grow_horizontal = 2 +grow_vertical = 0 +size_flags_vertical = 8 +theme_override_constants/margin_left = 32 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 42 +script = ExtResource("1_s1ka3") + +[node name="DialogPanelContainer" type="PanelContainer" parent="."] +layout_mode = 2 + +[node name="DialogMarginContainer" type="MarginContainer" parent="DialogPanelContainer"] +layout_mode = 2 +theme = ExtResource("2_q6yks") +theme_type_variation = &"dialog_container" +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 8 + +[node name="RichTextLabel" type="RichTextLabel" parent="DialogPanelContainer/DialogMarginContainer"] +custom_minimum_size = Vector2(460, 0) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +mouse_filter = 2 +text = "Dialog: test +2 +3 +4 +5" +fit_content = true diff --git a/scene/ground/boarder_frame.gd b/scene/ground/boarder_frame.gd new file mode 100644 index 00000000..ebe1acd3 --- /dev/null +++ b/scene/ground/boarder_frame.gd @@ -0,0 +1,8 @@ +extends Control + +@onready var color_top := %ColorRectTop as ColorRect +@onready var color_bottom := %ColorRectBottom as ColorRect + +func _ready() -> void: + color_top.color = GlobalConfig.COLOR_BOARDER + color_bottom.color = GlobalConfig.COLOR_BOARDER diff --git a/scene/ground/boarder_frame.tscn b/scene/ground/boarder_frame.tscn new file mode 100644 index 00000000..80d109c4 --- /dev/null +++ b/scene/ground/boarder_frame.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=2 format=3 uid="uid://o4a2eluydtep"] + +[ext_resource type="Script" path="res://scene/ground/boarder_frame.gd" id="1_50vde"] + +[node name="BoarderFrame" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_50vde") + +[node name="ColorRectTop" type="ColorRect" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(564, 38.5) +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -282.0 +offset_right = 282.0 +offset_bottom = 40.0 +grow_horizontal = 2 +color = Color(0.372549, 0.0156863, 0.0156863, 1) + +[node name="ColorRectBottom" type="ColorRect" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(564, 38.5) +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -282.0 +offset_top = -38.5 +offset_right = 282.0 +grow_horizontal = 2 +grow_vertical = 0 +color = Color(0.372549, 0.0156863, 0.0156863, 1) diff --git a/scene/ground/parallax_background.gd b/scene/ground/parallax_background.gd new file mode 100644 index 00000000..573448cd --- /dev/null +++ b/scene/ground/parallax_background.gd @@ -0,0 +1,12 @@ +extends ParallaxBackground + +# +## Called every frame. 'delta' is the elapsed time since the previous frame. +#func _physics_process(delta: float) -> void: + #var x = Input.get_axis("left", "right") + #var y = Input.get_axis("up", "down") + #camera.position.x += delta * x * 1000 + #camera.position.y += delta * y * 1000 + +func _ready() -> void: + layer = GlobalConfig.CANVAS_LAYER_BG diff --git a/scene/ground/parallax_background.tscn b/scene/ground/parallax_background.tscn new file mode 100644 index 00000000..79f58c95 --- /dev/null +++ b/scene/ground/parallax_background.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://d0mnndg67dru1"] + +[ext_resource type="Script" path="res://scene/ground/parallax_background.gd" id="1_w8735"] +[ext_resource type="Texture2D" uid="uid://dukm6cix2kghn" path="res://asset/art/第一章/场景五(1012外间现实版)/1012外间.png" id="2_6t5qy"] + +[node name="ParallaxBackground" type="ParallaxBackground"] +scroll_base_offset = Vector2(-250, -200) +script = ExtResource("1_w8735") + +[node name="ParallaxLayer" type="ParallaxLayer" parent="."] + +[node name="Sprite2D" type="Sprite2D" parent="ParallaxLayer"] +scale = Vector2(1.33649, 1.32083) +texture = ExtResource("2_6t5qy") +centered = false diff --git a/scene/ground/parallax_foreground.gd b/scene/ground/parallax_foreground.gd new file mode 100644 index 00000000..d7ce7730 --- /dev/null +++ b/scene/ground/parallax_foreground.gd @@ -0,0 +1,5 @@ +extends ParallaxBackground + + +func _ready() -> void: + layer = GlobalConfig.CANVAS_LAYER_FG diff --git a/scene/ground/parallax_foreground.tscn b/scene/ground/parallax_foreground.tscn new file mode 100644 index 00000000..c87ed0e6 --- /dev/null +++ b/scene/ground/parallax_foreground.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=3 uid="uid://02lmmldikfar"] + +[ext_resource type="Script" path="res://scene/ground/parallax_foreground.gd" id="1_32n5c"] +[ext_resource type="Texture2D" uid="uid://c0l7pkwvbrgey" path="res://asset/art/第一章/场景五(1012外间现实版)/前景.png" id="3_4g2yq"] +[ext_resource type="PackedScene" uid="uid://o4a2eluydtep" path="res://scene/ground/boarder_frame.tscn" id="4_18dtv"] + +[node name="ParallaxForeground" type="ParallaxBackground"] +scroll_base_offset = Vector2(-250, -200) +script = ExtResource("1_32n5c") + +[node name="ParallaxLayer2" type="ParallaxLayer" parent="."] +motion_scale = Vector2(1.1, 1.1) +motion_mirroring = Vector2(2000, 1300) + +[node name="Sprite2D" type="Sprite2D" parent="ParallaxLayer2"] +position = Vector2(2, 73) +scale = Vector2(1.33886, 1.05) +texture = ExtResource("3_4g2yq") +centered = false + +[node name="ShadedFrame" parent="." instance=ExtResource("4_18dtv")] diff --git a/scene/hud/prop_hud.gd b/scene/hud/prop_hud.gd new file mode 100644 index 00000000..e07a0bfc --- /dev/null +++ b/scene/hud/prop_hud.gd @@ -0,0 +1 @@ +extends Control diff --git a/scene/hud/prop_hud.tscn b/scene/hud/prop_hud.tscn new file mode 100644 index 00000000..6beeec37 --- /dev/null +++ b/scene/hud/prop_hud.tscn @@ -0,0 +1,85 @@ +[gd_scene load_steps=12 format=3 uid="uid://dc778gsjfr3ky"] + +[ext_resource type="Script" path="res://scene/hud/prop_hud.gd" id="1_bbv0a"] +[ext_resource type="Texture2D" uid="uid://chyumeohdhwnh" path="res://asset/art/ui/道具快捷栏/normal_left.png" id="2_bjc2b"] +[ext_resource type="Texture2D" uid="uid://cvepj6u80c5wv" path="res://asset/art/ui/道具快捷栏/pressed_left.png" id="3_fca7p"] +[ext_resource type="Script" path="res://ui/button/sound_texture_button.gd" id="3_xijdf"] +[ext_resource type="Texture2D" uid="uid://bospbmb0gr2sb" path="res://asset/art/ui/道具快捷栏/Prop.png" id="5_6tt77"] +[ext_resource type="Script" path="res://config/audio/audio_stream_collection.gd" id="5_wmvnw"] +[ext_resource type="Resource" uid="uid://cor5ec6ogq3fe" path="res://config/audio/casino/card_slide.tres" id="6_32vbt"] +[ext_resource type="Texture2D" uid="uid://optvgar5g2c" path="res://asset/art/道具/第一章/绳子物品.png" id="8_kpmil"] +[ext_resource type="Texture2D" uid="uid://bgsbwhy1c2jna" path="res://asset/art/ui/道具快捷栏/hand.png" id="9_0crjo"] +[ext_resource type="Texture2D" uid="uid://boqpr7i2a5uan" path="res://asset/art/ui/道具快捷栏/normal_right.png" id="10_vkaik"] +[ext_resource type="Texture2D" uid="uid://daj2n408y2vfp" path="res://asset/art/ui/道具快捷栏/pressed_right.png" id="11_a512b"] + +[node name="PropHUD" type="HBoxContainer"] +offset_left = 10.0 +offset_top = 4.0 +offset_right = 98.0 +offset_bottom = 36.0 +scale = Vector2(0.3, 0.3) +script = ExtResource("1_bbv0a") + +[node name="LeftMargin" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 8 + +[node name="LeftButton" type="TextureButton" parent="LeftMargin"] +layout_mode = 2 +size_flags_vertical = 4 +texture_normal = ExtResource("2_bjc2b") +texture_pressed = ExtResource("3_fca7p") +stretch_mode = 5 +script = ExtResource("3_xijdf") + +[node name="HudPanel" type="TextureButton" parent="."] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +size_flags_vertical = 4 +texture_normal = ExtResource("5_6tt77") +stretch_mode = 5 +script = ExtResource("3_xijdf") +audio_collections = Array[ExtResource("5_wmvnw")]([ExtResource("6_32vbt")]) + +[node name="Prop" type="TextureRect" parent="HudPanel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -73.0 +offset_top = -41.0 +offset_right = 73.0 +offset_bottom = 41.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("8_kpmil") + +[node name="Mark" type="TextureRect" parent="HudPanel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -14.0 +offset_top = -15.0 +grow_horizontal = 0 +grow_vertical = 0 +texture = ExtResource("9_0crjo") + +[node name="RightMargin" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_right = 8 + +[node name="RightButton" type="TextureButton" parent="RightMargin"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +texture_normal = ExtResource("10_vkaik") +texture_pressed = ExtResource("11_a512b") +stretch_mode = 5 +script = ExtResource("3_xijdf") diff --git a/scene/hud/prop_hud_item_2d.tscn b/scene/hud/prop_hud_item_2d.tscn new file mode 100644 index 00000000..64d15c0e --- /dev/null +++ b/scene/hud/prop_hud_item_2d.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://dpgmgc5qf0apc"] + +[sub_resource type="GDScript" id="GDScript_5nh3u"] +script/source = "extends StaticBody2D + +func _ready() -> void: + pass" + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_gyjm3"] +size = Vector2(38, 38) + +[node name="PropHudItem2D" type="StaticBody2D"] +script = SubResource("GDScript_5nh3u") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_gyjm3") diff --git a/scene/main.gd b/scene/main.gd new file mode 100644 index 00000000..62417a82 --- /dev/null +++ b/scene/main.gd @@ -0,0 +1,4 @@ +extends Node2D + +func _ready() -> void: + $UILayer.layer = GlobalConfig.CANVAS_LAYER_VIGNETTE diff --git a/scene/main.tscn b/scene/main.tscn new file mode 100644 index 00000000..28f282ac --- /dev/null +++ b/scene/main.tscn @@ -0,0 +1,48 @@ +[gd_scene load_steps=12 format=3 uid="uid://dygvcmykn02n8"] + +[ext_resource type="PackedScene" uid="uid://d0mnndg67dru1" path="res://scene/ground/parallax_background.tscn" id="1_f7g4d"] +[ext_resource type="Script" path="res://scene/main.gd" id="1_pks84"] +[ext_resource type="PackedScene" uid="uid://3gk1gxwanw24" path="res://ui/vignette/vignette_shading.tscn" id="2_d1re1"] +[ext_resource type="PackedScene" uid="uid://dmkt1roqc4he7" path="res://scene/dialog/dialog_container.tscn" id="3_prpss"] +[ext_resource type="PackedScene" uid="uid://dc778gsjfr3ky" path="res://scene/hud/prop_hud.tscn" id="4_t7gb2"] +[ext_resource type="PackedScene" uid="uid://5g07x6q7wwr1" path="res://scene/notification/notification.tscn" id="5_3gg5t"] +[ext_resource type="PackedScene" uid="uid://cekhj65axie0p" path="res://scene/popup/prop_inspector_2d.tscn" id="5_ux0rw"] +[ext_resource type="PackedScene" uid="uid://cjhw5ecygrqty" path="res://scene/player/player_2d.tscn" id="6_6geb0"] +[ext_resource type="PackedScene" uid="uid://cqkeegrcdjyg4" path="res://scene/camera/camera_focus_marker.tscn" id="7_n7qcv"] +[ext_resource type="PackedScene" uid="uid://ogyvstscr0kx" path="res://scene/camera/main_camera.tscn" id="8_nj084"] +[ext_resource type="PackedScene" uid="uid://02lmmldikfar" path="res://scene/ground/parallax_foreground.tscn" id="11_anxqu"] + +[node name="Main" type="Node2D"] +script = ExtResource("1_pks84") + +[node name="ParallaxBackground" parent="." instance=ExtResource("1_f7g4d")] + +[node name="UILayer" type="CanvasLayer" parent="."] +layer = 5 + +[node name="HUD2D" parent="UILayer" instance=ExtResource("4_t7gb2")] +scale = Vector2(0.24, 0.24) +mouse_filter = 0 + +[node name="DialogLayer" parent="UILayer" instance=ExtResource("3_prpss")] +offset_top = -134.0 +mouse_filter = 2 + +[node name="Notification" parent="UILayer" instance=ExtResource("5_3gg5t")] +mouse_filter = 2 + +[node name="VignetteShading" parent="." instance=ExtResource("2_d1re1")] + +[node name="PropInspector2D" parent="." instance=ExtResource("5_ux0rw")] + +[node name="Player2D" parent="." instance=ExtResource("6_6geb0")] +unique_name_in_owner = true + +[node name="CameraFocusMarker" parent="Player2D" instance=ExtResource("7_n7qcv")] +unique_name_in_owner = true + +[node name="MainCamera" parent="Player2D/CameraFocusMarker" instance=ExtResource("8_nj084")] +unique_name_in_owner = true +position = Vector2(1, 0) + +[node name="ParallaxForeground" parent="." instance=ExtResource("11_anxqu")] diff --git a/scene/notification/notification.gd b/scene/notification/notification.gd new file mode 100644 index 00000000..0e2889c3 --- /dev/null +++ b/scene/notification/notification.gd @@ -0,0 +1,6 @@ +extends Control + + +func show_notification(msg, number): + #TODO: Implement this method + print("show_notification:", msg, number) diff --git a/scene/notification/notification.tscn b/scene/notification/notification.tscn new file mode 100644 index 00000000..fa5e03b7 --- /dev/null +++ b/scene/notification/notification.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://5g07x6q7wwr1"] + +[ext_resource type="Script" path="res://scene/notification/notification.gd" id="1_j0g80"] + +[node name="Notification" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_j0g80") diff --git a/scene/player/player_2d.gd b/scene/player/player_2d.gd new file mode 100644 index 00000000..6b58e758 --- /dev/null +++ b/scene/player/player_2d.gd @@ -0,0 +1,166 @@ +extends CharacterBody2D + +class_name MainPlayer + +enum MOVEMENT_STATUS { + IDLE, + WALKING, + RUNNING, + LAYING_STAY, + LAYING_MOVING, + CLIMBING_STAY, + CLIMBING, +} + +@export var action_locked := false: + set(val): + action_locked = val + _process_action_lock() +@export var current_status : MOVEMENT_STATUS +@export var facing_direction := Vector2(1.0, -1.0) +@export var is_laying := false: + set(val): + is_laying = val + # reset the facing direction wether the player is laying or not. + _reset_face_direction() + if is_laying: + is_climbing = false +@export var is_climbing := false: + set(val): + is_climbing = val + # reset the facing direction wether the player is climbing or not. + _reset_face_direction() + if is_climbing: + is_laying = false +@export var running_locked := false +@export var speed_walking := 300.0 +@export var speed_runnig := 500.0 +@export var speed_laying := 150.0 +@export var speed_climbing := 170.0 +#const JUMP_VELOCITY = -400.0 + +@onready var camera = %MainCamera as Camera2D +@onready var sprite = %AnimatedSprite2D as AnimatedSprite2D + +func _ready() -> void: + _reset_face_direction() + +func _reset_face_direction() -> void: + facing_direction = Vector2(1, -1) + +func _process_action_lock() -> void: + # reset status to idle or stay + if action_locked: + if current_status == MOVEMENT_STATUS.WALKING or current_status == MOVEMENT_STATUS.RUNNING: + current_status = MOVEMENT_STATUS.IDLE + elif current_status == MOVEMENT_STATUS.LAYING_MOVING: + current_status = MOVEMENT_STATUS.LAYING_STAY + elif current_status == MOVEMENT_STATUS.CLIMBING: + current_status = MOVEMENT_STATUS.CLIMBING_STAY + _play_animation() + +# return whether the player status or facing direction has changed. +func _check_status(direction) -> bool: + var tmp_status = current_status + var new_facing_direction := facing_direction + if is_laying: + if direction.x: + new_facing_direction.x = direction.x + tmp_status = MOVEMENT_STATUS.LAYING_MOVING + else: + tmp_status = MOVEMENT_STATUS.LAYING_STAY + elif is_climbing: + if direction.y: + new_facing_direction.y = direction.y + tmp_status = MOVEMENT_STATUS.CLIMBING + else: + tmp_status = MOVEMENT_STATUS.CLIMBING_STAY + else: + if direction.x: + new_facing_direction.x = direction.x + tmp_status = MOVEMENT_STATUS.WALKING + if !running_locked and Input.is_action_pressed("run"): + tmp_status = MOVEMENT_STATUS.RUNNING + else: + tmp_status = MOVEMENT_STATUS.IDLE + if new_facing_direction != facing_direction or tmp_status != current_status: + facing_direction = new_facing_direction + current_status = tmp_status + return true + return false + +func _play_animation() -> void: + match current_status: + MOVEMENT_STATUS.IDLE: + if facing_direction.x > 0.0: + sprite.play(&"idle_r") + else: + sprite.play(&"idle_l") + MOVEMENT_STATUS.WALKING: + if facing_direction.x > 0.0: + sprite.play(&"walking_r") + else: + sprite.play(&"walking_l") + MOVEMENT_STATUS.RUNNING: + if facing_direction.x > 0.0: + sprite.play(&"running_r") + else: + sprite.play(&"running_l") + MOVEMENT_STATUS.LAYING_STAY: + if facing_direction.x > 0.0: + sprite.play(&"laying_stay_r") + else: + sprite.play(&"laying_stay_l") + MOVEMENT_STATUS.LAYING_MOVING: + if facing_direction.x > 0.0: + sprite.play(&"laying_moving_r") + else: + sprite.play(&"laying_moving_l") + MOVEMENT_STATUS.CLIMBING_STAY: + sprite.play(&"climbing_stay") + MOVEMENT_STATUS.CLIMBING: + if facing_direction.y > 0.0: + sprite.play(&"climbing_down") + else: + sprite.play(&"climbing_up") + +func _get_speed(direction: Vector2) -> Vector2: + match current_status: + MOVEMENT_STATUS.WALKING: + return Vector2(speed_walking * direction.x, 0.0) + MOVEMENT_STATUS.RUNNING: + return Vector2(speed_runnig * direction.x, 0.0) + MOVEMENT_STATUS.LAYING_MOVING: + return Vector2(speed_laying * direction.x, 0.0) + MOVEMENT_STATUS.CLIMBING: + return Vector2(0, speed_climbing * direction.y) + return Vector2(0, 0) + +func _physics_process(_delta: float) -> void: + if action_locked: + velocity = Vector2.ZERO + return + + # Add the gravity. + #if not is_on_floor(): + #velocity += get_gravity() * delta + #if Input.is_action_just_pressed("jump") and is_on_floor(): + #velocity.y = JUMP_VELOCITY + var x_direction := Input.get_axis("left", "right") + var y_direction := Input.get_axis("up", "down") + var direction := Vector2(x_direction, y_direction) + if _check_status(direction): + _play_animation() + var speed := _get_speed(direction) as Vector2 + velocity.x = move_toward(velocity.x, speed.x, 300.0) + velocity.y = move_toward(velocity.y, speed.y, 300.0) + move_and_slide() + _tweak_camera_marker() + +# drag the camera marker against the player movement +# so there will be a better vision in front of the player. +func _tweak_camera_marker(): + var marker = get_node("./CameraFocusMarker") as Node2D + if marker: + marker.position.x = facing_direction.x * abs(velocity.x) * 0.11 + marker.position.y = facing_direction.y * abs(velocity.y) * 0.11 diff --git a/scene/player/player_2d.tscn b/scene/player/player_2d.tscn new file mode 100644 index 00000000..5f132c21 --- /dev/null +++ b/scene/player/player_2d.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=5 format=3 uid="uid://cjhw5ecygrqty"] + +[ext_resource type="Script" path="res://scene/player/player_2d.gd" id="1_3a78y"] +[ext_resource type="SpriteFrames" uid="uid://c43gyexokirl4" path="res://config/animation/player_sprite_frames.tres" id="1_23i43"] +[ext_resource type="Texture2D" uid="uid://t526pexw4ng4" path="res://asset/art/neutral_point_light.webp" id="3_scilj"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_fno82"] +size = Vector2(105, 238.5) + +[node name="Player2D" type="CharacterBody2D"] +script = ExtResource("1_3a78y") +current_status = null +facing_direction = null +running_locked = null +speed_walking = null +speed_runnig = null +speed_laying = null +speed_climbing = null + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +unique_name_in_owner = true +sprite_frames = ExtResource("1_23i43") +animation = &"idle_r" + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(-1, -1) +shape = SubResource("RectangleShape2D_fno82") + +[node name="PointLight2D" type="PointLight2D" parent="."] +position = Vector2(2, -175) +scale = Vector2(0.810547, 0.849609) +energy = 0.4 +texture = ExtResource("3_scilj") +height = 50.0 diff --git a/scene/popup/prop_inspector_2d.gd b/scene/popup/prop_inspector_2d.gd new file mode 100644 index 00000000..4b576130 --- /dev/null +++ b/scene/popup/prop_inspector_2d.gd @@ -0,0 +1,12 @@ +extends CanvasLayer + +@onready var panel := %Panel + +@export var shown := false: + set(val): + shown = val + panel.visible = val + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + layer = GlobalConfig.CANVAS_LAYER_PROP_INSPECTOR diff --git a/scene/popup/prop_inspector_2d.tscn b/scene/popup/prop_inspector_2d.tscn new file mode 100644 index 00000000..55ec3321 --- /dev/null +++ b/scene/popup/prop_inspector_2d.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://cekhj65axie0p"] + +[ext_resource type="Script" path="res://scene/popup/prop_inspector_2d.gd" id="1_p4yxb"] + +[node name="PropInspector2D" type="CanvasLayer"] +script = ExtResource("1_p4yxb") + +[node name="Panel" type="Panel" parent="."] +unique_name_in_owner = true +visible = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/scene/settings/settings.tscn b/scene/settings/settings.tscn new file mode 100644 index 00000000..fe935303 --- /dev/null +++ b/scene/settings/settings.tscn @@ -0,0 +1,9 @@ +[gd_scene format=3 uid="uid://beok2r6fgburn"] + +[node name="Settings" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/ui/button/sound_button.gd b/ui/button/sound_button.gd new file mode 100644 index 00000000..49d9bb8c --- /dev/null +++ b/ui/button/sound_button.gd @@ -0,0 +1,19 @@ +extends Button + +class_name SoundButton + +@export var audio_collections: Array[AudioStreamCollection] + +func _ready(): + if !audio_collections: + audio_collections.append(preload("res://config/audio/ui/ui_click.tres")) + #print("sound button loaded default ui_click.tres") + + if audio_collections: + #print("sound button load audio_collections into audio_player") + var audio_player := RandomAudioStreamPlayer.new() + audio_player.audio_collections = audio_collections + pressed.connect(audio_player.play_random) + add_child(audio_player) + else: + printerr("sound button has no audio_collections! ignore initialization of audio_player") diff --git a/ui/button/sound_button.tscn b/ui/button/sound_button.tscn new file mode 100644 index 00000000..cd05e898 --- /dev/null +++ b/ui/button/sound_button.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://bdy4u3e7rmo7f"] + +[ext_resource type="Script" path="res://ui/button/sound_button.gd" id="1_vn1us"] + +[node name="SoundButton" type="Button"] +offset_right = 8.0 +offset_bottom = 8.0 +script = ExtResource("1_vn1us") diff --git a/ui/button/sound_texture_button.gd b/ui/button/sound_texture_button.gd new file mode 100644 index 00000000..b5d7ecb2 --- /dev/null +++ b/ui/button/sound_texture_button.gd @@ -0,0 +1,28 @@ +extends TextureButton + +class_name SoundTextureButton + +@export var audio_collections: Array[AudioStreamCollection] + +func _ready(): + if !audio_collections: + audio_collections.append(preload("res://config/audio/ui/ui_click.tres")) + #print("sound button loaded default ui_click.tres") + + if audio_collections: + #print("sound button load audio_collections into audio_player") + var audio_player := RandomAudioStreamPlayer.new() + audio_player.audio_collections = audio_collections + button_down.connect(audio_player.play_random) + add_child(audio_player) + else: + printerr("sound button has no audio_collections! ignore initialization of audio_player") + #button_down.connect(_on_press_down) + #button_up.connect(_on_press_up) + + +#func _on_press_down(): + #InputUtil.set_cursor_hand_patting() +# +#func _on_press_up(): + #InputUtil.set_cursor_hand_rest() diff --git a/ui/button/sound_texture_button.tscn b/ui/button/sound_texture_button.tscn new file mode 100644 index 00000000..db3bf661 --- /dev/null +++ b/ui/button/sound_texture_button.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://cef5mgr1hgpmr"] + +[ext_resource type="Script" path="res://ui/button/sound_texture_button.gd" id="1_k7j4d"] + +[node name="SoundButton" type="TextureButton"] +size_flags_horizontal = 0 +size_flags_vertical = 0 +script = ExtResource("1_k7j4d") diff --git a/ui/hud/Inventory.tscn b/ui/hud/Inventory.tscn new file mode 100755 index 00000000..e95a5432 --- /dev/null +++ b/ui/hud/Inventory.tscn @@ -0,0 +1,100 @@ +[gd_scene load_steps=14 format=3 uid="uid://cmyxinb0ickva"] + +[ext_resource type="Script" path="res://UI/Inventory.gd" id="1_gbar7"] +[ext_resource type="Texture2D" uid="uid://2jc6qio7tmuk" path="res://assets/HUD/背包按键/道具快捷栏/zz.png" id="2_c4gqh"] +[ext_resource type="Texture2D" uid="uid://dr1fvkrmtynb6" path="res://assets/HUD/背包按键/道具快捷栏/az.png" id="3_ftsid"] +[ext_resource type="Texture2D" uid="uid://b3pfdsyj0dcdb" path="res://assets/HUD/背包按键/道具快捷栏/wz.png" id="4_rn4wj"] +[ext_resource type="Texture2D" uid="uid://ykv7o43dtjn0" path="res://assets/HUD/背包按键/道具快捷栏/k.png" id="5_o2bgy"] +[ext_resource type="Texture2D" uid="uid://bgjt0y7vs5ix0" path="res://assets/HUD/背包按键/道具快捷栏/s.png" id="6_abx1s"] +[ext_resource type="Texture2D" uid="uid://ukdbwrvd8qdq" path="res://assets/HUD/背包按键/道具快捷栏/zy.png" id="7_n66je"] +[ext_resource type="Texture2D" uid="uid://sv8u083cpov6" path="res://assets/HUD/背包按键/道具快捷栏/ay.png" id="8_iym7w"] +[ext_resource type="Texture2D" uid="uid://dva4vpj5uo8td" path="res://assets/HUD/背包按键/道具快捷栏/wy.png" id="9_aoirx"] +[ext_resource type="FontFile" uid="uid://dehtmf0lanaf3" path="res://assets/HUD/字体/方正楷体简体.TTF" id="10_8p7i8"] + +[sub_resource type="FontFile" id="FontFile_5oap5"] +subpixel_positioning = 0 +msdf_pixel_range = 14 +msdf_size = 128 +cache/0/16/0/ascent = 0.0 +cache/0/16/0/descent = 0.0 +cache/0/16/0/underline_position = 0.0 +cache/0/16/0/underline_thickness = 0.0 +cache/0/16/0/scale = 1.0 +cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0) + +[sub_resource type="Theme" id="Theme_bioai"] +default_base_scale = 0.98 +default_font = SubResource("FontFile_5oap5") + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_jk1w7"] +size = Vector2(83.6779, 34.3683) + +[node name="Inventory" type="VBoxContainer"] +offset_left = 10.0 +offset_top = 7.0 +offset_right = 277.0 +offset_bottom = 184.0 +scale = Vector2(0.18, 0.18) +theme_override_constants/separation = 5 +script = ExtResource("1_gbar7") + +[node name="ItemBar" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Prev" type="TextureButton" parent="ItemBar"] +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +theme = SubResource("Theme_bioai") +texture_normal = ExtResource("2_c4gqh") +texture_pressed = ExtResource("3_ftsid") +texture_disabled = ExtResource("4_rn4wj") +stretch_mode = 3 + +[node name="Use" type="TextureButton" parent="ItemBar"] +layout_mode = 2 +texture_normal = ExtResource("5_o2bgy") + +[node name="Prop" type="Sprite2D" parent="ItemBar/Use"] +z_index = 1 +position = Vector2(72, 72) + +[node name="Hand" type="Sprite2D" parent="ItemBar/Use"] +z_index = 1 +position = Vector2(110, 102) +texture = ExtResource("6_abx1s") + +[node name="Next" type="TextureButton" parent="ItemBar"] +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +texture_normal = ExtResource("7_n66je") +texture_pressed = ExtResource("8_iym7w") +texture_disabled = ExtResource("9_aoirx") +stretch_mode = 3 + +[node name="Area2D" type="Area2D" parent="ItemBar"] +visible = false +position = Vector2(-200, -99.9999) +scale = Vector2(3.60011, 5.23739) + +[node name="CollisionShape2D" type="CollisionShape2D" parent="ItemBar/Area2D"] +position = Vector2(91.4901, 32.4589) +scale = Vector2(1, 0.999999) +shape = SubResource("RectangleShape2D_jk1w7") +one_way_collision_margin = 0.0 + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +theme_override_fonts/font = ExtResource("10_8p7i8") +theme_override_font_sizes/font_size = 35 +text = "道具的描述" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Timer" type="Timer" parent="Label"] +wait_time = 1.5 +one_shot = true + +[connection signal="pressed" from="ItemBar/Prev" to="." method="_on_prev_pressed"] +[connection signal="pressed" from="ItemBar/Use" to="." method="_on_use_pressed"] +[connection signal="pressed" from="ItemBar/Next" to="." method="_on_next_pressed"] +[connection signal="timeout" from="Label/Timer" to="." method="_on_timer_timeout"] diff --git a/ui/hud/hud.tscn b/ui/hud/hud.tscn new file mode 100644 index 00000000..b1d8fe49 --- /dev/null +++ b/ui/hud/hud.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://bs63fypttsiop"] + +[ext_resource type="PackedScene" uid="uid://cmyxinb0ickva" path="res://ui/hud/Inventory.tscn" id="1_tam4l"] + +[node name="HUD" type="CanvasLayer"] +layer = 99 + +[node name="Inventory" parent="." instance=ExtResource("1_tam4l")] +offset_top = 4.0 +offset_bottom = 193.0 +script = null diff --git a/ui/hud/inventory.gd b/ui/hud/inventory.gd new file mode 100755 index 00000000..dc870731 --- /dev/null +++ b/ui/hud/inventory.gd @@ -0,0 +1,112 @@ +#背包实现 +extends VBoxContainer + +var _hand_outro: Tween +var _label_outro: Tween +#@onready var item_bar = $ItemBar +@onready var prev = $ItemBar/Prev +@onready var prop = $ItemBar/Use/Prop +@onready var hand = $ItemBar/Use/Hand +@onready var next = $ItemBar/Next +@onready var label = $Label +@onready var timer = $Label/Timer + + +func _ready() -> void: + #SceneManager.inventory.add_item(preload("res://items/1014_yaoshi.tres")) + #SceneManager.inventory.add_item(preload("res://items/3014_yaoshi.tres")) + hand.hide() + hand.modulate.a = 0.0 + label.hide() + label.modulate.a = 0.0 + + SceneManager.inventory.changed.connect(_update_ui) + _update_ui(true) + + +#设置在屏幕中点击任意位置,互动手图案消失 +func _input(event: InputEvent) -> void: + if event.is_action_pressed("click") and SceneManager.inventory.active_item: + SceneManager.inventory.set_deferred("active_item", true) + + _hand_outro = create_tween() + _hand_outro.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE).set_parallel() + _hand_outro.tween_property(hand, "scale", Vector2.ONE * 0.5, 0.15) #Vector2.ONE * 0.5 设置手缩放的大小.0.15表示消失的速度 + _hand_outro.tween_property(hand, "modulate:a", 0.0, 0.15) + _hand_outro.chain().tween_callback(hand.hide) + + + +func _update_ui(is_init = false): #is_inte = false 让背包物品在最开始出现时没有动画效果 + var count = SceneManager.inventory.get_item_count() + prev.disabled = count < 2 + next.disabled = count < 2 + visible = count > 0 + + var item = SceneManager.inventory.get_current_item() + if not item: + return + else: + label.text = item.description + prop.texture = item.prop_texture + #item_bar.modulate.a = 1 + #print("prop") + +#添加背包物品在左右滑动时的弹出动画效果 + + if is_init: + return + + var tween := create_tween() + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + tween.tween_property(prop, "scale", Vector2.ONE, 0.15).from(Vector2.ZERO) + + _show_label() + +func _show_label() -> void: + if _label_outro and _label_outro.is_valid(): + _label_outro.kill() + _label_outro = null + label.show() + var tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE) + tween.tween_property(label,"modulate:a",1.0,0.2) + tween.tween_callback(timer.start) + + + +func _on_prev_pressed() -> void: + SoundManager.play_sfx("interact") + SceneManager.inventory.select_prev() + print("prev1") + +func _on_next_pressed() -> void: + SoundManager.play_sfx("interact") + SceneManager.inventory.select_next() + print("next2") + +@onready var use = $ItemBar/Use + + +func _on_use_pressed() -> void: + if use.button_mask == MOUSE_BUTTON_MASK_LEFT: + SceneManager.inventory.active_item = SceneManager.inventory.get_current_item() + print("use3") + + if _hand_outro and _hand_outro.is_valid(): + _hand_outro.kill() + _hand_outro = null + hand.show() + var tween = create_tween() + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK).set_parallel() + tween.tween_property(hand, "scale", Vector2.ONE, 0.15).from(Vector2.ZERO) + tween.tween_property(hand, "modulate:a", 1.0, 0.15) + + _show_label() + + +func _on_timer_timeout(): + _label_outro = create_tween() + _label_outro.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE) + _label_outro.tween_property(label,"modulate:a",0.0,0.2) + _label_outro.chain().tween_callback(label.hide) diff --git a/ui/settings.tscn b/ui/settings.tscn new file mode 100644 index 00000000..07edd1a4 --- /dev/null +++ b/ui/settings.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=3 format=3 uid="uid://c5wtakm14h7cy"] + +[ext_resource type="Texture2D" uid="uid://db8rd4nwl6pve" path="res://asset/art/ui/ui_settings.skt/Layer~2.png" id="1_sl0gr"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_jngmd"] +atlas = ExtResource("1_sl0gr") +region = Rect2(254, 362, 994, 1314) + +[node name="Settings" type="Sprite2D"] +texture = SubResource("AtlasTexture_jngmd") diff --git a/ui/vignette/vignette_shading.gd b/ui/vignette/vignette_shading.gd new file mode 100644 index 00000000..d07e69ce --- /dev/null +++ b/ui/vignette/vignette_shading.gd @@ -0,0 +1,14 @@ +extends CanvasLayer + +@export var rgb := Color8(0x3f,0x26,0x31): + set(new_val): + rgb = new_val + %ColorRect.material.set("shader_parameter/vignette_rgb", new_val) + +@export_range(0, 5) var intensity := 0.3: + set(new_val): + intensity = new_val + %ColorRect.material.set("shader_parameter/vignette_intensity", new_val) + +func _ready() -> void: + layer = GlobalConfig.CANVAS_LAYER_VIGNETTE diff --git a/ui/vignette/vignette_shading.tscn b/ui/vignette/vignette_shading.tscn new file mode 100644 index 00000000..bae512ce --- /dev/null +++ b/ui/vignette/vignette_shading.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=4 format=3 uid="uid://3gk1gxwanw24"] + +[ext_resource type="Script" path="res://ui/vignette/vignette_shading.gd" id="1_6w7er"] +[ext_resource type="Shader" path="res://asset/shader/vignette.gdshader" id="1_akp6k"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_pabt5"] +shader = ExtResource("1_akp6k") +shader_parameter/vignette_intensity = 0.4 +shader_parameter/vignette_rgb = Color(0.247059, 0.14902, 0.192157, 1) + +[node name="VignetteShading" type="CanvasLayer"] +layer = 100 +script = ExtResource("1_6w7er") + +[node name="ColorRect" type="ColorRect" parent="."] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_pabt5") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/util/resource_utils.gd b/util/resource_utils.gd new file mode 100644 index 00000000..3156d171 --- /dev/null +++ b/util/resource_utils.gd @@ -0,0 +1,17 @@ +extends Object +class_name ResourceUtils + +# # remove editor's cache +# func remove_editor_cache(resource: Resource) -> void: +# if Engine.is_editor_hint(): +# (resource) + + +static func remove_editor_cache(name: String) -> void: + pass + # var dir = DirAccess.open("res://.godot/editor") + # for file in dir.get_files(): + # if file.get_basename().begins_with(name): + # DirAccess.remove_absolute("res://.godot/editor/" + file) + # print("Removed editor cache: ", file) + # break