├── 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 |
--------------------------------------------------------------------------------