├── demo ├── eidolon │ ├── .gitignore │ ├── .dockerignore │ ├── Dockerfile │ └── resources │ │ └── npc_agent.yaml └── godot │ ├── .gitignore │ ├── art │ ├── grass.png │ ├── knight.png │ ├── priest.png │ ├── exclamation.png │ ├── wizard_white.png │ ├── grass.png.import │ ├── priest.png.import │ ├── knight.png.import │ ├── exclamation.png.import │ └── wizard_white.png.import │ ├── .gitattributes │ ├── addons │ └── EidolonClient │ │ ├── icon.png │ │ ├── addons │ │ └── HTTPSSEClient │ │ │ ├── .gitattributes │ │ │ ├── icon.png │ │ │ ├── .gitignore │ │ │ ├── HTTPSSEClient.tscn │ │ │ ├── httpsseclient_plugin.gd │ │ │ ├── plugin.cfg │ │ │ ├── Demo.tscn │ │ │ ├── Demo.gd │ │ │ ├── icon.png.import │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── HTTPSSEClient.gd │ │ ├── plugin.cfg │ │ ├── eidolon_client_plugin.gd │ │ ├── eidolon_handler.tscn │ │ ├── icon.png.import │ │ └── EidolonHandler.gd │ ├── world.gd │ ├── UI │ ├── message_container.tscn │ ├── dialogue_box.tscn │ └── dialogue_box.gd │ ├── main.gd │ ├── Player │ ├── player.tscn │ ├── Player.gd │ ├── player_skin_selector.gd │ └── player_skin_selector.tscn │ ├── NPC │ ├── npc.gd │ ├── npc.tscn │ ├── basic_mob_sprite.tscn │ └── basic_mob_sprite.gd │ ├── icon.svg │ ├── icon.svg.import │ ├── main.tscn │ ├── project.godot │ └── world.tscn ├── EidolonClient ├── icon.png ├── addons │ └── HTTPSSEClient │ │ ├── .gitattributes │ │ ├── icon.png │ │ ├── .gitignore │ │ ├── HTTPSSEClient.tscn │ │ ├── httpsseclient_plugin.gd │ │ ├── plugin.cfg │ │ ├── Demo.tscn │ │ ├── Demo.gd │ │ ├── icon.png.import │ │ ├── LICENSE │ │ ├── README.md │ │ └── HTTPSSEClient.gd ├── plugin.cfg ├── eidolon_client_plugin.gd ├── eidolon_handler.tscn ├── icon.png.import └── EidolonHandler.gd └── README.md /demo/eidolon/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /demo/eidolon/.dockerignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /demo/godot/.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /EidolonClient/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/EidolonClient/icon.png -------------------------------------------------------------------------------- /demo/godot/art/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/art/grass.png -------------------------------------------------------------------------------- /demo/godot/art/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/art/knight.png -------------------------------------------------------------------------------- /demo/godot/art/priest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/art/priest.png -------------------------------------------------------------------------------- /demo/godot/.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /demo/godot/art/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/art/exclamation.png -------------------------------------------------------------------------------- /demo/godot/art/wizard_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/art/wizard_white.png -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /demo/eidolon/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eidolonai/sdk_base:latest 2 | 3 | COPY . . 4 | CMD ["eidolon-server", "-m", "local_dev", "resources"] -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/addons/EidolonClient/icon.png -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/EidolonClient/addons/HTTPSSEClient/icon.png -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wizzerrd/GodotAgent/HEAD/demo/godot/addons/EidolonClient/addons/HTTPSSEClient/icon.png -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | -------------------------------------------------------------------------------- /demo/godot/world.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var player 4 | 5 | signal start_conversation(character_name) 6 | signal end_conversation 7 | 8 | func _on_start_conversation(character_name): 9 | player.talking = true 10 | 11 | func _on_end_conversation(): 12 | player.talking = false 13 | -------------------------------------------------------------------------------- /demo/godot/UI/message_container.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=3 uid="uid://diaisyb0c0sqg"] 2 | 3 | [node name="MessageContainer" type="HBoxContainer"] 4 | anchors_preset = 15 5 | anchor_right = 1.0 6 | anchor_bottom = 1.0 7 | offset_bottom = -632.0 8 | grow_horizontal = 2 9 | grow_vertical = 2 10 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bhv2sbnvvkx2v"] 2 | 3 | [ext_resource type="Script" path="res://addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.gd" id="1"] 4 | 5 | [node name="HTTPSSEClient" type="Node"] 6 | script = ExtResource("1") 7 | -------------------------------------------------------------------------------- /EidolonClient/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="EidolonClient" 4 | description="Eidolon Client Handler for Godot Engine. Allows for integrating LLM into Godot. An HTTPClient-based implementation that supports server-sent events." 5 | author="Luis Laffitte" 6 | version="0.1" 7 | script="eidolon_client_plugin.gd" 8 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/httpsseclient_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | add_custom_type("HTTPSSEClient", "Node", preload("HTTPSSEClient.gd"), preload("icon.png")) 6 | pass 7 | 8 | func _exit_tree(): 9 | remove_custom_type("HTTPSSEClient") 10 | pass 11 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="HTTPSSEClient" 4 | description="An HTTPClient-based implementation that supports server-sent events, effectively enabling push notifications in Godot, using GDScript." 5 | author="Kyle Szklenski" 6 | version="1.0" 7 | script="httpsseclient_plugin.gd" 8 | -------------------------------------------------------------------------------- /EidolonClient/eidolon_client_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | add_custom_type("EidolonHandler", "Node", preload("EidolonHandler.gd"), preload("res://addons/EidolonClient/icon.png")) 6 | pass 7 | 8 | func _exit_tree(): 9 | remove_custom_type("EidolonHandler") 10 | pass 11 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bhv2sbnvvkx2v"] 2 | 3 | [ext_resource type="Script" path="res://addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.gd" id="1"] 4 | 5 | [node name="HTTPSSEClient" type="Node"] 6 | script = ExtResource("1") 7 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/httpsseclient_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | add_custom_type("HTTPSSEClient", "Node", preload("HTTPSSEClient.gd"), preload("icon.png")) 6 | pass 7 | 8 | func _exit_tree(): 9 | remove_custom_type("HTTPSSEClient") 10 | pass 11 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="EidolonClient" 4 | description="Eidolon Client Handler for Godot Engine. Allows for integrating LLM into Godot. An HTTPClient-based implementation that supports server-sent events." 5 | author="Luis Laffitte" 6 | version="0.1" 7 | script="eidolon_client_plugin.gd" 8 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="HTTPSSEClient" 4 | description="An HTTPClient-based implementation that supports server-sent events, effectively enabling push notifications in Godot, using GDScript." 5 | author="Kyle Szklenski" 6 | version="1.0" 7 | script="httpsseclient_plugin.gd" 8 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/eidolon_client_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | add_custom_type("EidolonHandler", "Node", preload("EidolonHandler.gd"), preload("res://addons/EidolonClient/icon.png")) 6 | pass 7 | 8 | func _exit_tree(): 9 | remove_custom_type("EidolonHandler") 10 | pass 11 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/Demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cm2qayhr1543e"] 2 | 3 | [ext_resource type="Script" path="res://addons/HTTPSSEClient/Demo.gd" id="1"] 4 | [ext_resource type="Script" path="res://addons/HTTPSSEClient/HTTPSSEClient.gd" id="2"] 5 | 6 | [node name="Demo" type="Node2D"] 7 | script = ExtResource("1") 8 | 9 | [node name="HTTPSSEClient" type="Node" parent="."] 10 | script = ExtResource("2") 11 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/Demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cm2qayhr1543e"] 2 | 3 | [ext_resource type="Script" path="res://addons/HTTPSSEClient/Demo.gd" id="1"] 4 | [ext_resource type="Script" path="res://addons/HTTPSSEClient/HTTPSSEClient.gd" id="2"] 5 | 6 | [node name="Demo" type="Node2D"] 7 | script = ExtResource("1") 8 | 9 | [node name="HTTPSSEClient" type="Node" parent="."] 10 | script = ExtResource("2") 11 | -------------------------------------------------------------------------------- /EidolonClient/eidolon_handler.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bcfug5ny0wrib"] 2 | 3 | [ext_resource type="Script" path="res://addons/EidolonClient/EidolonHandler.gd" id="1_muyqa"] 4 | [ext_resource type="PackedScene" uid="uid://bhv2sbnvvkx2v" path="res://addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.tscn" id="2_tg6jx"] 5 | 6 | [node name="EidolonHandler" type="Node"] 7 | script = ExtResource("1_muyqa") 8 | 9 | [node name="HTTPSSEClient" parent="." instance=ExtResource("2_tg6jx")] 10 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/eidolon_handler.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bcfug5ny0wrib"] 2 | 3 | [ext_resource type="Script" path="res://addons/EidolonClient/EidolonHandler.gd" id="1_muyqa"] 4 | [ext_resource type="PackedScene" uid="uid://bhv2sbnvvkx2v" path="res://addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.tscn" id="2_tg6jx"] 5 | 6 | [node name="EidolonHandler" type="Node"] 7 | script = ExtResource("1_muyqa") 8 | 9 | [node name="HTTPSSEClient" parent="." instance=ExtResource("2_tg6jx")] 10 | -------------------------------------------------------------------------------- /demo/eidolon/resources/npc_agent.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: eidolon/v1 2 | kind: Agent 3 | metadata: 4 | name: npc_agent 5 | spec: 6 | description: "Agent for driving NPC dialogue." 7 | system_prompt: " 8 | This is an Agent for driving NPC dialogue. Below is the role, in first person, for the agent to follow. The Agent is the Knight. 9 | 10 | You are a gallant knight wandering the marsh. 11 | To the north there is a necromancer's tower. 12 | To the east there is a cave with a troll. 13 | To the west there is a river with a hydra. 14 | To the south there is a safe village. 15 | A wizard approaches you from the south and begins to make conversation. 16 | " 17 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/Demo.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var config = { 4 | # Add full config data here 5 | "url":"" # For demo purposes, I use my Firebase database URL 6 | } 7 | 8 | func _ready(): 9 | var sub_url = "" # Add the "/sub_list_url" stuff here, including query parameters as needed; for demo purposes, I use the list path in my Firebase database, combined with ".json?auth=" and whatever the auth token is. 10 | $HTTPSSEClient.connected.connect(on_connected) 11 | $HTTPSSEClient.connect_to_host(config.url, sub_url, 443, true, false) 12 | 13 | func on_connected(): 14 | $HTTPSSEClient.new_sse_event.connect(on_new_sse_event) 15 | 16 | func on_new_sse_event(headers, event, data): 17 | print("event is: " + event) 18 | print("data is: " + data) 19 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/Demo.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var config = { 4 | # Add full config data here 5 | "url":"" # For demo purposes, I use my Firebase database URL 6 | } 7 | 8 | func _ready(): 9 | var sub_url = "" # Add the "/sub_list_url" stuff here, including query parameters as needed; for demo purposes, I use the list path in my Firebase database, combined with ".json?auth=" and whatever the auth token is. 10 | $HTTPSSEClient.connected.connect(on_connected) 11 | $HTTPSSEClient.connect_to_host(config.url, sub_url, 443, true, false) 12 | 13 | func on_connected(): 14 | $HTTPSSEClient.new_sse_event.connect(on_new_sse_event) 15 | 16 | func on_new_sse_event(headers, event, data): 17 | print("event is: " + event) 18 | print("data is: " + data) 19 | -------------------------------------------------------------------------------- /demo/godot/main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | func _ready(): 4 | $DialogueBox.hide() 5 | 6 | func _on_world_start_conversation(character_name): 7 | $DialogueBox.show() 8 | 9 | func _on_world_end_conversation(): 10 | $DialogueBox.hide() 11 | 12 | func _on_dialogue_box_send_message(message): 13 | $EidolonHandler.post_message(message) 14 | 15 | func _on_eidolon_handler_get_process_id(process_id): 16 | var message = "Process ID: %s" % process_id 17 | $DialogueBox.add_message("SYSTEM", message) 18 | 19 | func _on_eidolon_handler_new_message(): 20 | $DialogueBox.add_message("AGENT") 21 | 22 | func _on_eidolon_handler_get_message(message): 23 | $DialogueBox.update_last_message(message) 24 | 25 | func _on_eidolon_handler_finish_message(): 26 | $DialogueBox.waiting = false 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/godot/Player/player.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://qxal2f8l7iuk"] 2 | 3 | [ext_resource type="Script" path="res://Player/Player.gd" id="1_67ti2"] 4 | [ext_resource type="PackedScene" uid="uid://dhb820qr54ed4" path="res://Player/player_skin_selector.tscn" id="2_np8xg"] 5 | 6 | [sub_resource type="CircleShape2D" id="CircleShape2D_2m1x7"] 7 | radius = 6.0 8 | 9 | [node name="Player" type="CharacterBody2D"] 10 | script = ExtResource("1_67ti2") 11 | 12 | [node name="Camera2D" type="Camera2D" parent="."] 13 | 14 | [node name="PlayerSkinSelector" parent="." instance=ExtResource("2_np8xg")] 15 | 16 | [node name="Rotation" type="Node2D" parent="."] 17 | position = Vector2(0, 7) 18 | 19 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 20 | position = Vector2(0, 7) 21 | shape = SubResource("CircleShape2D_2m1x7") 22 | -------------------------------------------------------------------------------- /demo/godot/art/grass.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://chdf55pqrl6bb" 6 | path="res://.godot/imported/grass.png-af9205a0fdfd145e55fd9de45a00e35a.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/grass.png" 14 | dest_files=["res://.godot/imported/grass.png-af9205a0fdfd145e55fd9de45a00e35a.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/art/priest.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://yr733vvltdhh" 6 | path="res://.godot/imported/priest.png-f364881787a1066030451ab8eba6cb36.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/priest.png" 14 | dest_files=["res://.godot/imported/priest.png-f364881787a1066030451ab8eba6cb36.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/art/knight.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b5xlcddgovqtb" 6 | path="res://.godot/imported/knight.png-4f1603bf09ab2709fb269482f2d9c42f.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/knight.png" 14 | dest_files=["res://.godot/imported/knight.png-4f1603bf09ab2709fb269482f2d9c42f.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /EidolonClient/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cknyhelvw6d8u" 6 | path="res://.godot/imported/icon.png-e7cea3d9a7227fa7acf3a746366b1923.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/EidolonClient/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-e7cea3d9a7227fa7acf3a746366b1923.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/NPC/npc.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | var world = null 4 | var player = null 5 | var talking = false 6 | var velocity = Vector2.ZERO 7 | 8 | func _ready(): 9 | player = get_parent().player 10 | world = get_parent() 11 | $BasicMobSprite.choose_type("knight") 12 | $Exclamation.hide() 13 | 14 | func _physics_process(delta): 15 | $Rotation.look_at(player.global_position) 16 | $BasicMobSprite.choose_animation(velocity, $Rotation.rotation_degrees) 17 | z_index = position.y - player.global_position.y 18 | if Input.is_action_just_pressed("talk") and $Exclamation.visible and not talking: 19 | world.start_conversation.emit("knight") 20 | talking = true 21 | if Input.is_action_just_pressed("end_conversation") and talking: 22 | world.end_conversation.emit() 23 | talking = false 24 | 25 | func _on_body_entered(body): 26 | $Exclamation.show() 27 | 28 | func _on_body_exited(body): 29 | if body == player: $Exclamation.hide() 30 | -------------------------------------------------------------------------------- /demo/godot/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/godot/art/exclamation.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c5ay77qeey2lg" 6 | path="res://.godot/imported/exclamation.png-1a5390cc85465a160c3f37329fae2739.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/exclamation.png" 14 | dest_files=["res://.godot/imported/exclamation.png-1a5390cc85465a160c3f37329fae2739.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/art/wizard_white.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ceylmk402w7q2" 6 | path="res://.godot/imported/wizard_white.png-99f3d74b4e767526441c23d385282522.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/wizard_white.png" 14 | dest_files=["res://.godot/imported/wizard_white.png-99f3d74b4e767526441c23d385282522.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cknyhelvw6d8u" 6 | path="res://.godot/imported/icon.png-e7cea3d9a7227fa7acf3a746366b1923.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/EidolonClient/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-e7cea3d9a7227fa7acf3a746366b1923.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cexbctj4tvxqp" 6 | path="res://.godot/imported/icon.png-41e628ab302da928bf3e4a6d3768c28b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/EidolonClient/addons/HTTPSSEClient/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-41e628ab302da928bf3e4a6d3768c28b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cexbctj4tvxqp" 6 | path="res://.godot/imported/icon.png-41e628ab302da928bf3e4a6d3768c28b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/EidolonClient/addons/HTTPSSEClient/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-41e628ab302da928bf3e4a6d3768c28b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/godot/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dbj5oh45qpjmm" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kyle Szklenski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kyle Szklenski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /demo/godot/NPC/npc.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://27i550khvkbd"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://ca1sjayv20fc3" path="res://NPC/basic_mob_sprite.tscn" id="1_o7bwc"] 4 | [ext_resource type="Script" path="res://NPC/npc.gd" id="1_yw3ht"] 5 | [ext_resource type="Texture2D" uid="uid://c5ay77qeey2lg" path="res://art/exclamation.png" id="3_eplfl"] 6 | 7 | [sub_resource type="CircleShape2D" id="CircleShape2D_0o7kf"] 8 | radius = 42.0 9 | 10 | [node name="NPC" type="Area2D"] 11 | collision_layer = 0 12 | script = ExtResource("1_yw3ht") 13 | 14 | [node name="Rotation" type="Node2D" parent="."] 15 | 16 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 17 | position = Vector2(0, 6) 18 | shape = SubResource("CircleShape2D_0o7kf") 19 | 20 | [node name="BasicMobSprite" parent="." instance=ExtResource("1_o7bwc")] 21 | 22 | [node name="Exclamation" type="Sprite2D" parent="."] 23 | position = Vector2(0, -25) 24 | texture = ExtResource("3_eplfl") 25 | 26 | [connection signal="area_entered" from="." to="." method="_on_area_entered"] 27 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 28 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 29 | -------------------------------------------------------------------------------- /demo/godot/UI/dialogue_box.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://de01h04q55cgs"] 2 | 3 | [ext_resource type="Script" path="res://UI/dialogue_box.gd" id="1_cl2i0"] 4 | [ext_resource type="Texture2D" uid="uid://b5xlcddgovqtb" path="res://art/knight.png" id="1_wed4i"] 5 | [ext_resource type="PackedScene" path="res://UI/message_container.tscn" id="2_ug7xi"] 6 | 7 | [node name="DialogueBox" type="CanvasLayer"] 8 | script = ExtResource("1_cl2i0") 9 | message_container_scene = ExtResource("2_ug7xi") 10 | 11 | [node name="ScrollContainer" type="ScrollContainer" parent="."] 12 | offset_left = 122.0 13 | offset_top = 438.0 14 | offset_right = 1022.0 15 | offset_bottom = 588.0 16 | 17 | [node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer"] 18 | layout_mode = 2 19 | 20 | [node name="Button" type="Button" parent="."] 21 | offset_left = 939.0 22 | offset_top = 591.0 23 | offset_right = 1009.0 24 | offset_bottom = 628.0 25 | text = "Speak" 26 | 27 | [node name="MessageInput" type="LineEdit" parent="."] 28 | offset_left = 13.0 29 | offset_top = 597.0 30 | offset_right = 930.0 31 | offset_bottom = 628.0 32 | 33 | [node name="Sprite2D" type="Sprite2D" parent="."] 34 | position = Vector2(63, 510) 35 | scale = Vector2(5, 4.763) 36 | texture = ExtResource("1_wed4i") 37 | hframes = 8 38 | vframes = 6 39 | 40 | [connection signal="pressed" from="Button" to="." method="_on_button_pressed"] 41 | -------------------------------------------------------------------------------- /demo/godot/UI/dialogue_box.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var message_container_scene: PackedScene 4 | 5 | var waiting = false 6 | var last_message = null 7 | 8 | signal send_message(message) 9 | 10 | func add_message(sender, text=""): 11 | var container = message_container_scene.instantiate() 12 | var name = Label.new() 13 | var message = Label.new() 14 | name.text = sender + " -" 15 | message.autowrap_mode = 3 16 | message.text = text 17 | message.custom_minimum_size = Vector2(800,0) 18 | container.add_child(name) 19 | container.add_child(VSeparator.new()) 20 | container.add_child(message) 21 | $ScrollContainer/VBoxContainer.add_child(HSeparator.new()) 22 | $ScrollContainer/VBoxContainer.add_child(container) 23 | last_message = message 24 | 25 | func update_last_message(text="", replace=false): 26 | if replace: last_message.text=text 27 | else: last_message.text += text 28 | 29 | func validate_message(message): 30 | if not ( 31 | ensure_message_contents(message) 32 | ): 33 | return false 34 | return true 35 | 36 | func ensure_message_contents(message): 37 | for i in range(len(message)): 38 | if ( 39 | message[i] != ' ' 40 | and message[i] != '\n' 41 | and message[i] != '\t' 42 | ): 43 | return true 44 | return false 45 | 46 | func _on_button_pressed(): 47 | if not waiting: 48 | var message = $MessageInput.text 49 | if validate_message(message): 50 | $MessageInput.clear() 51 | add_message("USER", message) 52 | waiting = true 53 | send_message.emit(message) 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Welcome to the GodotAgent Plugin for Godot 4! This is a plugin providing LLM integration in Godot Engine using Eidolon AI SDK 2 | 3 | ### Features 4 | * Configure an agent with Eidolon and connect it to your game in Godot 5 | * HTTPSSE Streaming for near real-time responses from your agents 6 | 7 | ### Requirements 8 | * Godot 4.x 9 | * Docker 10 | 11 | ### Demo Instructions 12 | 1) Open ```demo/godot/``` as a project in Godot editor 13 | 2) Build Docker image from the file contents in ```demo/eidolon/``` 14 | 15 | ``` docker build -t godot-agent demo/eidolon/ ``` 16 | 17 | 3) Run the Docker image in a container to start the Eidolon server 18 | 19 | ``` docker run -p 8080:8080 -e OPENAI_API_KEY= ``` 20 | 21 | 5) Run the Godot project. You can verify if the client and server are communicating by checking the Eidolon server logs for a POST request. 22 | 6) Game controls are WASD to move, E to talk to the NPC, ESC to exit the conversation. 23 | 7) Have a conversation with a knight! 24 | 25 | The Agent's behavior can be easily customized by modifying the contents of the ``` system_prompt ``` field of ``` demo/eidolon/resources/npc_agent.yaml ```. 26 | 27 | Make sure to re-build your Docker image and restart your container when you make changes to your Agent. 28 | 29 | On Mac and Linux, you can run the Eidolon server directly on your machine without using Docker. Follow the [quickstart](https://www.eidolonai.com/docs/getting_started/quickstart/introduction/) tutorial on the [Eidolon AI Website](https://www.eidolonai.com/) to get started. 30 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/README.md: -------------------------------------------------------------------------------- 1 | # HTTPSSEClient 2 | 3 | This is an implementation of the server-sent events/event-source protocol (https://www.w3.org/TR/eventsource/) in GDScript for the Godot game engine. 4 | 5 | To use this, simply download this project and place it into the `res://addons/HTTPSSEClient/` folder in your project; then you can just turn it on. 6 | 7 | I've included Demo.tscn and Demo.gd to show the usage of this plugin, and here's a summary: 8 | 9 | 1) Download and place into the proper folder as the above suggests 10 | 2) Switch the new plugin, found in Project Settings -> Plugins, to active 11 | 3) Instantiate a new HTTPSSEClient node in your scene tree somewhere 12 | 4) Click on the script icon for the newly-created node 13 | 5) Enter in any connection information necessary to connect to your SSE-supported server; for demonstration purposes, I use Firebase, and in the config dictionary, I just add the entire config I get back from adding a new Android app to any Firebase project (it'll give you back the google-services.json file, copy/paste it into config and change the url in the script to firebase_url and you're set for this) 14 | 6) If you're using Firebase, you need a sub_url value that is something like "/your_demo_list.json?auth=" and then the value of either your Firebase ID token, or your database secret. It's not clear how long database secrets will remain functional as they're already deprecated, but it is supported for the time being due to backward compatibility issues. 15 | 16 | When using my GDFirebase plugin, all of the above is handled for you automatically, so you will only need to use the information provided by that plugin. 17 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/README.md: -------------------------------------------------------------------------------- 1 | # HTTPSSEClient 2 | 3 | This is an implementation of the server-sent events/event-source protocol (https://www.w3.org/TR/eventsource/) in GDScript for the Godot game engine. 4 | 5 | To use this, simply download this project and place it into the `res://addons/HTTPSSEClient/` folder in your project; then you can just turn it on. 6 | 7 | I've included Demo.tscn and Demo.gd to show the usage of this plugin, and here's a summary: 8 | 9 | 1) Download and place into the proper folder as the above suggests 10 | 2) Switch the new plugin, found in Project Settings -> Plugins, to active 11 | 3) Instantiate a new HTTPSSEClient node in your scene tree somewhere 12 | 4) Click on the script icon for the newly-created node 13 | 5) Enter in any connection information necessary to connect to your SSE-supported server; for demonstration purposes, I use Firebase, and in the config dictionary, I just add the entire config I get back from adding a new Android app to any Firebase project (it'll give you back the google-services.json file, copy/paste it into config and change the url in the script to firebase_url and you're set for this) 14 | 6) If you're using Firebase, you need a sub_url value that is something like "/your_demo_list.json?auth=" and then the value of either your Firebase ID token, or your database secret. It's not clear how long database secrets will remain functional as they're already deprecated, but it is supported for the time being due to backward compatibility issues. 15 | 16 | When using my GDFirebase plugin, all of the above is handled for you automatically, so you will only need to use the information provided by that plugin. 17 | -------------------------------------------------------------------------------- /demo/godot/main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://dkjsvji4wbrjk"] 2 | 3 | [ext_resource type="Script" path="res://main.gd" id="1_r3fuf"] 4 | [ext_resource type="PackedScene" uid="uid://b1bfyc86om0q3" path="res://world.tscn" id="1_wimlu"] 5 | [ext_resource type="PackedScene" uid="uid://qxal2f8l7iuk" path="res://Player/player.tscn" id="2_07ovo"] 6 | [ext_resource type="PackedScene" uid="uid://27i550khvkbd" path="res://NPC/npc.tscn" id="3_4ftqt"] 7 | [ext_resource type="PackedScene" uid="uid://de01h04q55cgs" path="res://UI/dialogue_box.tscn" id="4_8ocqm"] 8 | [ext_resource type="PackedScene" uid="uid://bcfug5ny0wrib" path="res://addons/EidolonClient/eidolon_handler.tscn" id="6_olgdq"] 9 | 10 | [node name="main" type="Node"] 11 | script = ExtResource("1_r3fuf") 12 | 13 | [node name="World" parent="." instance=ExtResource("1_wimlu")] 14 | 15 | [node name="Player" parent="World" instance=ExtResource("2_07ovo")] 16 | 17 | [node name="NPC" parent="World" instance=ExtResource("3_4ftqt")] 18 | position = Vector2(2, -98) 19 | 20 | [node name="DialogueBox" parent="." instance=ExtResource("4_8ocqm")] 21 | 22 | [node name="EidolonHandler" parent="." instance=ExtResource("6_olgdq")] 23 | 24 | [connection signal="end_conversation" from="World" to="." method="_on_world_end_conversation"] 25 | [connection signal="start_conversation" from="World" to="." method="_on_world_start_conversation"] 26 | [connection signal="send_message" from="DialogueBox" to="." method="_on_dialogue_box_send_message"] 27 | [connection signal="finish_message" from="EidolonHandler" to="." method="_on_eidolon_handler_finish_message"] 28 | [connection signal="get_message" from="EidolonHandler" to="." method="_on_eidolon_handler_get_message"] 29 | [connection signal="get_process_id" from="EidolonHandler" to="." method="_on_eidolon_handler_get_process_id"] 30 | [connection signal="new_message" from="EidolonHandler" to="." method="_on_eidolon_handler_new_message"] 31 | -------------------------------------------------------------------------------- /EidolonClient/EidolonHandler.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var process_id = "" 4 | 5 | var agent = "npc_agent" 6 | var title = "first meeting" 7 | 8 | signal get_process_id(process_id: String) 9 | signal get_message(message: String) 10 | signal new_message 11 | signal finish_message 12 | 13 | func _ready(): 14 | var post_req = HTTPRequest.new() 15 | add_child(post_req) 16 | post_req.request_completed.connect(_set_process_id) 17 | var url = "http://localhost:8080/processes" 18 | var headers = ["Content-Type: application/json"] 19 | var method = HTTPClient.METHOD_POST 20 | var body = JSON.stringify({ 21 | "agent": agent, 22 | "title": title 23 | }) 24 | post_req.request(url, headers, method, body) 25 | 26 | func on_sse_connected(): 27 | $HTTPSSEClient.new_sse_event.connect(on_new_sse_event) 28 | 29 | func on_new_sse_event(headers, event, data): 30 | match data["category"]: 31 | "end": 32 | finish_message.emit() 33 | $HTTPSSEClient.is_requested = false 34 | "output": get_message.emit(data["content"]) 35 | "transform": pass 36 | 37 | func post_message(message: String): 38 | var url = "http://localhost:8080/processes/%s/agent/%s/actions/converse" % [process_id, agent] 39 | var headers = ["Content-Type: application/json", "Accept: text/event-stream"] 40 | var method = HTTPClient.METHOD_POST 41 | var body = JSON.stringify(message) 42 | $HTTPSSEClient.set_outgoing_request(method, url, headers, body) 43 | new_message.emit() 44 | 45 | func _set_process_id(result, response_code, headers, body): 46 | var json = JSON.new() 47 | json.parse(body.get_string_from_utf8()) 48 | var response = json.get_data() 49 | process_id = response.process_id 50 | get_process_id.emit(process_id) 51 | _connect_sse() 52 | 53 | func _connect_sse(): 54 | var sub_url = "" # Add the "/sub_list_url" stuff here, including query parameters as needed; for demo purposes, I use the list path in my Firebase database, combined with ".json?auth=" and whatever the auth token is. 55 | $HTTPSSEClient.process_id = process_id 56 | $HTTPSSEClient.connected.connect(on_sse_connected) 57 | $HTTPSSEClient.connect_to_host("localhost", sub_url, 8080, true, false) 58 | 59 | 60 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/EidolonHandler.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var process_id = "" 4 | 5 | var agent = "npc_agent" 6 | var title = "first meeting" 7 | 8 | signal get_process_id(process_id: String) 9 | signal get_message(message: String) 10 | signal new_message 11 | signal finish_message 12 | 13 | func _ready(): 14 | var post_req = HTTPRequest.new() 15 | add_child(post_req) 16 | post_req.request_completed.connect(_set_process_id) 17 | var url = "http://localhost:8080/processes" 18 | var headers = ["Content-Type: application/json"] 19 | var method = HTTPClient.METHOD_POST 20 | var body = JSON.stringify({ 21 | "agent": agent, 22 | "title": title 23 | }) 24 | post_req.request(url, headers, method, body) 25 | 26 | func on_sse_connected(): 27 | $HTTPSSEClient.new_sse_event.connect(on_new_sse_event) 28 | 29 | func on_new_sse_event(headers, event, data): 30 | match data["category"]: 31 | "end": 32 | finish_message.emit() 33 | $HTTPSSEClient.is_requested = false 34 | "output": get_message.emit(data["content"]) 35 | "transform": pass 36 | 37 | func post_message(message: String): 38 | var url = "http://localhost:8080/processes/%s/agent/%s/actions/converse" % [process_id, agent] 39 | var headers = ["Content-Type: application/json", "Accept: text/event-stream"] 40 | var method = HTTPClient.METHOD_POST 41 | var body = JSON.stringify(message) 42 | $HTTPSSEClient.set_outgoing_request(method, url, headers, body) 43 | new_message.emit() 44 | 45 | func _set_process_id(result, response_code, headers, body): 46 | var json = JSON.new() 47 | json.parse(body.get_string_from_utf8()) 48 | var response = json.get_data() 49 | process_id = response.process_id 50 | get_process_id.emit(process_id) 51 | _connect_sse() 52 | 53 | func _connect_sse(): 54 | var sub_url = "" # Add the "/sub_list_url" stuff here, including query parameters as needed; for demo purposes, I use the list path in my Firebase database, combined with ".json?auth=" and whatever the auth token is. 55 | $HTTPSSEClient.process_id = process_id 56 | $HTTPSSEClient.connected.connect(on_sse_connected) 57 | $HTTPSSEClient.connect_to_host("localhost", sub_url, 8080, true, false) 58 | 59 | 60 | -------------------------------------------------------------------------------- /demo/godot/Player/Player.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ################# 4 | ### Variables ### 5 | ################# 6 | 7 | # Variable that holds a reference to the parent world node 8 | # Set from _ready 9 | # Object 10 | var world 11 | 12 | # Variable that holds an integer that determines the player's speed 13 | # int 14 | var speed = 80 15 | 16 | var talking = false 17 | 18 | ########################## 19 | ### Built in Functions ### 20 | ########################## 21 | 22 | ### _ready() ### 23 | # Invoked when node is first instantiated. 24 | func _ready(): 25 | # assign parent node to world variable 26 | world = get_parent() 27 | world.player = self 28 | _set_scale(1.5) 29 | 30 | ### _physics_process(delta: float) ### 31 | # Invoked every frame 32 | func _physics_process(delta): 33 | # invoke handle_movement passing down the delta argument 34 | handle_movement(delta) 35 | # invoke choose_animation logic 36 | choose_animation() 37 | 38 | ######################## 39 | ### Custom Functions ### 40 | ######################## 41 | 42 | ### add_body_to_world(body) ### 43 | # Custom function invoked to add a body to the current game world's scene tree 44 | # Called from _ready, shoot, _on_mob_spawn_path_add_mob_to_world 45 | func add_body_to_world(body): 46 | world.add_child(body) 47 | 48 | func _set_scale(incoming_scale): 49 | var new_scale = Vector2(incoming_scale, incoming_scale) 50 | $CollisionShape2D.apply_scale(new_scale) 51 | $PlayerSkinSelector.get_node("Sprite2D").apply_scale(new_scale) 52 | 53 | ### handle_movement(delta) ### 54 | # Custom function invoked to listen for player input and move the Player node 55 | # Called from _physics_process 56 | func handle_movement(delta): 57 | # Reset Velocity every frame 58 | velocity = Vector2.ZERO 59 | # Listen for input in Y-Axis. Assign it to variable vertical_axis 60 | var vertical_axis = Input.get_axis("move_up", "move_down") 61 | # Listen for input in X-Axis. Assign it to variable horizontal_axis 62 | var horizontal_axis = Input.get_axis("move_left","move_right") 63 | if not talking: 64 | # Set Velocity Vectors with player input 65 | velocity.y += vertical_axis 66 | velocity.x += horizontal_axis 67 | $Rotation.look_at(get_global_mouse_position()) 68 | # Apply speed to velocity vector and normalize Velocity 69 | if velocity.length() > 0: 70 | velocity = velocity.normalized() * speed 71 | # Apply velocity to position 72 | position += velocity * delta 73 | # Apply rotation 74 | 75 | ### choose_animation() ### 76 | # Custom function invoked to choose an animation based on player action and play it 77 | # Called from _physics_process 78 | func choose_animation(): 79 | # assign "idle" into modifier variable 80 | var modifier = "idle" 81 | # invoke choose_animation logic on PlayerSkinSelector node passing velocity, Rotation node's rotation, and modifier as the arguments 82 | $PlayerSkinSelector.choose_animation(velocity, $Rotation.rotation_degrees, modifier) 83 | 84 | 85 | -------------------------------------------------------------------------------- /demo/godot/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="AgentDemo2" 14 | run/main_scene="res://main.tscn" 15 | config/features=PackedStringArray("4.2", "GL Compatibility") 16 | config/icon="res://icon.svg" 17 | 18 | [display] 19 | 20 | window/size/viewport_width=1024 21 | window/size/viewport_height=640 22 | window/stretch/mode="canvas_items" 23 | 24 | [editor_plugins] 25 | 26 | enabled=PackedStringArray("res://addons/EidolonClient/plugin.cfg") 27 | 28 | [input] 29 | 30 | move_up={ 31 | "deadzone": 0.5, 32 | "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":4194320,"key_label":0,"unicode":0,"echo":false,"script":null) 33 | , 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,"echo":false,"script":null) 34 | ] 35 | } 36 | move_left={ 37 | "deadzone": 0.5, 38 | "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,"echo":false,"script":null) 39 | , 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,"echo":false,"script":null) 40 | ] 41 | } 42 | move_down={ 43 | "deadzone": 0.5, 44 | "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,"echo":false,"script":null) 45 | , 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,"echo":false,"script":null) 46 | ] 47 | } 48 | move_right={ 49 | "deadzone": 0.5, 50 | "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":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) 51 | , 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,"echo":false,"script":null) 52 | ] 53 | } 54 | talk={ 55 | "deadzone": 0.5, 56 | "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":69,"key_label":0,"unicode":101,"echo":false,"script":null) 57 | ] 58 | } 59 | end_conversation={ 60 | "deadzone": 0.5, 61 | "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":4194305,"key_label":0,"unicode":0,"echo":false,"script":null) 62 | ] 63 | } 64 | 65 | [rendering] 66 | 67 | textures/canvas_textures/default_texture_filter=0 68 | renderer/rendering_method="gl_compatibility" 69 | renderer/rendering_method.mobile="gl_compatibility" 70 | -------------------------------------------------------------------------------- /EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal new_sse_event(headers, event, data) 4 | signal connected 5 | signal connection_error(error) 6 | 7 | const event_tag = "id:" 8 | const data_tag = "data:" 9 | const continue_internal = "continue_internal" 10 | 11 | var httpclient = HTTPClient.new() 12 | var is_connected = false 13 | 14 | var domain 15 | var url_after_domain 16 | var port 17 | var use_ssl 18 | var verify_host 19 | var told_to_connect = false 20 | var outgoing_request = null 21 | var connection_in_progress = false 22 | var request_in_progress = false 23 | var is_requested = false 24 | var response_body = PackedByteArray() 25 | 26 | var process_id 27 | 28 | func connect_to_host(domain : String, url_after_domain : String, port : int = -1, use_ssl : bool = false, verify_host : bool = true): 29 | self.domain = domain 30 | self.url_after_domain = url_after_domain 31 | self.port = port 32 | self.use_ssl = use_ssl 33 | self.verify_host = verify_host 34 | told_to_connect = true 35 | 36 | func attempt_to_connect(): 37 | var err = httpclient.connect_to_host(domain, port) 38 | if err == OK: 39 | emit_signal("connected") 40 | is_connected = true 41 | else: 42 | emit_signal("connection_error", str(err)) 43 | 44 | func attempt_to_request(httpclient_status): 45 | if httpclient_status == HTTPClient.STATUS_CONNECTING or httpclient_status == HTTPClient.STATUS_RESOLVING: 46 | return 47 | # When Eidolon backend pings httpclient, httpclient_status is set to HTTPClient.STATUS_CONNECTION_ERROR 48 | # To remedy this, the httpclient reconnects via attempt_to_connect 49 | if httpclient_status == HTTPClient.STATUS_CONNECTION_ERROR: attempt_to_connect() 50 | if httpclient_status == HTTPClient.STATUS_CONNECTED: 51 | var err = httpclient.request(outgoing_request["method"], outgoing_request["url"], outgoing_request["headers"], outgoing_request["body"]) 52 | if err == OK: 53 | outgoing_request = null 54 | is_requested = true 55 | 56 | func _process(delta): 57 | if !told_to_connect: 58 | return 59 | 60 | if !is_connected: 61 | if !connection_in_progress: 62 | attempt_to_connect() 63 | connection_in_progress = true 64 | return 65 | 66 | httpclient.poll() 67 | var httpclient_status = httpclient.get_status() 68 | if outgoing_request: 69 | if !is_requested: 70 | if !request_in_progress: 71 | attempt_to_request(httpclient_status) 72 | return 73 | 74 | var httpclient_has_response = httpclient.has_response() 75 | 76 | if httpclient_has_response or httpclient_status == HTTPClient.STATUS_BODY: 77 | var headers = httpclient.get_response_headers_as_dictionary() 78 | httpclient.poll() 79 | var chunk = httpclient.read_response_body_chunk() 80 | if(chunk.size() == 0): 81 | return 82 | else: 83 | response_body = response_body + chunk 84 | 85 | var json = JSON.new() 86 | var body = response_body.get_string_from_utf8() 87 | if body: 88 | var event_data = get_event_data(body) 89 | if event_data.event != "keep-alive" and event_data.event != continue_internal: 90 | var result = event_data.data 91 | if response_body.size() > 0 and result: # stop here if the value doesn't parse 92 | response_body.resize(0) 93 | emit_signal("new_sse_event", headers, event_data.event, result) 94 | else: 95 | if event_data.event != continue_internal: 96 | response_body.resize(0) 97 | 98 | func set_outgoing_request(method, url, headers, body): 99 | if not outgoing_request: 100 | outgoing_request = {"method":method, "url":url, "headers":headers, "body":body} 101 | 102 | func get_event_data(body : String) -> Dictionary: 103 | var json = JSON.new() 104 | body = body.strip_edges() 105 | var result = {} 106 | var event_idx = body.find(event_tag) 107 | if event_idx == -1: 108 | result["event"] = continue_internal 109 | return result 110 | assert(event_idx != -1) 111 | var data_idx = body.find(data_tag) 112 | assert(data_idx != -1) 113 | var event = body.substr(event_idx, data_idx) 114 | event = event.replace(event_tag, "").strip_edges() 115 | assert(event) 116 | assert(event.length() > 0) 117 | result["event"] = event 118 | var data = body.right(-(data_idx + data_tag.length())).strip_edges() 119 | assert(data) 120 | assert(data.length() > 0) 121 | result["data"] = JSON.parse_string(data) 122 | return result 123 | 124 | func _exit_tree(): 125 | if httpclient: 126 | httpclient.close() 127 | 128 | func _notification(what): 129 | if what == NOTIFICATION_WM_CLOSE_REQUEST: 130 | if httpclient: 131 | httpclient.close() 132 | get_tree().quit() 133 | -------------------------------------------------------------------------------- /demo/godot/addons/EidolonClient/addons/HTTPSSEClient/HTTPSSEClient.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal new_sse_event(headers, event, data) 4 | signal connected 5 | signal connection_error(error) 6 | 7 | const event_tag = "id:" 8 | const data_tag = "data:" 9 | const continue_internal = "continue_internal" 10 | 11 | var httpclient = HTTPClient.new() 12 | var is_connected = false 13 | 14 | var domain 15 | var url_after_domain 16 | var port 17 | var use_ssl 18 | var verify_host 19 | var told_to_connect = false 20 | var outgoing_request = null 21 | var connection_in_progress = false 22 | var request_in_progress = false 23 | var is_requested = false 24 | var response_body = PackedByteArray() 25 | 26 | var process_id 27 | 28 | func connect_to_host(domain : String, url_after_domain : String, port : int = -1, use_ssl : bool = false, verify_host : bool = true): 29 | self.domain = domain 30 | self.url_after_domain = url_after_domain 31 | self.port = port 32 | self.use_ssl = use_ssl 33 | self.verify_host = verify_host 34 | told_to_connect = true 35 | 36 | func attempt_to_connect(): 37 | var err = httpclient.connect_to_host(domain, port) 38 | if err == OK: 39 | emit_signal("connected") 40 | is_connected = true 41 | else: 42 | emit_signal("connection_error", str(err)) 43 | 44 | func attempt_to_request(httpclient_status): 45 | if httpclient_status == HTTPClient.STATUS_CONNECTING or httpclient_status == HTTPClient.STATUS_RESOLVING: 46 | return 47 | # When Eidolon backend pings httpclient, httpclient_status is set to HTTPClient.STATUS_CONNECTION_ERROR 48 | # To remedy this, the httpclient reconnects via attempt_to_connect 49 | if httpclient_status == HTTPClient.STATUS_CONNECTION_ERROR: attempt_to_connect() 50 | if httpclient_status == HTTPClient.STATUS_CONNECTED: 51 | var err = httpclient.request(outgoing_request["method"], outgoing_request["url"], outgoing_request["headers"], outgoing_request["body"]) 52 | if err == OK: 53 | outgoing_request = null 54 | is_requested = true 55 | 56 | func _process(delta): 57 | if !told_to_connect: 58 | return 59 | 60 | if !is_connected: 61 | if !connection_in_progress: 62 | attempt_to_connect() 63 | connection_in_progress = true 64 | return 65 | 66 | httpclient.poll() 67 | var httpclient_status = httpclient.get_status() 68 | if outgoing_request: 69 | if !is_requested: 70 | if !request_in_progress: 71 | attempt_to_request(httpclient_status) 72 | return 73 | 74 | var httpclient_has_response = httpclient.has_response() 75 | 76 | if httpclient_has_response or httpclient_status == HTTPClient.STATUS_BODY: 77 | var headers = httpclient.get_response_headers_as_dictionary() 78 | httpclient.poll() 79 | var chunk = httpclient.read_response_body_chunk() 80 | if(chunk.size() == 0): 81 | return 82 | else: 83 | response_body = response_body + chunk 84 | 85 | var json = JSON.new() 86 | var body = response_body.get_string_from_utf8() 87 | if body: 88 | var event_data = get_event_data(body) 89 | if event_data.event != "keep-alive" and event_data.event != continue_internal: 90 | var result = event_data.data 91 | if response_body.size() > 0 and result: # stop here if the value doesn't parse 92 | response_body.resize(0) 93 | emit_signal("new_sse_event", headers, event_data.event, result) 94 | else: 95 | if event_data.event != continue_internal: 96 | response_body.resize(0) 97 | 98 | func set_outgoing_request(method, url, headers, body): 99 | if not outgoing_request: 100 | outgoing_request = {"method":method, "url":url, "headers":headers, "body":body} 101 | 102 | func get_event_data(body : String) -> Dictionary: 103 | var json = JSON.new() 104 | body = body.strip_edges() 105 | var result = {} 106 | var event_idx = body.find(event_tag) 107 | if event_idx == -1: 108 | result["event"] = continue_internal 109 | return result 110 | assert(event_idx != -1) 111 | var data_idx = body.find(data_tag) 112 | assert(data_idx != -1) 113 | var event = body.substr(event_idx, data_idx) 114 | event = event.replace(event_tag, "").strip_edges() 115 | assert(event) 116 | assert(event.length() > 0) 117 | result["event"] = event 118 | var data = body.right(-(data_idx + data_tag.length())).strip_edges() 119 | assert(data) 120 | assert(data.length() > 0) 121 | result["data"] = JSON.parse_string(data) 122 | return result 123 | 124 | func _exit_tree(): 125 | if httpclient: 126 | httpclient.close() 127 | 128 | func _notification(what): 129 | if what == NOTIFICATION_WM_CLOSE_REQUEST: 130 | if httpclient: 131 | httpclient.close() 132 | get_tree().quit() 133 | -------------------------------------------------------------------------------- /demo/godot/NPC/basic_mob_sprite.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=3 uid="uid://ca1sjayv20fc3"] 2 | 3 | [ext_resource type="Script" path="res://NPC/basic_mob_sprite.gd" id="1_l8hwo"] 4 | [ext_resource type="Texture2D" uid="uid://b5xlcddgovqtb" path="res://art/knight.png" id="2_ybqq2"] 5 | 6 | [sub_resource type="Animation" id="Animation_ilnws"] 7 | length = 0.001 8 | tracks/0/type = "value" 9 | tracks/0/imported = false 10 | tracks/0/enabled = true 11 | tracks/0/path = NodePath("Sprite2D:frame_coords") 12 | tracks/0/interp = 1 13 | tracks/0/loop_wrap = true 14 | tracks/0/keys = { 15 | "times": PackedFloat32Array(0), 16 | "transitions": PackedFloat32Array(1), 17 | "update": 1, 18 | "values": [Vector2i(0, 0)] 19 | } 20 | 21 | [sub_resource type="Animation" id="Animation_jodxd"] 22 | resource_name = "standing_down" 23 | length = 0.8 24 | loop_mode = 1 25 | tracks/0/type = "value" 26 | tracks/0/imported = false 27 | tracks/0/enabled = true 28 | tracks/0/path = NodePath("Sprite2D:frame_coords") 29 | tracks/0/interp = 1 30 | tracks/0/loop_wrap = true 31 | tracks/0/keys = { 32 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 33 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 34 | "update": 1, 35 | "values": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), Vector2i(3, 0), Vector2i(4, 0), Vector2i(5, 0), Vector2i(6, 0), Vector2i(7, 0)] 36 | } 37 | 38 | [sub_resource type="Animation" id="Animation_fvvtr"] 39 | length = 0.8 40 | loop_mode = 1 41 | tracks/0/type = "value" 42 | tracks/0/imported = false 43 | tracks/0/enabled = true 44 | tracks/0/path = NodePath("Sprite2D:frame_coords") 45 | tracks/0/interp = 1 46 | tracks/0/loop_wrap = true 47 | tracks/0/keys = { 48 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 49 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 50 | "update": 1, 51 | "values": [Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1), Vector2i(3, 1), Vector2i(4, 1), Vector2i(5, 1), Vector2i(6, 1), Vector2i(7, 1)] 52 | } 53 | 54 | [sub_resource type="Animation" id="Animation_dxlbn"] 55 | length = 0.8 56 | loop_mode = 1 57 | tracks/0/type = "value" 58 | tracks/0/imported = false 59 | tracks/0/enabled = true 60 | tracks/0/path = NodePath("Sprite2D:frame_coords") 61 | tracks/0/interp = 1 62 | tracks/0/loop_wrap = true 63 | tracks/0/keys = { 64 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 65 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 66 | "update": 1, 67 | "values": [Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2), Vector2i(3, 2), Vector2i(4, 2), Vector2i(5, 2), Vector2i(6, 2), Vector2i(7, 2)] 68 | } 69 | 70 | [sub_resource type="Animation" id="Animation_lexk4"] 71 | length = 0.8 72 | loop_mode = 1 73 | tracks/0/type = "value" 74 | tracks/0/imported = false 75 | tracks/0/enabled = true 76 | tracks/0/path = NodePath("Sprite2D:frame_coords") 77 | tracks/0/interp = 1 78 | tracks/0/loop_wrap = true 79 | tracks/0/keys = { 80 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 81 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 82 | "update": 1, 83 | "values": [Vector2i(0, 3), Vector2i(1, 3), Vector2i(2, 3), Vector2i(3, 3), Vector2i(4, 3), Vector2i(5, 3), Vector2i(6, 3), Vector2i(7, 3)] 84 | } 85 | 86 | [sub_resource type="Animation" id="Animation_nlt27"] 87 | length = 0.8 88 | loop_mode = 1 89 | tracks/0/type = "value" 90 | tracks/0/imported = false 91 | tracks/0/enabled = true 92 | tracks/0/path = NodePath("Sprite2D:frame_coords") 93 | tracks/0/interp = 1 94 | tracks/0/loop_wrap = true 95 | tracks/0/keys = { 96 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 97 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 98 | "update": 1, 99 | "values": [Vector2i(0, 4), Vector2i(1, 4), Vector2i(2, 4), Vector2i(3, 4), Vector2i(4, 4), Vector2i(5, 4), Vector2i(6, 4), Vector2i(7, 4)] 100 | } 101 | 102 | [sub_resource type="Animation" id="Animation_ivt1l"] 103 | length = 0.8 104 | loop_mode = 1 105 | tracks/0/type = "value" 106 | tracks/0/imported = false 107 | tracks/0/enabled = true 108 | tracks/0/path = NodePath("Sprite2D:frame_coords") 109 | tracks/0/interp = 1 110 | tracks/0/loop_wrap = true 111 | tracks/0/keys = { 112 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 113 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 114 | "update": 1, 115 | "values": [Vector2i(0, 5), Vector2i(1, 5), Vector2i(2, 5), Vector2i(3, 5), Vector2i(4, 5), Vector2i(5, 5), Vector2i(6, 5), Vector2i(7, 5)] 116 | } 117 | 118 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_vefey"] 119 | _data = { 120 | "RESET": SubResource("Animation_ilnws"), 121 | "standing_down": SubResource("Animation_jodxd"), 122 | "standing_side": SubResource("Animation_fvvtr"), 123 | "standing_up": SubResource("Animation_dxlbn"), 124 | "walking_down": SubResource("Animation_lexk4"), 125 | "walking_side": SubResource("Animation_nlt27"), 126 | "walking_up": SubResource("Animation_ivt1l") 127 | } 128 | 129 | [node name="BasicMobSprite" type="Node2D"] 130 | script = ExtResource("1_l8hwo") 131 | 132 | [node name="Sprite2D" type="Sprite2D" parent="."] 133 | texture = ExtResource("2_ybqq2") 134 | hframes = 8 135 | vframes = 6 136 | 137 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 138 | libraries = { 139 | "": SubResource("AnimationLibrary_vefey") 140 | } 141 | -------------------------------------------------------------------------------- /demo/godot/NPC/basic_mob_sprite.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ################# 4 | ### Variables ### 5 | ################# 6 | 7 | # Variable that holds a boolean indicating whether or not the mob is walking 8 | # Set in _apply_velocity() 9 | # bool 10 | var walking = false 11 | 12 | # Variable that holds a string indicating which direction the mob is facing 13 | # Set in apply_velocity() 14 | # str 15 | var direction = "down" 16 | 17 | # Variable that holds a string indicating which animation is currently playing 18 | # Set in choose_animation() 19 | # str 20 | var current_animation = "" 21 | 22 | ######################## 23 | ### Custom Functions ### 24 | ######################## 25 | 26 | ### choose_type(incoming_type: str) ### 27 | # Custom function invoked to set the texture of Sprite2D node 28 | # Called from BasicMob node 29 | func choose_type(incoming_type): 30 | # Set the path variable to a string interpolated with the incoming_type argument 31 | var path = "res://art/%s.png" % incoming_type 32 | # Set Sprite2D node's texture to the result of loading the path variable 33 | $Sprite2D.texture = load(path) 34 | $Sprite2D.apply_scale(Vector2(1.5,1.5)) 35 | 36 | ### choose_animation(velocity: Vector2, incoming_modifier: str) ### 37 | # Custom function invoked to choose the proper animation according to incoming parameters 38 | # Called from Player node 39 | func choose_animation(velocity, rotation): 40 | # invoke _apply_velocity logic passing velocity parameter normalized down 41 | _apply_velocity(velocity.normalized(), rotation) 42 | # assign new variable new_animation to an empty string 43 | var new_animation = "" 44 | # If the walking variable is true, start new_animation with "walking_" 45 | if walking: new_animation += "walking_" 46 | # If the walking variable is false, start new_animation with "standing_" 47 | else: new_animation += "standing_" 48 | # If the direction is in the y-axis, append the direction variable to new_animation variable 49 | if direction == "up" or direction == "down": new_animation += direction 50 | # If the direction is in the x-axis, append "side" to the new_animation variable 51 | elif direction == "right" or direction == "left": new_animation += "side" 52 | # Check if the current_animation variable does not match the new_animation variable 53 | if current_animation != new_animation: 54 | # Assign the new_animation variable's contents into the current_animation variable 55 | current_animation = new_animation 56 | # Invoke AnimationPlayer play logic passing current_animation as the argument 57 | $AnimationPlayer.play(current_animation) 58 | 59 | ### _apply_velocity(velocity: Vector2, rotation: float, modifier: str) ### 60 | # Custom function invoked to set walking and direction variables according to a given velocity 61 | # Called from choose_animation() 62 | func _apply_velocity(velocity, rotation): 63 | # If the velocity indicates no movement, set walking variable to false 64 | if velocity.x == 0 and velocity.y == 0: walking = false 65 | # If the velocity indicates movment, set walking variable to true 66 | if abs(velocity.x) > 0 or abs(velocity.y) > 0: walking = true 67 | # Check if the walking variable is true and if the modifier argument is "idle" 68 | if walking: 69 | # Check if magnitude of the movment in the x axis is greater than the magnitude of the movment in the y axis 70 | if abs(velocity.x) > abs(velocity.y): 71 | # If moving right 72 | if velocity.x > 0: 73 | # Set direction variable to "right" and set Sprite2D node's flip_h to true 74 | direction = "right" 75 | $Sprite2D.flip_h = true 76 | # Else if moving left 77 | elif velocity.x < 0: 78 | # Set direction variable to "left" and set Sprite2D node's flip_h to false 79 | direction = "left" 80 | $Sprite2D.flip_h = false 81 | # Else, check if magnitude of the movment in the y axis is greater than the magnitude of the movment in the x axis 82 | elif abs(velocity.x) < abs(velocity.y): 83 | # If moving down, set direction variable to "down" 84 | if velocity.y > 0: direction = "down" 85 | # Else if moving up, set direction variable to "up" 86 | elif velocity.y < 0: direction = "up" 87 | # If the walking variable is false or the modifier argument is not "idle" 88 | # invoke _apply_rotation logic passing down the rotation argument 89 | else: _apply_rotation(rotation) 90 | 91 | ### _apply_rotation(rotation: float) ### 92 | # Custom function invoked to change the direction based on a given rotation 93 | # Called from _apply_velocity 94 | func _apply_rotation(rotation): 95 | # set rotation variable as the remaineder of the rotation argument cast into an integer / 360 96 | rotation = int(rotation) % 360 97 | # if the rotation variable is less than 0 add 360 to it 98 | if rotation < 0: rotation += 360 99 | # 0 degrees is pointing right. Check rotation variable and change the direction variable appropriately 100 | if rotation > 315 or rotation < 45: direction = "right" 101 | elif rotation > 45 and rotation < 135: direction = "down" 102 | elif rotation > 135 and rotation < 225: direction = "left" 103 | elif rotation > 225 and rotation < 315: direction = "up" 104 | # If the direction variable is "left" don't flip the Sprite2D 105 | if direction == "left": $Sprite2D.flip_h = false 106 | # If the direction variable is "left" flip the Sprite2D 107 | elif direction == "right": $Sprite2D.flip_h = true 108 | 109 | ### die() ### 110 | # Custom function invoked to clear status shader and stop the animation 111 | # Called from BasicMob node 112 | func die(): 113 | # modulate color to default 114 | $Sprite2D.modulate = Color(1,1,1,1) 115 | # Stop AnimationPlayer's animation from playing 116 | $AnimationPlayer.stop() 117 | 118 | ### reanimate(incoming_tome: int) ### 119 | # Custom function invoked to modulate color to based on incoming tome 120 | # Called from BasicMob node 121 | func reanimate(incoming_tome): 122 | # Start AnimationPlayer's animation 123 | $AnimationPlayer.play() 124 | # TEMPORARY - If incoming_tome < 0 modulate color to Blue 125 | if incoming_tome: $Sprite2D.modulate = Color(5,1,5,0.9) 126 | # TEMPORARY - Else if incoming tome > 0 modulate color to Purple 127 | else: $Sprite2D.modulate = Color(1,1,5,0.9) 128 | 129 | ### cleanse(incoming_holy: int) ### 130 | # Custom function invoked to modulate color based on incoming_holy 131 | # Called from BasicMob node 132 | func cleanse(incoming_holy): 133 | $Sprite2D.modulate = Color(1.5,2,1,1) 134 | 135 | 136 | -------------------------------------------------------------------------------- /demo/godot/Player/player_skin_selector.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ################# 4 | ### Variables ### 5 | ################# 6 | 7 | # Variable that holds a boolean indicating whether or not the player is walking 8 | # Set in apply_velocity() 9 | # bool 10 | var walking = false 11 | 12 | # Variable that holds a boolean indicating whether or not the player is flashing 13 | # Set in start_flashing(), Player node 14 | # bool 15 | var flashing = false 16 | 17 | # Variable that holds a boolean indicating whether or not the player sprite is all-white at the moment 18 | # Set in _on_flash_timer_timeout() 19 | # bool 20 | var all_white = false 21 | 22 | # Variable that holds a string indicating which direction the player is facing 23 | # Set in _apply_velocity() 24 | # str 25 | var direction = "down" 26 | 27 | # Variable that holds a string indicating which animation is currently playing 28 | # Set in choose_animation() 29 | # str 30 | var current_animation = "" 31 | 32 | ######################## 33 | ### Custom Functions ### 34 | ######################## 35 | 36 | ### choose_skin(incoming_skin: str) ### 37 | # Custom function invoked to set the texture of Sprite2D node 38 | # Called from Player node 39 | func choose_skin(incoming_skin="wizard_white"): 40 | # Set the path variable to a string interpolated with the incoming_skin argument 41 | var path = "res://art/%s.png" % incoming_skin 42 | # Set Sprite2D node's texture to the result of loading the path variable 43 | $Sprite2D.texture = load(path) 44 | 45 | ### choose_animation(velocity: Vector2, incoming_modifier: str) ### 46 | # Custom function invoked to choose the proper animation according to incoming parameters 47 | # Called from Player node 48 | func choose_animation(velocity, rotation, incoming_modifier): 49 | # invoke _apply_velocity logic passing velocity parameter normalized down 50 | _apply_velocity(velocity.normalized(), rotation, incoming_modifier) 51 | # assign new variable new_animation to an empty string 52 | var new_animation = "" 53 | # If the walking variable is true, start new_animation with "walking_" 54 | if walking: new_animation += "walking_" 55 | # If the walking variable is false, start new_animation with "standing_" 56 | else: new_animation += "standing_" 57 | # Append incoming_modifier argument + "_" to new_animation variable 58 | new_animation += incoming_modifier + "_" 59 | # If the direction is in the y-axis, append the direction variable to new_animation variable 60 | if direction == "up" or direction == "down": new_animation += direction 61 | # If the direction is in the x-axis, append "side" to the new_animation variable 62 | elif direction == "right" or direction == "left": new_animation += "side" 63 | # Check if the current_animation variable does not match the new_animation variable 64 | if current_animation != new_animation: 65 | # Assign the new_animation variable's contents into the current_animation variable 66 | current_animation = new_animation 67 | # Invoke AnimationPlayer play logic passing current_animation as the argument 68 | $AnimationPlayer.play(current_animation) 69 | 70 | ### _apply_velocity(velocity: Vector2, rotation: float, modifier: str) ### 71 | # Custom function invoked to set walking and direction variables according to a given velocity 72 | # Called from choose_animation() 73 | func _apply_velocity(velocity, rotation, modifier): 74 | # If the velocity indicates no movement, set walking variable to false 75 | if velocity.x == 0 and velocity.y == 0: walking = false 76 | # If the velocity indicates movment, set walking variable to true 77 | if abs(velocity.x) > 0 or abs(velocity.y) > 0: walking = true 78 | # Check if the walking variable is true and if the modifier argument is "idle" 79 | if walking and modifier == "idle": 80 | # Check if magnitude of the movment in the x axis is greater than the magnitude of the movment in the y axis 81 | if abs(velocity.x) > abs(velocity.y): 82 | # If moving right 83 | if velocity.x > 0: 84 | # Set direction variable to "right" and set Sprite2D node's flip_h to true 85 | direction = "right" 86 | $Sprite2D.flip_h = true 87 | # Else if moving left 88 | elif velocity.x < 0: 89 | # Set direction variable to "left" and set Sprite2D node's flip_h to false 90 | direction = "left" 91 | $Sprite2D.flip_h = false 92 | # Else, check if magnitude of the movment in the y axis is greater than the magnitude of the movment in the x axis 93 | elif abs(velocity.x) < abs(velocity.y): 94 | # If moving down, set direction variable to "down" 95 | if velocity.y > 0: direction = "down" 96 | # Else if moving up, set direction variable to "up" 97 | elif velocity.y < 0: direction = "up" 98 | # If the walking variable is false or the modifier argument is not "idle" 99 | # invoke _apply_rotation logic passing down the rotation argument 100 | else: _apply_rotation(rotation) 101 | 102 | ### _apply_rotation(rotation: float) ### 103 | # Custom function invoked to change the direction based on a given rotation 104 | # Called from _apply_velocity 105 | func _apply_rotation(rotation): 106 | # set rotation variable as the remaineder of the rotation argument cast into an integer / 360 107 | rotation = int(rotation) % 360 108 | # if the rotation variable is less than 0 add 360 to it 109 | if rotation < 0: rotation += 360 110 | # 0 degrees is pointing right. Check rotation variable and change the direction variable appropriately 111 | if rotation > 315 or rotation < 45: direction = "right" 112 | elif rotation > 45 and rotation < 135: direction = "down" 113 | elif rotation > 135 and rotation < 225: direction = "left" 114 | elif rotation > 225 and rotation < 315: direction = "up" 115 | # If the direction variable is "left" don't flip the Sprite2D 116 | if direction == "left": $Sprite2D.flip_h = false 117 | # If the direction variable is "left" flip the Sprite2D 118 | elif direction == "right": $Sprite2D.flip_h = true 119 | 120 | ### start_flashing() ### 121 | # Custom function invoked to begin the flashing animation 122 | # Called from Player node 123 | func start_flashing(): 124 | # Set flashing variable to true and start FlashTimer 125 | flashing = true 126 | $FlashTimer.start() 127 | 128 | ######################## 129 | ### Signal Listeners ### 130 | ######################## 131 | 132 | ### _on_flash_timer_timeout() ### 133 | # Emitted from FlashTimer node 134 | func _on_flash_timer_timeout(): 135 | # Check if the flashing variable is true 136 | if flashing: 137 | # Check if the all_white variable is true 138 | if all_white: 139 | # set all_white variable to false and modulate Color to default 140 | all_white = false 141 | $Sprite2D.modulate = Color(1,1,1,1) 142 | # If the all_white variable is false 143 | else: 144 | # set all_white variable to true and modulate Color to white 145 | all_white = true 146 | $Sprite2D.modulate = Color(10,10,10,10) 147 | # If the flashing variable is false 148 | else: 149 | # Stop the FlashTimer 150 | $FlashTimer.stop() 151 | # set all_white variable to false and modulate Color to default 152 | all_white = false 153 | $Sprite2D.modulate = Color(1,1,1,1) 154 | -------------------------------------------------------------------------------- /demo/godot/world.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://b1bfyc86om0q3"] 2 | 3 | [ext_resource type="Script" path="res://world.gd" id="1_p2e2y"] 4 | [ext_resource type="Texture2D" uid="uid://chdf55pqrl6bb" path="res://art/grass.png" id="1_rovhj"] 5 | 6 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_e4bel"] 7 | texture = ExtResource("1_rovhj") 8 | texture_region_size = Vector2i(32, 32) 9 | 0:0/0 = 0 10 | 1:0/0 = 0 11 | 2:0/0 = 0 12 | 3:0/0 = 0 13 | 4:0/0 = 0 14 | 5:0/0 = 0 15 | 6:0/0 = 0 16 | 7:0/0 = 0 17 | 8:0/0 = 0 18 | 9:0/0 = 0 19 | 10:0/0 = 0 20 | 11:0/0 = 0 21 | 0:1/0 = 0 22 | 1:1/0 = 0 23 | 2:1/0 = 0 24 | 3:1/0 = 0 25 | 4:1/0 = 0 26 | 5:1/0 = 0 27 | 6:1/0 = 0 28 | 7:1/0 = 0 29 | 8:1/0 = 0 30 | 9:1/0 = 0 31 | 10:1/0 = 0 32 | 11:1/0 = 0 33 | 0:2/0 = 0 34 | 1:2/0 = 0 35 | 2:2/0 = 0 36 | 3:2/0 = 0 37 | 4:2/0 = 0 38 | 5:2/0 = 0 39 | 6:2/0 = 0 40 | 7:2/0 = 0 41 | 8:2/0 = 0 42 | 9:2/0 = 0 43 | 10:2/0 = 0 44 | 11:2/0 = 0 45 | 0:3/0 = 0 46 | 1:3/0 = 0 47 | 2:3/0 = 0 48 | 3:3/0 = 0 49 | 4:3/0 = 0 50 | 5:3/0 = 0 51 | 6:3/0 = 0 52 | 7:3/0 = 0 53 | 8:3/0 = 0 54 | 9:3/0 = 0 55 | 10:3/0 = 0 56 | 11:3/0 = 0 57 | 58 | [sub_resource type="TileSet" id="TileSet_mgf8i"] 59 | tile_size = Vector2i(32, 32) 60 | sources/0 = SubResource("TileSetAtlasSource_e4bel") 61 | 62 | [node name="World" type="Node2D"] 63 | script = ExtResource("1_p2e2y") 64 | 65 | [node name="ParallaxBackground" type="ParallaxBackground" parent="."] 66 | 67 | [node name="ParallaxLayer" type="ParallaxLayer" parent="ParallaxBackground"] 68 | motion_mirroring = Vector2(1024, 640) 69 | 70 | [node name="TileMap" type="TileMap" parent="ParallaxBackground/ParallaxLayer"] 71 | tile_set = SubResource("TileSet_mgf8i") 72 | rendering_quadrant_size = 32 73 | format = 2 74 | layer_0/tile_data = PackedInt32Array(0, 0, 0, 65536, 0, 1, 131072, 0, 0, 196608, 0, 0, 262144, 0, 1, 327680, 0, 0, 393216, 0, 0, 458752, 0, 0, 524288, 0, 0, 589824, 0, 0, 655360, 0, 0, 720896, 0, 1, 786432, 0, 1, 851968, 0, 0, 917504, 0, 1, 983040, 0, 1, 1048576, 0, 1, 1114112, 0, 1, 1, 0, 0, 65537, 0, 1, 131073, 0, 1, 196609, 0, 1, 262145, 0, 1, 327681, 0, 1, 393217, 0, 1, 458753, 0, 1, 524289, 0, 0, 589825, 0, 1, 655361, 0, 0, 720897, 0, 0, 786433, 0, 1, 851969, 0, 0, 917505, 0, 1, 983041, 0, 1, 1048577, 0, 0, 1114113, 0, 0, 2, 0, 1, 65538, 262144, 1, 131074, 0, 1, 196610, 0, 0, 262146, 0, 1, 327682, 0, 1, 393218, 262144, 0, 458754, 0, 1, 524290, 0, 1, 589826, 0, 0, 655362, 0, 0, 720898, 0, 1, 786434, 0, 0, 851970, 0, 1, 917506, 0, 0, 983042, 0, 1, 1048578, 0, 0, 1114114, 262144, 1, 3, 0, 1, 65539, 0, 1, 131075, 0, 1, 196611, 0, 1, 262147, 0, 0, 327683, 0, 1, 393219, 0, 0, 458755, 0, 0, 524291, 262144, 1, 589827, 0, 0, 655363, 0, 1, 720899, 0, 0, 786435, 0, 0, 851971, 0, 0, 917507, 0, 1, 983043, 0, 0, 1048579, 0, 1, 1114115, 262144, 0, 4, 0, 1, 65540, 0, 1, 131076, 0, 1, 196612, 0, 0, 262148, 0, 1, 327684, 0, 0, 393220, 0, 0, 458756, 0, 1, 524292, 0, 1, 589828, 0, 1, 655364, 0, 0, 720900, 0, 1, 786436, 0, 0, 851972, 0, 0, 917508, 0, 0, 983044, 0, 1, 1048580, 0, 1, 1114116, 0, 1, 5, 0, 0, 65541, 0, 1, 131077, 0, 0, 196613, 0, 0, 262149, 0, 0, 327685, 0, 1, 393221, 131072, 1, 458757, 0, 0, 524293, 0, 0, 589829, 0, 1, 655365, 0, 1, 720901, 0, 1, 786437, 0, 1, 851973, 0, 1, 917509, 0, 1, 983045, 0, 0, 1048581, 0, 1, 1114117, 0, 0, 6, 0, 0, 65542, 0, 1, 131078, 0, 1, 196614, 0, 1, 262150, 0, 1, 327686, 0, 0, 393222, 0, 0, 458758, 131072, 1, 524294, 0, 0, 589830, 0, 1, 655366, 0, 0, 720902, 0, 1, 786438, 0, 1, 851974, 0, 0, 917510, 0, 1, 983046, 0, 0, 1048582, 0, 1, 1114118, 0, 1, 7, 0, 0, 65543, 0, 1, 131079, 0, 0, 196615, 0, 0, 262151, 0, 1, 327687, 0, 0, 393223, 0, 1, 458759, 0, 0, 524295, 0, 1, 589831, 0, 1, 655367, 0, 0, 720903, 0, 0, 786439, 196608, 0, 851975, 0, 0, 917511, 0, 0, 983047, 0, 1, 1048583, 0, 1, 1114119, 0, 0, 8, 0, 1, 65544, 0, 1, 131080, 0, 1, 196616, 0, 1, 262152, 0, 0, 327688, 0, 1, 393224, 0, 0, 458760, 0, 1, 524296, 0, 1, 589832, 131072, 1, 655368, 0, 1, 720904, 0, 1, 786440, 0, 1, 851976, 0, 1, 917512, 0, 0, 983048, 0, 1, 1048584, 0, 0, 1114120, 0, 0, 9, 0, 0, 65545, 65536, 0, 131081, 0, 0, 196617, 0, 0, 262153, 0, 0, 327689, 0, 0, 393225, 196608, 1, 458761, 0, 1, 524297, 0, 0, 589833, 0, 1, 655369, 0, 1, 720905, 0, 1, 786441, 0, 0, 851977, 0, 1, 917513, 0, 0, 983049, 0, 0, 1048585, 0, 0, 1114121, 0, 1, 10, 0, 1, 65546, 0, 1, 131082, 0, 1, 196618, 0, 0, 262154, 0, 1, 327690, 0, 1, 393226, 0, 0, 458762, 0, 1, 524298, 0, 0, 589834, 0, 1, 655370, 0, 1, 720906, 0, 0, 786442, 0, 0, 851978, 0, 0, 917514, 0, 0, 983050, 0, 0, 1048586, 0, 0, 1114122, 0, 1, 11, 0, 0, 65547, 0, 1, 131083, 0, 1, 196619, 65536, 0, 262155, 0, 1, 327691, 0, 1, 393227, 0, 1, 458763, 0, 1, 524299, 0, 0, 589835, 0, 0, 655371, 0, 1, 720907, 0, 1, 786443, 0, 0, 851979, 0, 1, 917515, 0, 0, 983051, 0, 1, 1048587, 0, 1, 1114123, 0, 1, 12, 0, 0, 65548, 0, 1, 131084, 0, 0, 196620, 0, 1, 262156, 0, 0, 327692, 0, 0, 393228, 0, 1, 458764, 0, 1, 524300, 0, 1, 589836, 0, 0, 655372, 0, 1, 720908, 0, 0, 786444, 0, 0, 851980, 0, 0, 917516, 0, 1, 983052, 0, 0, 1048588, 0, 1, 1114124, 0, 0, 13, 0, 1, 65549, 0, 1, 131085, 0, 0, 196621, 0, 1, 262157, 0, 0, 327693, 0, 0, 393229, 0, 1, 458765, 0, 1, 524301, 0, 0, 589837, 0, 0, 655373, 0, 0, 720909, 0, 1, 786445, 0, 1, 851981, 0, 1, 917517, 0, 1, 983053, 0, 0, 1048589, 0, 0, 1114125, 0, 1, 14, 0, 0, 65550, 0, 1, 131086, 0, 0, 196622, 0, 0, 262158, 0, 1, 327694, 0, 1, 393230, 0, 0, 458766, 0, 1, 524302, 0, 0, 589838, 262144, 0, 655374, 0, 0, 720910, 0, 0, 786446, 0, 1, 851982, 0, 0, 917518, 0, 1, 983054, 0, 0, 1048590, 0, 0, 1114126, 0, 0, 15, 0, 1, 65551, 0, 0, 131087, 0, 1, 196623, 0, 0, 262159, 0, 1, 327695, 0, 0, 393231, 0, 0, 458767, 0, 0, 524303, 0, 1, 589839, 0, 0, 655375, 0, 1, 720911, 0, 0, 786447, 0, 1, 851983, 131072, 1, 917519, 0, 0, 983055, 0, 0, 1048591, 0, 0, 1114127, 196608, 0, 16, 0, 0, 65552, 0, 0, 131088, 0, 1, 196624, 0, 1, 262160, 0, 1, 327696, 0, 1, 393232, 0, 1, 458768, 0, 1, 524304, 0, 0, 589840, 0, 0, 655376, 0, 0, 720912, 0, 1, 786448, 0, 0, 851984, 0, 0, 917520, 0, 1, 983056, 0, 0, 1048592, 0, 0, 1114128, 0, 1, 17, 0, 1, 65553, 0, 1, 131089, 0, 1, 196625, 0, 1, 262161, 0, 1, 327697, 0, 1, 393233, 0, 0, 458769, 0, 1, 524305, 0, 1, 589841, 0, 0, 655377, 0, 0, 720913, 0, 0, 786449, 0, 0, 851985, 0, 1, 917521, 0, 0, 983057, 0, 1, 1048593, 0, 0, 1114129, 0, 1, 18, 0, 0, 65554, 0, 0, 131090, 0, 0, 196626, 0, 0, 262162, 0, 1, 327698, 0, 0, 393234, 65536, 0, 458770, 0, 0, 524306, 0, 1, 589842, 0, 1, 655378, 0, 1, 720914, 0, 1, 786450, 0, 1, 851986, 0, 1, 917522, 0, 0, 983058, 0, 1, 1048594, 0, 0, 1114130, 0, 1, 19, 0, 1, 65555, 0, 0, 131091, 0, 0, 196627, 0, 1, 262163, 0, 0, 327699, 0, 0, 393235, 0, 0, 458771, 0, 1, 524307, 0, 0, 589843, 0, 1, 655379, 0, 0, 720915, 196608, 1, 786451, 0, 0, 851987, 0, 0, 917523, 0, 0, 983059, 0, 0, 1048595, 0, 0, 1114131, 0, 0, 20, 0, 1, 65556, 0, 0, 131092, 0, 1, 196628, 0, 1, 262164, 0, 1, 327700, 0, 0, 393236, 0, 1, 458772, 0, 0, 524308, 0, 0, 589844, 0, 0, 655380, 0, 0, 720916, 0, 1, 786452, 0, 0, 851988, 0, 1, 917524, 0, 0, 983060, 0, 1, 1048596, 0, 0, 1114132, 65536, 0, 21, 0, 1, 65557, 0, 1, 131093, 0, 0, 196629, 0, 1, 262165, 0, 1, 327701, 0, 0, 393237, 0, 0, 458773, 0, 0, 524309, 0, 0, 589845, 0, 0, 655381, 0, 0, 720917, 0, 1, 786453, 0, 0, 851989, 0, 0, 917525, 0, 1, 983061, 0, 1, 1048597, 0, 0, 1114133, 0, 0, 22, 0, 1, 65558, 0, 0, 131094, 0, 1, 196630, 0, 1, 262166, 0, 0, 327702, 0, 0, 393238, 0, 1, 458774, 0, 0, 524310, 0, 1, 589846, 0, 0, 655382, 0, 1, 720918, 0, 0, 786454, 0, 1, 917526, 262144, 1, 983062, 0, 0, 1048598, 0, 1, 1114134, 196608, 0, 23, 0, 0, 65559, 0, 1, 131095, 0, 1, 196631, 0, 1, 262167, 0, 1, 327703, 0, 1, 393239, 0, 0, 458775, 0, 1, 524311, 0, 0, 589847, 0, 1, 655383, 0, 1, 720919, 196608, 1, 786455, 0, 1, 851991, 0, 1, 917527, 0, 0, 983063, 65536, 1, 1048599, 0, 1, 1114135, 0, 1, 24, 0, 1, 65560, 0, 0, 131096, 0, 0, 196632, 0, 1, 262168, 0, 1, 327704, 0, 1, 393240, 0, 0, 458776, 0, 0, 524312, 0, 0, 589848, 65536, 0, 655384, 0, 1, 720920, 0, 1, 786456, 0, 1, 851992, 0, 1, 917528, 196608, 0, 983064, 0, 1, 1048600, 0, 1, 1114136, 0, 0, 25, 0, 0, 131097, 0, 0, 196633, 0, 1, 262169, 0, 1, 327705, 0, 0, 393241, 0, 0, 458777, 65536, 0, 524313, 0, 0, 589849, 0, 1, 655385, 0, 0, 720921, 0, 1, 786457, 0, 1, 851993, 0, 0, 917529, 0, 1, 983065, 0, 0, 1048601, 0, 0, 1114137, 0, 0, 26, 0, 0, 131098, 0, 0, 196634, 0, 1, 262170, 0, 1, 327706, 0, 1, 393242, 0, 1, 458778, 0, 0, 524314, 0, 0, 589850, 0, 1, 655386, 0, 1, 720922, 0, 0, 786458, 65536, 0, 851994, 0, 1, 917530, 0, 0, 983066, 0, 0, 1048602, 0, 0, 1114138, 0, 1, 27, 0, 0, 131099, 0, 0, 196635, 0, 1, 262171, 0, 0, 327707, 0, 0, 393243, 262144, 0, 458779, 0, 1, 524315, 0, 0, 589851, 0, 1, 655387, 0, 0, 720923, 0, 0, 786459, 0, 1, 851995, 0, 1, 917531, 0, 0, 983067, 0, 0, 1048603, 0, 0, 1114139, 0, 1, 131100, 0, 0, 196636, 0, 1, 262172, 0, 1, 327708, 0, 1, 393244, 0, 1, 458780, 0, 1, 524316, 0, 0, 589852, 0, 1, 655388, 0, 1, 720924, 0, 1, 786460, 0, 1, 851996, 0, 0, 917532, 0, 0, 983068, 0, 1, 1048604, 0, 0, 1114140, 0, 1, 131101, 0, 0, 196637, 0, 1, 262173, 0, 0, 327709, 0, 0, 393245, 0, 0, 458781, 0, 1, 524317, 0, 0, 589853, 0, 1, 655389, 0, 0, 720925, 196608, 1, 786461, 0, 0, 851997, 0, 1, 917533, 0, 0, 983069, 0, 1, 1048605, 0, 0, 1114141, 0, 0, 131102, 0, 0, 196638, 0, 0, 262174, 0, 0, 327710, 0, 0, 393246, 0, 0, 458782, 0, 1, 524318, 0, 1, 589854, 0, 1, 655390, 0, 1, 720926, 0, 0, 786462, 0, 0, 851998, 0, 0, 917534, 0, 1, 983070, 0, 1, 1048606, 0, 0, 1114142, 262144, 1, 65567, 0, 0, 196639, 0, 1, 262175, 0, 0, 327711, 0, 0, 393247, 0, 0, 458783, 0, 0, 524319, 0, 1, 589855, 0, 0, 655391, 0, 0, 720927, 0, 1, 786463, 0, 0, 851999, 0, 1, 917535, 0, 1, 983071, 0, 1, 1048607, 0, 0, 1114143, 0, 0, 851990, 0, 0, 1179648, 0, 0, 1245184, 0, 0, 1179649, 0, 0, 1245185, 0, 0, 1179650, 0, 0, 1245186, 0, 0, 1179651, 0, 0, 1245187, 0, 0, 1179652, 0, 0, 1245188, 0, 0, 1179653, 0, 0, 1245189, 0, 0, 1179654, 0, 0, 1245190, 0, 0, 1179655, 0, 0, 1245191, 0, 0, 1179656, 0, 0, 1245192, 0, 0, 1179657, 0, 0, 1245193, 0, 0, 1179658, 0, 0, 1245194, 0, 0, 1179659, 0, 0, 1245195, 0, 0, 1179660, 0, 0, 1245196, 0, 0, 1179661, 0, 0, 1245197, 0, 0, 1179662, 0, 0, 1245198, 0, 0, 1179663, 0, 0, 1245199, 0, 0, 1179664, 0, 0, 1245200, 0, 0, 1179665, 0, 0, 1245201, 0, 0, 1179666, 0, 0, 1245202, 0, 0, 1179667, 0, 0, 1245203, 0, 0, 1179668, 0, 0, 1245204, 0, 0, 1179669, 0, 0, 1245205, 0, 0, 1179670, 0, 0, 1245206, 0, 0, 1179671, 0, 0, 1245207, 0, 0, 1179672, 0, 0, 1245208, 0, 0, 1179673, 0, 0, 1245209, 0, 0, 1179674, 0, 0, 1245210, 0, 0, 1179675, 0, 0, 1245211, 0, 0, 1179676, 0, 0, 1245212, 0, 0, 1179677, 0, 0, 1245213, 0, 0, 1179678, 0, 0, 1245214, 0, 0, 1179679, 0, 0, 1245215, 0, 0, 65561, 0, 0, 65562, 0, 0, 65563, 0, 0, 28, 0, 0, 65564, 0, 0, 29, 0, 0, 65565, 0, 0, 30, 0, 0, 65566, 0, 0, 31, 0, 0, 131103, 0, 0) 75 | 76 | [connection signal="end_conversation" from="." to="." method="_on_end_conversation"] 77 | [connection signal="start_conversation" from="." to="." method="_on_start_conversation"] 78 | -------------------------------------------------------------------------------- /demo/godot/Player/player_skin_selector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=23 format=3 uid="uid://dhb820qr54ed4"] 2 | 3 | [ext_resource type="Script" path="res://Player/player_skin_selector.gd" id="1_6x6pt"] 4 | [ext_resource type="Texture2D" uid="uid://ceylmk402w7q2" path="res://art/wizard_white.png" id="2_udvb8"] 5 | 6 | [sub_resource type="Animation" id="Animation_ct72s"] 7 | length = 0.001 8 | tracks/0/type = "value" 9 | tracks/0/imported = false 10 | tracks/0/enabled = true 11 | tracks/0/path = NodePath("Sprite2D:frame_coords") 12 | tracks/0/interp = 1 13 | tracks/0/loop_wrap = true 14 | tracks/0/keys = { 15 | "times": PackedFloat32Array(0), 16 | "transitions": PackedFloat32Array(1), 17 | "update": 1, 18 | "values": [Vector2i(0, 0)] 19 | } 20 | 21 | [sub_resource type="Animation" id="Animation_ad3ja"] 22 | length = 0.8 23 | loop_mode = 1 24 | tracks/0/type = "value" 25 | tracks/0/imported = false 26 | tracks/0/enabled = true 27 | tracks/0/path = NodePath("Sprite2D:frame_coords") 28 | tracks/0/interp = 1 29 | tracks/0/loop_wrap = true 30 | tracks/0/keys = { 31 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 32 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 33 | "update": 1, 34 | "values": [Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), Vector2i(3, 0), Vector2i(4, 0), Vector2i(5, 0), Vector2i(6, 0), Vector2i(7, 0)] 35 | } 36 | 37 | [sub_resource type="Animation" id="Animation_gk5ix"] 38 | length = 0.8 39 | loop_mode = 1 40 | tracks/0/type = "value" 41 | tracks/0/imported = false 42 | tracks/0/enabled = true 43 | tracks/0/path = NodePath("Sprite2D:frame_coords") 44 | tracks/0/interp = 1 45 | tracks/0/loop_wrap = true 46 | tracks/0/keys = { 47 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 48 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 49 | "update": 1, 50 | "values": [Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1), Vector2i(3, 1), Vector2i(4, 1), Vector2i(5, 1), Vector2i(6, 1), Vector2i(7, 1)] 51 | } 52 | 53 | [sub_resource type="Animation" id="Animation_1e2yt"] 54 | length = 0.8 55 | loop_mode = 1 56 | tracks/0/type = "value" 57 | tracks/0/imported = false 58 | tracks/0/enabled = true 59 | tracks/0/path = NodePath("Sprite2D:frame_coords") 60 | tracks/0/interp = 1 61 | tracks/0/loop_wrap = true 62 | tracks/0/keys = { 63 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 64 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 65 | "update": 1, 66 | "values": [Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2), Vector2i(3, 2), Vector2i(4, 2), Vector2i(5, 2), Vector2i(6, 2), Vector2i(7, 2)] 67 | } 68 | 69 | [sub_resource type="Animation" id="Animation_gv7e8"] 70 | length = 0.8 71 | loop_mode = 1 72 | tracks/0/type = "value" 73 | tracks/0/imported = false 74 | tracks/0/enabled = true 75 | tracks/0/path = NodePath("Sprite2D:frame_coords") 76 | tracks/0/interp = 1 77 | tracks/0/loop_wrap = true 78 | tracks/0/keys = { 79 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 80 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 81 | "update": 1, 82 | "values": [Vector2i(0, 3), Vector2i(1, 3), Vector2i(2, 3), Vector2i(3, 3), Vector2i(4, 3), Vector2i(5, 3), Vector2i(6, 3), Vector2i(7, 3)] 83 | } 84 | 85 | [sub_resource type="Animation" id="Animation_3kgki"] 86 | length = 0.8 87 | loop_mode = 1 88 | tracks/0/type = "value" 89 | tracks/0/imported = false 90 | tracks/0/enabled = true 91 | tracks/0/path = NodePath("Sprite2D:frame_coords") 92 | tracks/0/interp = 1 93 | tracks/0/loop_wrap = true 94 | tracks/0/keys = { 95 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 96 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 97 | "update": 1, 98 | "values": [Vector2i(0, 4), Vector2i(1, 4), Vector2i(2, 4), Vector2i(3, 4), Vector2i(4, 4), Vector2i(5, 4), Vector2i(6, 4), Vector2i(7, 4)] 99 | } 100 | 101 | [sub_resource type="Animation" id="Animation_34fhh"] 102 | length = 0.8 103 | loop_mode = 1 104 | tracks/0/type = "value" 105 | tracks/0/imported = false 106 | tracks/0/enabled = true 107 | tracks/0/path = NodePath("Sprite2D:frame_coords") 108 | tracks/0/interp = 1 109 | tracks/0/loop_wrap = true 110 | tracks/0/keys = { 111 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 112 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 113 | "update": 1, 114 | "values": [Vector2i(0, 5), Vector2i(1, 5), Vector2i(2, 5), Vector2i(3, 5), Vector2i(4, 5), Vector2i(5, 5), Vector2i(6, 5), Vector2i(7, 5)] 115 | } 116 | 117 | [sub_resource type="Animation" id="Animation_eqjj5"] 118 | length = 0.8 119 | loop_mode = 1 120 | tracks/0/type = "value" 121 | tracks/0/imported = false 122 | tracks/0/enabled = true 123 | tracks/0/path = NodePath("Sprite2D:frame_coords") 124 | tracks/0/interp = 1 125 | tracks/0/loop_wrap = true 126 | tracks/0/keys = { 127 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 128 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 129 | "update": 1, 130 | "values": [Vector2i(0, 6), Vector2i(1, 6), Vector2i(2, 6), Vector2i(3, 6), Vector2i(4, 6), Vector2i(5, 6), Vector2i(6, 6), Vector2i(7, 6)] 131 | } 132 | 133 | [sub_resource type="Animation" id="Animation_gashp"] 134 | length = 0.8 135 | loop_mode = 1 136 | tracks/0/type = "value" 137 | tracks/0/imported = false 138 | tracks/0/enabled = true 139 | tracks/0/path = NodePath("Sprite2D:frame_coords") 140 | tracks/0/interp = 1 141 | tracks/0/loop_wrap = true 142 | tracks/0/keys = { 143 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 144 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 145 | "update": 1, 146 | "values": [Vector2i(0, 7), Vector2i(1, 7), Vector2i(2, 7), Vector2i(3, 7), Vector2i(4, 7), Vector2i(5, 7), Vector2i(6, 7), Vector2i(7, 7)] 147 | } 148 | 149 | [sub_resource type="Animation" id="Animation_mxijs"] 150 | length = 0.8 151 | loop_mode = 1 152 | tracks/0/type = "value" 153 | tracks/0/imported = false 154 | tracks/0/enabled = true 155 | tracks/0/path = NodePath("Sprite2D:frame_coords") 156 | tracks/0/interp = 1 157 | tracks/0/loop_wrap = true 158 | tracks/0/keys = { 159 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 160 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 161 | "update": 1, 162 | "values": [Vector2i(0, 8), Vector2i(1, 8), Vector2i(2, 8), Vector2i(3, 8), Vector2i(4, 8), Vector2i(5, 8), Vector2i(6, 8), Vector2i(7, 8)] 163 | } 164 | 165 | [sub_resource type="Animation" id="Animation_tyrsl"] 166 | length = 0.8 167 | loop_mode = 1 168 | tracks/0/type = "value" 169 | tracks/0/imported = false 170 | tracks/0/enabled = true 171 | tracks/0/path = NodePath("Sprite2D:frame_coords") 172 | tracks/0/interp = 1 173 | tracks/0/loop_wrap = true 174 | tracks/0/keys = { 175 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 176 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 177 | "update": 1, 178 | "values": [Vector2i(0, 9), Vector2i(1, 9), Vector2i(2, 9), Vector2i(3, 9), Vector2i(4, 9), Vector2i(5, 9), Vector2i(6, 9), Vector2i(7, 9)] 179 | } 180 | 181 | [sub_resource type="Animation" id="Animation_nsmt0"] 182 | length = 0.8 183 | loop_mode = 1 184 | tracks/0/type = "value" 185 | tracks/0/imported = false 186 | tracks/0/enabled = true 187 | tracks/0/path = NodePath("Sprite2D:frame_coords") 188 | tracks/0/interp = 1 189 | tracks/0/loop_wrap = true 190 | tracks/0/keys = { 191 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 192 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 193 | "update": 1, 194 | "values": [Vector2i(0, 10), Vector2i(1, 10), Vector2i(2, 10), Vector2i(3, 10), Vector2i(4, 10), Vector2i(5, 10), Vector2i(6, 10), Vector2i(7, 10)] 195 | } 196 | 197 | [sub_resource type="Animation" id="Animation_wi5sn"] 198 | length = 0.8 199 | loop_mode = 1 200 | tracks/0/type = "value" 201 | tracks/0/imported = false 202 | tracks/0/enabled = true 203 | tracks/0/path = NodePath("Sprite2D:frame_coords") 204 | tracks/0/interp = 1 205 | tracks/0/loop_wrap = true 206 | tracks/0/keys = { 207 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 208 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 209 | "update": 1, 210 | "values": [Vector2i(0, 11), Vector2i(1, 11), Vector2i(2, 11), Vector2i(3, 11), Vector2i(4, 11), Vector2i(5, 11), Vector2i(6, 11), Vector2i(7, 11)] 211 | } 212 | 213 | [sub_resource type="Animation" id="Animation_j60sb"] 214 | length = 0.8 215 | loop_mode = 1 216 | tracks/0/type = "value" 217 | tracks/0/imported = false 218 | tracks/0/enabled = true 219 | tracks/0/path = NodePath("Sprite2D:frame_coords") 220 | tracks/0/interp = 1 221 | tracks/0/loop_wrap = true 222 | tracks/0/keys = { 223 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 224 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 225 | "update": 1, 226 | "values": [Vector2i(0, 12), Vector2i(1, 12), Vector2i(2, 12), Vector2i(3, 12), Vector2i(4, 12), Vector2i(5, 12), Vector2i(6, 12), Vector2i(7, 12)] 227 | } 228 | 229 | [sub_resource type="Animation" id="Animation_6atj6"] 230 | length = 0.8 231 | loop_mode = 1 232 | tracks/0/type = "value" 233 | tracks/0/imported = false 234 | tracks/0/enabled = true 235 | tracks/0/path = NodePath("Sprite2D:frame_coords") 236 | tracks/0/interp = 1 237 | tracks/0/loop_wrap = true 238 | tracks/0/keys = { 239 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 240 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 241 | "update": 1, 242 | "values": [Vector2i(0, 13), Vector2i(1, 13), Vector2i(2, 13), Vector2i(3, 13), Vector2i(4, 13), Vector2i(5, 13), Vector2i(6, 13), Vector2i(7, 13)] 243 | } 244 | 245 | [sub_resource type="Animation" id="Animation_3iwsl"] 246 | length = 0.8 247 | loop_mode = 1 248 | tracks/0/type = "value" 249 | tracks/0/imported = false 250 | tracks/0/enabled = true 251 | tracks/0/path = NodePath("Sprite2D:frame_coords") 252 | tracks/0/interp = 1 253 | tracks/0/loop_wrap = true 254 | tracks/0/keys = { 255 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 256 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 257 | "update": 1, 258 | "values": [Vector2i(0, 14), Vector2i(1, 14), Vector2i(2, 14), Vector2i(3, 14), Vector2i(4, 14), Vector2i(5, 14), Vector2i(6, 14), Vector2i(7, 14)] 259 | } 260 | 261 | [sub_resource type="Animation" id="Animation_3meml"] 262 | length = 0.8 263 | loop_mode = 1 264 | tracks/0/type = "value" 265 | tracks/0/imported = false 266 | tracks/0/enabled = true 267 | tracks/0/path = NodePath("Sprite2D:frame_coords") 268 | tracks/0/interp = 1 269 | tracks/0/loop_wrap = true 270 | tracks/0/keys = { 271 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 272 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 273 | "update": 1, 274 | "values": [Vector2i(0, 15), Vector2i(1, 15), Vector2i(2, 15), Vector2i(3, 15), Vector2i(4, 15), Vector2i(5, 15), Vector2i(6, 15), Vector2i(7, 15)] 275 | } 276 | 277 | [sub_resource type="Animation" id="Animation_p41a3"] 278 | length = 0.8 279 | loop_mode = 1 280 | tracks/0/type = "value" 281 | tracks/0/imported = false 282 | tracks/0/enabled = true 283 | tracks/0/path = NodePath("Sprite2D:frame_coords") 284 | tracks/0/interp = 1 285 | tracks/0/loop_wrap = true 286 | tracks/0/keys = { 287 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 288 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 289 | "update": 1, 290 | "values": [Vector2i(0, 16), Vector2i(1, 16), Vector2i(2, 16), Vector2i(3, 16), Vector2i(4, 16), Vector2i(5, 16), Vector2i(6, 16), Vector2i(7, 16)] 291 | } 292 | 293 | [sub_resource type="Animation" id="Animation_20wo2"] 294 | length = 0.8 295 | loop_mode = 1 296 | tracks/0/type = "value" 297 | tracks/0/imported = false 298 | tracks/0/enabled = true 299 | tracks/0/path = NodePath("Sprite2D:frame_coords") 300 | tracks/0/interp = 1 301 | tracks/0/loop_wrap = true 302 | tracks/0/keys = { 303 | "times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), 304 | "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1), 305 | "update": 1, 306 | "values": [Vector2i(0, 17), Vector2i(1, 17), Vector2i(2, 17), Vector2i(3, 17), Vector2i(4, 17), Vector2i(5, 17), Vector2i(6, 17), Vector2i(7, 17)] 307 | } 308 | 309 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_mq4ni"] 310 | _data = { 311 | "RESET": SubResource("Animation_ct72s"), 312 | "standing_attacking_down": SubResource("Animation_ad3ja"), 313 | "standing_attacking_side": SubResource("Animation_gk5ix"), 314 | "standing_attacking_up": SubResource("Animation_1e2yt"), 315 | "standing_idle_down": SubResource("Animation_gv7e8"), 316 | "standing_idle_side": SubResource("Animation_3kgki"), 317 | "standing_idle_up": SubResource("Animation_34fhh"), 318 | "standing_raising_down": SubResource("Animation_eqjj5"), 319 | "standing_raising_side": SubResource("Animation_gashp"), 320 | "standing_raising_up": SubResource("Animation_mxijs"), 321 | "walking_attacking_down": SubResource("Animation_tyrsl"), 322 | "walking_attacking_side": SubResource("Animation_nsmt0"), 323 | "walking_attacking_up": SubResource("Animation_wi5sn"), 324 | "walking_idle_down": SubResource("Animation_j60sb"), 325 | "walking_idle_side": SubResource("Animation_6atj6"), 326 | "walking_idle_up": SubResource("Animation_3iwsl"), 327 | "walking_raising_down": SubResource("Animation_3meml"), 328 | "walking_raising_side": SubResource("Animation_p41a3"), 329 | "walking_raising_up": SubResource("Animation_20wo2") 330 | } 331 | 332 | [node name="PlayerSkinSelector" type="Node2D"] 333 | script = ExtResource("1_6x6pt") 334 | 335 | [node name="Sprite2D" type="Sprite2D" parent="."] 336 | texture = ExtResource("2_udvb8") 337 | hframes = 8 338 | vframes = 18 339 | 340 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 341 | libraries = { 342 | "": SubResource("AnimationLibrary_mq4ni") 343 | } 344 | 345 | [node name="FlashTimer" type="Timer" parent="."] 346 | wait_time = 0.1 347 | 348 | [connection signal="timeout" from="FlashTimer" to="." method="_on_flash_timer_timeout"] 349 | --------------------------------------------------------------------------------