handles enemy respawning properly
This commit is contained in:
parent
1e4be7ba8d
commit
3cf084e69b
|
@ -0,0 +1,7 @@
|
||||||
|
# Hyper Space
|
||||||
|
|
||||||
|
Space Invaders like space shooter
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* Godot 4.2.2
|
|
@ -0,0 +1,368 @@
|
||||||
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var llms = $LLMs
|
||||||
|
@onready var context_label = $VBoxParent/Context
|
||||||
|
@onready var status_label = $VBoxParent/Status
|
||||||
|
@onready var model_select = $VBoxParent/ModelSetting/Model
|
||||||
|
@onready var shortcut_modifier_select = $VBoxParent/ShortcutSetting/HBoxContainer/Modifier
|
||||||
|
@onready var shortcut_key_select = $VBoxParent/ShortcutSetting/HBoxContainer/Key
|
||||||
|
@onready var multiline_toggle = $VBoxParent/MultilineSetting/Multiline
|
||||||
|
@onready var openai_key_title = $VBoxParent/OpenAiSetting/Label
|
||||||
|
@onready var openai_key_input = $VBoxParent/OpenAiSetting/OpenAiKey
|
||||||
|
@onready var version_label = $Version
|
||||||
|
@onready var info = $VBoxParent/Info
|
||||||
|
|
||||||
|
@export var icon_shader : ShaderMaterial
|
||||||
|
@export var highlight_color : Color
|
||||||
|
|
||||||
|
var editor_interface : EditorInterface
|
||||||
|
var screen = "Script"
|
||||||
|
|
||||||
|
var request_code_state = null
|
||||||
|
var cur_highlight = null
|
||||||
|
var indicator = null
|
||||||
|
|
||||||
|
var models = {}
|
||||||
|
var openai_api_key
|
||||||
|
var cur_model
|
||||||
|
var cur_shortcut_modifier = "Control" if is_mac() else "Alt"
|
||||||
|
var cur_shortcut_key = "C"
|
||||||
|
var allow_multiline = true
|
||||||
|
|
||||||
|
const PREFERENCES_STORAGE_NAME = "user://copilot.cfg"
|
||||||
|
const PREFERENCES_PASS = "F4fv2Jxpasp20VS5VSp2Yp2v9aNVJ21aRK"
|
||||||
|
const GITHUB_COPILOT_DISCLAIMER = "Use GitHub Copilot keys at your own risk. Retrieve key by following instructions [url=https://gitlab.com/aaamoon/copilot-gpt4-service?tab=readme-ov-file#obtaining-copilot-token]here[/url]."
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
#Initialize dock, load settings
|
||||||
|
populate_models()
|
||||||
|
populate_modifiers()
|
||||||
|
load_config()
|
||||||
|
|
||||||
|
func populate_models():
|
||||||
|
#Add all found models to settings
|
||||||
|
model_select.clear()
|
||||||
|
for llm in llms.get_children():
|
||||||
|
var new_models = llm._get_models()
|
||||||
|
for model in new_models:
|
||||||
|
model_select.add_item(model)
|
||||||
|
models[model] = get_path_to(llm)
|
||||||
|
model_select.select(0)
|
||||||
|
set_model(model_select.get_item_text(0))
|
||||||
|
|
||||||
|
func populate_modifiers():
|
||||||
|
#Add available shortcut modifiers based on platform
|
||||||
|
shortcut_modifier_select.clear()
|
||||||
|
var modifiers = ["Alt", "Ctrl", "Shift"]
|
||||||
|
if is_mac(): modifiers = ["Cmd", "Option", "Control", "Shift"]
|
||||||
|
for modifier in modifiers:
|
||||||
|
shortcut_modifier_select.add_item(modifier)
|
||||||
|
apply_by_value(shortcut_modifier_select, cur_shortcut_modifier)
|
||||||
|
|
||||||
|
func _unhandled_key_input(event):
|
||||||
|
#Handle input
|
||||||
|
if event is InputEventKey:
|
||||||
|
if cur_highlight:
|
||||||
|
#If completion is shown, TAB will accept it
|
||||||
|
#and the TAB input ignored
|
||||||
|
if event.keycode == KEY_TAB:
|
||||||
|
undo_input()
|
||||||
|
clear_highlights()
|
||||||
|
#BACKSPACE will remove it
|
||||||
|
elif event.keycode == KEY_BACKSPACE:
|
||||||
|
revert_change()
|
||||||
|
clear_highlights()
|
||||||
|
#Any other key press will plainly accept it
|
||||||
|
else:
|
||||||
|
clear_highlights()
|
||||||
|
#If shortcut modifier and key are pressed, request completion
|
||||||
|
if shortcut_key_pressed(event) and shortcut_modifier_pressed(event):
|
||||||
|
request_completion()
|
||||||
|
|
||||||
|
func is_mac():
|
||||||
|
#Platform check
|
||||||
|
return OS.get_name() == "macOS"
|
||||||
|
|
||||||
|
func shortcut_key_pressed(event):
|
||||||
|
#Check if selected shortcut key is pressed
|
||||||
|
var key_string = OS.get_keycode_string(event.keycode)
|
||||||
|
return key_string == cur_shortcut_key
|
||||||
|
|
||||||
|
func shortcut_modifier_pressed(event):
|
||||||
|
#Check if selected shortcut modifier is pressed
|
||||||
|
match cur_shortcut_modifier:
|
||||||
|
"Control":
|
||||||
|
return event.ctrl_pressed
|
||||||
|
"Ctrl":
|
||||||
|
return event.ctrl_pressed
|
||||||
|
"Alt":
|
||||||
|
return event.alt_pressed
|
||||||
|
"Option":
|
||||||
|
return event.alt_pressed
|
||||||
|
"Shift":
|
||||||
|
return event.shift_pressed
|
||||||
|
"Cmd":
|
||||||
|
return event.meta_pressed
|
||||||
|
_:
|
||||||
|
return false
|
||||||
|
|
||||||
|
func clear_highlights():
|
||||||
|
#Clear all currently highlighted lines
|
||||||
|
#and reset request status
|
||||||
|
request_code_state = null
|
||||||
|
cur_highlight = null
|
||||||
|
var editor = get_code_editor()
|
||||||
|
for line in range(editor.get_line_count()):
|
||||||
|
editor.set_line_background_color(line, Color(0, 0, 0, 0))
|
||||||
|
|
||||||
|
func undo_input():
|
||||||
|
#Undo last input in code editor
|
||||||
|
var editor = get_code_editor()
|
||||||
|
editor.undo()
|
||||||
|
|
||||||
|
func update_loading_indicator(create = false):
|
||||||
|
#Make sure loading indicator is placed at caret position
|
||||||
|
if screen != "Script": return
|
||||||
|
var editor = get_code_editor()
|
||||||
|
if !editor: return
|
||||||
|
var line_height = editor.get_line_height()
|
||||||
|
if !is_instance_valid(indicator):
|
||||||
|
if !create: return
|
||||||
|
indicator = ColorRect.new()
|
||||||
|
indicator.material = icon_shader
|
||||||
|
indicator.custom_minimum_size = Vector2(line_height, line_height)
|
||||||
|
editor.add_child(indicator)
|
||||||
|
var pos = editor.get_caret_draw_pos()
|
||||||
|
var pre_post = get_pre_post()
|
||||||
|
#Caret position returned from Godot is not reliable
|
||||||
|
#Needs to be adjusted for empty lines
|
||||||
|
var is_on_empty_line = pre_post[0].right(1) == "\n"
|
||||||
|
var offset = line_height/2-1 if is_on_empty_line else line_height-1
|
||||||
|
indicator.position = Vector2(pos.x, pos.y - offset)
|
||||||
|
editor.editable = false
|
||||||
|
|
||||||
|
func remove_loading_indicator():
|
||||||
|
#Free loading indicator, and return editor to editable state
|
||||||
|
if is_instance_valid(indicator): indicator.queue_free()
|
||||||
|
set_status("")
|
||||||
|
var editor = get_code_editor()
|
||||||
|
editor.editable = true
|
||||||
|
|
||||||
|
func set_status(text):
|
||||||
|
#Update status label in dock
|
||||||
|
status_label.text = ""
|
||||||
|
|
||||||
|
func insert_completion(content: String, pre, post):
|
||||||
|
#Overwrite code editor text to insert received completion
|
||||||
|
var editor = get_code_editor()
|
||||||
|
var scroll = editor.scroll_vertical
|
||||||
|
|
||||||
|
var caret_text = pre + content
|
||||||
|
var lines_from = pre.split("\n")
|
||||||
|
var lines_to = caret_text.split("\n")
|
||||||
|
|
||||||
|
cur_highlight = [lines_from.size(), lines_to.size()]
|
||||||
|
|
||||||
|
editor.set_text(pre + content + post)
|
||||||
|
editor.set_caret_line(lines_to.size())
|
||||||
|
editor.set_caret_column(lines_to[-1].length())
|
||||||
|
editor.scroll_vertical = scroll
|
||||||
|
editor.update_code_completion_options(false)
|
||||||
|
|
||||||
|
func revert_change():
|
||||||
|
#Revert inserted completion
|
||||||
|
var code_edit = get_code_editor()
|
||||||
|
var scroll = code_edit.scroll_vertical
|
||||||
|
var old_text = request_code_state[0] + request_code_state[1]
|
||||||
|
var lines_from = request_code_state[0].strip_edges(false, true).split("\n")
|
||||||
|
code_edit.set_text(old_text)
|
||||||
|
code_edit.set_caret_line(lines_from.size()-1)
|
||||||
|
code_edit.set_caret_column(lines_from[-1].length())
|
||||||
|
code_edit.scroll_vertical = scroll
|
||||||
|
clear_highlights()
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
#Update visuals and context label
|
||||||
|
update_highlights()
|
||||||
|
update_loading_indicator()
|
||||||
|
update_context()
|
||||||
|
|
||||||
|
func update_highlights():
|
||||||
|
#Make sure highlighted lines persist until explicitely removed
|
||||||
|
#via key input
|
||||||
|
if cur_highlight:
|
||||||
|
var editor = get_code_editor()
|
||||||
|
for line in range(cur_highlight[0]-1, cur_highlight[1]):
|
||||||
|
editor.set_line_background_color(line, highlight_color)
|
||||||
|
|
||||||
|
func update_context():
|
||||||
|
#Show currently edited file in dock
|
||||||
|
var script = get_current_script()
|
||||||
|
if script: context_label.text = script.resource_path.get_file()
|
||||||
|
|
||||||
|
func on_main_screen_changed(_screen):
|
||||||
|
#Track current editor screen (2D, 3D, Script)
|
||||||
|
screen = _screen
|
||||||
|
|
||||||
|
func get_current_script():
|
||||||
|
#Get currently edited script
|
||||||
|
if !editor_interface: return
|
||||||
|
var script_editor = editor_interface.get_script_editor()
|
||||||
|
return script_editor.get_current_script()
|
||||||
|
|
||||||
|
func get_code_editor():
|
||||||
|
#Get currently used code editor
|
||||||
|
#This does not return the shader editor!
|
||||||
|
if !editor_interface: return
|
||||||
|
var script_editor = editor_interface.get_script_editor()
|
||||||
|
var base_editor = script_editor.get_current_editor()
|
||||||
|
if base_editor:
|
||||||
|
var code_edit = base_editor.get_base_editor()
|
||||||
|
return code_edit
|
||||||
|
return null
|
||||||
|
|
||||||
|
func request_completion():
|
||||||
|
#Get current code and request completion from active model
|
||||||
|
if request_code_state: return
|
||||||
|
set_status("Asking %s..." % cur_model)
|
||||||
|
update_loading_indicator(true)
|
||||||
|
var pre_post = get_pre_post()
|
||||||
|
var llm = get_llm()
|
||||||
|
if !llm: return
|
||||||
|
llm._send_user_prompt(pre_post[0], pre_post[1])
|
||||||
|
request_code_state = pre_post
|
||||||
|
|
||||||
|
func get_pre_post():
|
||||||
|
#Split current code based on caret position
|
||||||
|
var editor = get_code_editor()
|
||||||
|
var text = editor.get_text()
|
||||||
|
var pos = Vector2(editor.get_caret_line(), editor.get_caret_column())
|
||||||
|
var pre = ""
|
||||||
|
var post = ""
|
||||||
|
for i in range(pos.x):
|
||||||
|
pre += editor.get_line(i) + "\n"
|
||||||
|
pre += editor.get_line(pos.x).substr(0,pos.y)
|
||||||
|
post += editor.get_line(pos.x).substr(pos.y) + "\n"
|
||||||
|
for ii in range(pos.x+1, editor.get_line_count()):
|
||||||
|
post += editor.get_line(ii) + "\n"
|
||||||
|
return [pre, post]
|
||||||
|
|
||||||
|
func get_llm():
|
||||||
|
#Get currently active llm and set active model
|
||||||
|
var llm = get_node(models[cur_model])
|
||||||
|
llm._set_api_key(openai_api_key)
|
||||||
|
llm._set_model(cur_model)
|
||||||
|
llm._set_multiline(allow_multiline)
|
||||||
|
return llm
|
||||||
|
|
||||||
|
func matches_request_state(pre, post):
|
||||||
|
#Check if code passed for completion request matches current code
|
||||||
|
return request_code_state[0] == pre and request_code_state[1] == post
|
||||||
|
|
||||||
|
func set_openai_api_key(key):
|
||||||
|
#Apply API key
|
||||||
|
openai_api_key = key
|
||||||
|
|
||||||
|
func set_model(model_name):
|
||||||
|
#Apply selected model
|
||||||
|
cur_model = model_name
|
||||||
|
# Handle some special model scenarios
|
||||||
|
if "github-copilot" in model_name:
|
||||||
|
openai_key_title.text = "GitHub Copilot API Key"
|
||||||
|
info.parse_bbcode(GITHUB_COPILOT_DISCLAIMER)
|
||||||
|
info.show()
|
||||||
|
else:
|
||||||
|
openai_key_title.text = "OpenAI API Key"
|
||||||
|
info.hide()
|
||||||
|
|
||||||
|
func set_shortcut_modifier(modifier):
|
||||||
|
#Apply selected shortcut modifier
|
||||||
|
cur_shortcut_modifier = modifier
|
||||||
|
|
||||||
|
func set_shortcut_key(key):
|
||||||
|
#Apply selected shortcut key
|
||||||
|
cur_shortcut_key = key
|
||||||
|
|
||||||
|
func set_multiline(active):
|
||||||
|
#Apply selected multiline setting
|
||||||
|
allow_multiline = active
|
||||||
|
|
||||||
|
func _on_code_completion_received(completion, pre, post):
|
||||||
|
#Attempt to insert received code completion
|
||||||
|
remove_loading_indicator()
|
||||||
|
if matches_request_state(pre, post):
|
||||||
|
insert_completion(completion, pre, post)
|
||||||
|
else:
|
||||||
|
clear_highlights()
|
||||||
|
|
||||||
|
func _on_code_completion_error(error):
|
||||||
|
#Display error
|
||||||
|
remove_loading_indicator()
|
||||||
|
clear_highlights()
|
||||||
|
push_error(error)
|
||||||
|
|
||||||
|
func _on_open_ai_key_changed(key):
|
||||||
|
#Apply setting and store in config file
|
||||||
|
set_openai_api_key(key)
|
||||||
|
store_config()
|
||||||
|
|
||||||
|
func _on_model_selected(index):
|
||||||
|
#Apply setting and store in config file
|
||||||
|
set_model(model_select.get_item_text(index))
|
||||||
|
store_config()
|
||||||
|
|
||||||
|
func _on_shortcut_modifier_selected(index):
|
||||||
|
#Apply setting and store in config file
|
||||||
|
set_shortcut_modifier(shortcut_modifier_select.get_item_text(index))
|
||||||
|
store_config()
|
||||||
|
|
||||||
|
func _on_shortcut_key_selected(index):
|
||||||
|
#Apply setting and store in config file
|
||||||
|
set_shortcut_key(shortcut_key_select.get_item_text(index))
|
||||||
|
store_config()
|
||||||
|
|
||||||
|
func _on_multiline_toggled(button_pressed):
|
||||||
|
#Apply setting and store in config file
|
||||||
|
set_multiline(button_pressed)
|
||||||
|
store_config()
|
||||||
|
|
||||||
|
func store_config():
|
||||||
|
#Store current setting in config file
|
||||||
|
var config = ConfigFile.new()
|
||||||
|
config.set_value("preferences", "model", cur_model)
|
||||||
|
config.set_value("preferences", "shortcut_modifier", cur_shortcut_modifier)
|
||||||
|
config.set_value("preferences", "shortcut_key", cur_shortcut_key)
|
||||||
|
config.set_value("preferences", "allow_multiline", allow_multiline)
|
||||||
|
config.set_value("keys", "openai", openai_api_key)
|
||||||
|
config.save_encrypted_pass(PREFERENCES_STORAGE_NAME, PREFERENCES_PASS)
|
||||||
|
|
||||||
|
func load_config():
|
||||||
|
#Retrieve current settings from config file
|
||||||
|
var config = ConfigFile.new()
|
||||||
|
var err = config.load_encrypted_pass(PREFERENCES_STORAGE_NAME, PREFERENCES_PASS)
|
||||||
|
if err != OK: return
|
||||||
|
cur_model = config.get_value("preferences", "model", cur_model)
|
||||||
|
apply_by_value(model_select, cur_model)
|
||||||
|
set_model(model_select.get_item_text(model_select.selected))
|
||||||
|
cur_shortcut_modifier = config.get_value("preferences", "shortcut_modifier", cur_shortcut_modifier)
|
||||||
|
apply_by_value(shortcut_modifier_select, cur_shortcut_modifier)
|
||||||
|
cur_shortcut_key = config.get_value("preferences", "shortcut_key", cur_shortcut_key)
|
||||||
|
apply_by_value(shortcut_key_select, cur_shortcut_key)
|
||||||
|
allow_multiline = config.get_value("preferences", "allow_multiline", allow_multiline)
|
||||||
|
multiline_toggle.set_pressed_no_signal(allow_multiline)
|
||||||
|
openai_api_key = config.get_value("keys", "openai", "")
|
||||||
|
openai_key_input.text = openai_api_key
|
||||||
|
|
||||||
|
func apply_by_value(option_button, value):
|
||||||
|
#Select item for option button based on value instead of index
|
||||||
|
for i in option_button.item_count:
|
||||||
|
if option_button.get_item_text(i) == value:
|
||||||
|
option_button.select(i)
|
||||||
|
|
||||||
|
func set_version(version):
|
||||||
|
version_label.text = "v%s" % version
|
||||||
|
|
||||||
|
|
||||||
|
func on_info_meta_clicked(meta):
|
||||||
|
OS.shell_open(meta)
|
|
@ -0,0 +1,293 @@
|
||||||
|
[gd_scene load_steps=7 format=3 uid="uid://rv5dl08lcb8e"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/copilot/Copilot.gd" id="1_pq1gj"]
|
||||||
|
[ext_resource type="Script" path="res://addons/copilot/OpenAIChat.gd" id="2"]
|
||||||
|
[ext_resource type="Material" uid="uid://cccmbprav6vgu" path="res://addons/copilot/small_icon.tres" id="2_gdw4j"]
|
||||||
|
[ext_resource type="Script" path="res://addons/copilot/OpenAICompletion.gd" id="3_loa2x"]
|
||||||
|
[ext_resource type="Material" uid="uid://bl1rtf743e4l3" path="res://addons/copilot/large_icon.tres" id="3_xn70b"]
|
||||||
|
[ext_resource type="Script" path="res://addons/copilot/GithubCopilot.gd" id="6_hmh8w"]
|
||||||
|
|
||||||
|
[node name="Copilot" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_pq1gj")
|
||||||
|
icon_shader = ExtResource("2_gdw4j")
|
||||||
|
highlight_color = Color(0.223529, 0.254902, 0.298039, 1)
|
||||||
|
|
||||||
|
[node name="VBoxParent" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
|
||||||
|
[node name="Indicator" type="ColorRect" parent="VBoxParent"]
|
||||||
|
material = ExtResource("3_xn70b")
|
||||||
|
custom_minimum_size = Vector2(200, 200)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
|
[node name="ContextTitle" type="Label" parent="VBoxParent"]
|
||||||
|
modulate = Color(1, 1, 1, 0.7)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Current Context"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="Context" type="Label" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="Status" type="Label" parent="VBoxParent"]
|
||||||
|
modulate = Color(1, 1, 1, 0.7)
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 100)
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="HowToTitle" type="Label" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "How To Use"
|
||||||
|
|
||||||
|
[node name="Separator1" type="HSeparator" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="HowTo" type="Label" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Press the selected shortcut in the code editor to request a completion from Copilot at the current caret position"
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="SettingTitle" type="Label" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Settings"
|
||||||
|
|
||||||
|
[node name="Separator2" type="HSeparator" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="OpenAiSetting" type="HBoxContainer" parent="VBoxParent"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxParent/OpenAiSetting"]
|
||||||
|
custom_minimum_size = Vector2(100, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 1
|
||||||
|
text = "OpenAI API Key"
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="VBoxParent/OpenAiSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="OpenAiKey" type="LineEdit" parent="VBoxParent/OpenAiSetting"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
placeholder_text = "API Key"
|
||||||
|
secret = true
|
||||||
|
|
||||||
|
[node name="ModelSetting" type="HBoxContainer" parent="VBoxParent"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxParent/ModelSetting"]
|
||||||
|
custom_minimum_size = Vector2(100, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 1
|
||||||
|
text = "Model"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="VBoxParent/ModelSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Model" type="OptionButton" parent="VBoxParent/ModelSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
item_count = 3
|
||||||
|
selected = 1
|
||||||
|
fit_to_longest_item = false
|
||||||
|
popup/item_0/text = "text-davinci-003"
|
||||||
|
popup/item_0/id = 0
|
||||||
|
popup/item_1/text = "gpt-3.5-turbo"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
popup/item_2/text = "gpt-4"
|
||||||
|
popup/item_2/id = 2
|
||||||
|
|
||||||
|
[node name="ShortcutSetting" type="HBoxContainer" parent="VBoxParent"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxParent/ShortcutSetting"]
|
||||||
|
custom_minimum_size = Vector2(100, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 1
|
||||||
|
text = "Shortcut"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="VBoxParent/ShortcutSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxParent/ShortcutSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
|
||||||
|
[node name="Modifier" type="OptionButton" parent="VBoxParent/ShortcutSetting/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
item_count = 4
|
||||||
|
selected = 2
|
||||||
|
popup/item_0/text = "Cmd"
|
||||||
|
popup/item_0/id = 0
|
||||||
|
popup/item_1/text = "Option"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
popup/item_2/text = "Control"
|
||||||
|
popup/item_2/id = 2
|
||||||
|
popup/item_3/text = "Shift"
|
||||||
|
popup/item_3/id = 3
|
||||||
|
|
||||||
|
[node name="Key" type="OptionButton" parent="VBoxParent/ShortcutSetting/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
item_count = 32
|
||||||
|
selected = 2
|
||||||
|
popup/item_0/text = "A"
|
||||||
|
popup/item_0/id = 0
|
||||||
|
popup/item_1/text = "B"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
popup/item_2/text = "C"
|
||||||
|
popup/item_2/id = 2
|
||||||
|
popup/item_3/text = "D"
|
||||||
|
popup/item_3/id = 3
|
||||||
|
popup/item_4/text = "E"
|
||||||
|
popup/item_4/id = 4
|
||||||
|
popup/item_5/text = "F"
|
||||||
|
popup/item_5/id = 5
|
||||||
|
popup/item_6/text = "G"
|
||||||
|
popup/item_6/id = 6
|
||||||
|
popup/item_7/text = "H"
|
||||||
|
popup/item_7/id = 7
|
||||||
|
popup/item_8/text = "L"
|
||||||
|
popup/item_8/id = 8
|
||||||
|
popup/item_9/text = "M"
|
||||||
|
popup/item_9/id = 9
|
||||||
|
popup/item_10/text = "N"
|
||||||
|
popup/item_10/id = 10
|
||||||
|
popup/item_11/text = "O"
|
||||||
|
popup/item_11/id = 11
|
||||||
|
popup/item_12/text = "P"
|
||||||
|
popup/item_12/id = 12
|
||||||
|
popup/item_13/text = "Q"
|
||||||
|
popup/item_13/id = 13
|
||||||
|
popup/item_14/text = "R"
|
||||||
|
popup/item_14/id = 14
|
||||||
|
popup/item_15/text = "S"
|
||||||
|
popup/item_15/id = 15
|
||||||
|
popup/item_16/text = "T"
|
||||||
|
popup/item_16/id = 16
|
||||||
|
popup/item_17/text = "U"
|
||||||
|
popup/item_17/id = 17
|
||||||
|
popup/item_18/text = "V"
|
||||||
|
popup/item_18/id = 18
|
||||||
|
popup/item_19/text = "X"
|
||||||
|
popup/item_19/id = 19
|
||||||
|
popup/item_20/text = "Y"
|
||||||
|
popup/item_20/id = 20
|
||||||
|
popup/item_21/text = "Z"
|
||||||
|
popup/item_21/id = 21
|
||||||
|
popup/item_22/text = "1"
|
||||||
|
popup/item_22/id = 22
|
||||||
|
popup/item_23/text = "2"
|
||||||
|
popup/item_23/id = 23
|
||||||
|
popup/item_24/text = "3"
|
||||||
|
popup/item_24/id = 24
|
||||||
|
popup/item_25/text = "4"
|
||||||
|
popup/item_25/id = 25
|
||||||
|
popup/item_26/text = "5"
|
||||||
|
popup/item_26/id = 26
|
||||||
|
popup/item_27/text = "6"
|
||||||
|
popup/item_27/id = 27
|
||||||
|
popup/item_28/text = "7"
|
||||||
|
popup/item_28/id = 28
|
||||||
|
popup/item_29/text = "8"
|
||||||
|
popup/item_29/id = 29
|
||||||
|
popup/item_30/text = "9"
|
||||||
|
popup/item_30/id = 30
|
||||||
|
popup/item_31/text = "0"
|
||||||
|
popup/item_31/id = 31
|
||||||
|
|
||||||
|
[node name="MultilineSetting" type="HBoxContainer" parent="VBoxParent"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxParent/MultilineSetting"]
|
||||||
|
custom_minimum_size = Vector2(100, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 1
|
||||||
|
text = "Multiline Completions"
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="VBoxParent/MultilineSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Multiline" type="CheckBox" parent="VBoxParent/MultilineSetting"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
button_pressed = true
|
||||||
|
text = "Enabled"
|
||||||
|
|
||||||
|
[node name="Info" type="RichTextLabel" parent="VBoxParent"]
|
||||||
|
layout_mode = 2
|
||||||
|
focus_mode = 2
|
||||||
|
fit_content = true
|
||||||
|
selection_enabled = true
|
||||||
|
|
||||||
|
[node name="LLMs" type="Node" parent="."]
|
||||||
|
|
||||||
|
[node name="OpenAICompletion" type="Node" parent="LLMs"]
|
||||||
|
script = ExtResource("3_loa2x")
|
||||||
|
|
||||||
|
[node name="OpenAIChat" type="Node" parent="LLMs"]
|
||||||
|
script = ExtResource("2")
|
||||||
|
|
||||||
|
[node name="GithubCopilot" type="Node" parent="LLMs"]
|
||||||
|
script = ExtResource("6_hmh8w")
|
||||||
|
|
||||||
|
[node name="Version" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -23.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
horizontal_alignment = 2
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
|
[connection signal="text_changed" from="VBoxParent/OpenAiSetting/OpenAiKey" to="." method="_on_open_ai_key_changed"]
|
||||||
|
[connection signal="item_selected" from="VBoxParent/ModelSetting/Model" to="." method="_on_model_selected"]
|
||||||
|
[connection signal="item_selected" from="VBoxParent/ShortcutSetting/HBoxContainer/Modifier" to="." method="_on_shortcut_modifier_selected"]
|
||||||
|
[connection signal="item_selected" from="VBoxParent/ShortcutSetting/HBoxContainer/Key" to="." method="_on_shortcut_key_selected"]
|
||||||
|
[connection signal="toggled" from="VBoxParent/MultilineSetting/Multiline" to="." method="_on_multiline_toggled"]
|
||||||
|
[connection signal="meta_clicked" from="VBoxParent/Info" to="." method="on_info_meta_clicked"]
|
||||||
|
[connection signal="completion_error" from="LLMs/OpenAICompletion" to="." method="_on_code_completion_error"]
|
||||||
|
[connection signal="completion_received" from="LLMs/OpenAICompletion" to="." method="_on_code_completion_received"]
|
||||||
|
[connection signal="completion_error" from="LLMs/OpenAIChat" to="." method="_on_code_completion_error"]
|
||||||
|
[connection signal="completion_received" from="LLMs/OpenAIChat" to="." method="_on_code_completion_received"]
|
||||||
|
[connection signal="completion_error" from="LLMs/GithubCopilot" to="." method="_on_code_completion_error"]
|
||||||
|
[connection signal="completion_received" from="LLMs/GithubCopilot" to="." method="_on_code_completion_received"]
|
|
@ -0,0 +1,189 @@
|
||||||
|
@tool
|
||||||
|
extends "res://addons/copilot/LLM.gd"
|
||||||
|
|
||||||
|
const URL = "https://api.githubcopilot.com/chat/completions"
|
||||||
|
const AUTH_URL = "https://api.github.com/copilot_internal/v2/token"
|
||||||
|
const SYSTEM_TEMPLATE = """You are a brilliant coding assistant for the game-engine Godot. The version used is Godot 4.0, and all code must be valid GDScript!
|
||||||
|
That means the new GDScript 2.0 syntax is used. Here's a couple of important changes that were introduced:
|
||||||
|
- Use @export annotation for exports
|
||||||
|
- Use Node3D instead of Spatial, and position instead of translation
|
||||||
|
- Use randf_range and randi_range instead of rand_range
|
||||||
|
- Connect signals via node.SIGNAL_NAME.connect(Callable(TARGET_OBJECT, TARGET_FUNC))
|
||||||
|
- Same for sort_custom calls, pass a Callable(TARGET_OBJECT, TARGET_FUNC)
|
||||||
|
- Use rad_to_deg instead of rad2deg
|
||||||
|
- Use PackedByteArray instead of PoolByteArray
|
||||||
|
- Use instantiate instead of instance
|
||||||
|
- You can't use enumerate(OBJECT). Instead, use "for i in len(OBJECT):"
|
||||||
|
|
||||||
|
Remember, this is not Python. It's GDScript for use in Godot.
|
||||||
|
|
||||||
|
You may only answer in code, never add any explanations. In your prompt, there will be an !INSERT_CODE_HERE! tag. Only respond with plausible code that may be inserted at that point. Never repeat the full script, only the parts to be inserted. Treat this as if it was an autocompletion. You may continue whatever word or expression was left unfinished before the tag. Make sure indentation matches the surrounding context."""
|
||||||
|
const INSERT_TAG = "!INSERT_CODE_HERE!"
|
||||||
|
const MAX_LENGTH = 8500
|
||||||
|
|
||||||
|
const PREFERENCES_STORAGE_NAME = "user://github_copilot_llm.cfg"
|
||||||
|
const PREFERENCES_PASS = "Jr55ICpdp3M3CuWHX0WHLqg3yh4XBjbXX"
|
||||||
|
|
||||||
|
var machine_id
|
||||||
|
var session_id
|
||||||
|
var auth_token
|
||||||
|
|
||||||
|
signal auth_token_retrieved
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
var role: String
|
||||||
|
var content: String
|
||||||
|
|
||||||
|
func get_json():
|
||||||
|
return {
|
||||||
|
"role": role,
|
||||||
|
"content": content
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROLES = {
|
||||||
|
"SYSTEM": "system",
|
||||||
|
"USER": "user",
|
||||||
|
"ASSISTANT": "assistant"
|
||||||
|
}
|
||||||
|
|
||||||
|
func _get_models():
|
||||||
|
return [
|
||||||
|
"gpt-4-github-copilot"
|
||||||
|
]
|
||||||
|
|
||||||
|
func _set_model(model_name):
|
||||||
|
model = model_name.replace("github-copilot", "")
|
||||||
|
|
||||||
|
func _send_user_prompt(user_prompt, user_suffix):
|
||||||
|
var messages = format_prompt(user_prompt, user_suffix)
|
||||||
|
get_completion(messages, user_prompt, user_suffix)
|
||||||
|
|
||||||
|
func format_prompt(prompt, suffix):
|
||||||
|
var messages = []
|
||||||
|
var system_prompt = SYSTEM_TEMPLATE
|
||||||
|
|
||||||
|
var combined_prompt = prompt + suffix
|
||||||
|
var diff = combined_prompt.length() - MAX_LENGTH
|
||||||
|
if diff > 0:
|
||||||
|
if suffix.length() > diff:
|
||||||
|
suffix = suffix.substr(0,diff)
|
||||||
|
else:
|
||||||
|
prompt = prompt.substr(diff - suffix.length())
|
||||||
|
suffix = ""
|
||||||
|
var user_prompt = prompt + INSERT_TAG + suffix
|
||||||
|
|
||||||
|
var msg = Message.new()
|
||||||
|
msg.role = ROLES.SYSTEM
|
||||||
|
msg.content = system_prompt
|
||||||
|
messages.append(msg.get_json())
|
||||||
|
|
||||||
|
msg = Message.new()
|
||||||
|
msg.role = ROLES.USER
|
||||||
|
msg.content = user_prompt
|
||||||
|
messages.append(msg.get_json())
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
func gen_hex_str(length: int) -> String:
|
||||||
|
var rng = RandomNumberGenerator.new()
|
||||||
|
var result = PackedByteArray()
|
||||||
|
for i in range(length / 2):
|
||||||
|
result.push_back(rng.randi_range(0, 255))
|
||||||
|
var hex_str = ""
|
||||||
|
for byte in result:
|
||||||
|
hex_str += "%02x" % byte
|
||||||
|
return hex_str
|
||||||
|
|
||||||
|
func create_headers(token: String, stream: bool):
|
||||||
|
var contentType: String = "application/json; charset=utf-8"
|
||||||
|
if stream:
|
||||||
|
contentType = "text/event-stream; charset=utf-8"
|
||||||
|
|
||||||
|
load_config()
|
||||||
|
var uuidString: String = UUID.v4()
|
||||||
|
|
||||||
|
return [
|
||||||
|
"Authorization: %s" % ("Bearer " + token),
|
||||||
|
"X-Request-Id: %s" % uuidString,
|
||||||
|
"Vscode-Sessionid: %s" % session_id,
|
||||||
|
"Vscode-Machineid: %s" % machine_id,
|
||||||
|
"Editor-Version: vscode/1.83.1",
|
||||||
|
"Editor-Plugin-Version: copilot-chat/0.8.0",
|
||||||
|
"Openai-Organization: github-copilot",
|
||||||
|
"Openai-Intent: conversation-panel",
|
||||||
|
"Content-Type: %s" % contentType,
|
||||||
|
"User-Agent: GitHubCopilotChat/0.8.0",
|
||||||
|
"Accept: */*",
|
||||||
|
"Accept-Encoding: gzip,deflate,br",
|
||||||
|
"Connection: close"
|
||||||
|
]
|
||||||
|
|
||||||
|
func get_auth():
|
||||||
|
var headers = [
|
||||||
|
"Accept-Encoding: gzip",
|
||||||
|
"Authorization: token %s" % api_key
|
||||||
|
]
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.connect("request_completed",on_auth_request_completed)
|
||||||
|
var error = http_request.request(AUTH_URL, headers, HTTPClient.METHOD_GET)
|
||||||
|
if error != OK:
|
||||||
|
emit_signal("completion_error", null)
|
||||||
|
|
||||||
|
func get_completion(messages, prompt, suffix):
|
||||||
|
if not auth_token:
|
||||||
|
get_auth()
|
||||||
|
await auth_token_retrieved
|
||||||
|
|
||||||
|
var body = {
|
||||||
|
"model": model,
|
||||||
|
"messages": messages,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"top_p": 1,
|
||||||
|
"n": 1,
|
||||||
|
"stream": false,
|
||||||
|
}
|
||||||
|
var headers = create_headers(auth_token, false)
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.connect("request_completed",on_request_completed.bind(prompt, suffix, http_request))
|
||||||
|
var json_body = JSON.stringify(body)
|
||||||
|
var error = http_request.request(URL, headers, HTTPClient.METHOD_POST, json_body)
|
||||||
|
if error != OK:
|
||||||
|
emit_signal("completion_error", null)
|
||||||
|
|
||||||
|
func on_auth_request_completed(result, response_code, headers, body):
|
||||||
|
var test_json_conv = JSON.new()
|
||||||
|
test_json_conv.parse(body.get_string_from_utf8())
|
||||||
|
var json = test_json_conv.get_data()
|
||||||
|
auth_token = json.token
|
||||||
|
auth_token_retrieved.emit()
|
||||||
|
|
||||||
|
func on_request_completed(result, response_code, headers, body, pre, post, http_request):
|
||||||
|
var test_json_conv = JSON.new()
|
||||||
|
test_json_conv.parse(body.get_string_from_utf8())
|
||||||
|
var json = test_json_conv.get_data()
|
||||||
|
var response = json
|
||||||
|
if !response.has("choices") :
|
||||||
|
emit_signal("completion_error", response)
|
||||||
|
return
|
||||||
|
var completion = response.choices[0].message
|
||||||
|
if is_instance_valid(http_request):
|
||||||
|
http_request.queue_free()
|
||||||
|
emit_signal("completion_received", completion.content, pre, post)
|
||||||
|
|
||||||
|
func store_config():
|
||||||
|
var config = ConfigFile.new()
|
||||||
|
config.set_value("auth", "machine_id", machine_id)
|
||||||
|
config.save_encrypted_pass(PREFERENCES_STORAGE_NAME, PREFERENCES_PASS)
|
||||||
|
|
||||||
|
func load_config():
|
||||||
|
var config = ConfigFile.new()
|
||||||
|
var err = config.load_encrypted_pass(PREFERENCES_STORAGE_NAME, PREFERENCES_PASS)
|
||||||
|
if not session_id:
|
||||||
|
session_id = gen_hex_str(8) + "-" + gen_hex_str(4) + "-" + gen_hex_str(4) + "-" + gen_hex_str(4) + "-" + gen_hex_str(25)
|
||||||
|
if err != OK:
|
||||||
|
machine_id = UUID.v4()
|
||||||
|
store_config()
|
||||||
|
return
|
||||||
|
machine_id = config.get_value("auth", "machine_id", UUID.v4())
|
|
@ -0,0 +1,30 @@
|
||||||
|
@tool
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var model
|
||||||
|
var api_key
|
||||||
|
var allow_multiline
|
||||||
|
|
||||||
|
signal completion_received(completion, pre, post)
|
||||||
|
signal completion_error(error)
|
||||||
|
|
||||||
|
#Expects return value of String Array
|
||||||
|
func _get_models():
|
||||||
|
return []
|
||||||
|
|
||||||
|
#Sets active model
|
||||||
|
func _set_model(model_name):
|
||||||
|
model = model_name
|
||||||
|
|
||||||
|
#Sets API key
|
||||||
|
func _set_api_key(key):
|
||||||
|
api_key = key
|
||||||
|
|
||||||
|
#Determines if multiline completions are allowed
|
||||||
|
func _set_multiline(allowed):
|
||||||
|
allow_multiline = allowed
|
||||||
|
|
||||||
|
#Sends user prompt
|
||||||
|
func _send_user_prompt(user_prompt, user_suffix):
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
@tool
|
||||||
|
extends "res://addons/copilot/LLM.gd"
|
||||||
|
|
||||||
|
const URL = "https://api.openai.com/v1/chat/completions"
|
||||||
|
const SYSTEM_TEMPLATE = """You are a brilliant coding assistant for the game-engine Godot. The version used is Godot 4.0, and all code must be valid GDScript!
|
||||||
|
That means the new GDScript 2.0 syntax is used. Here's a couple of important changes that were introduced:
|
||||||
|
- Use @export annotation for exports
|
||||||
|
- Use Node3D instead of Spatial, and position instead of translation
|
||||||
|
- Use randf_range and randi_range instead of rand_range
|
||||||
|
- Connect signals via node.SIGNAL_NAME.connect(Callable(TARGET_OBJECT, TARGET_FUNC))
|
||||||
|
- Same for sort_custom calls, pass a Callable(TARGET_OBJECT, TARGET_FUNC)
|
||||||
|
- Use rad_to_deg instead of rad2deg
|
||||||
|
- Use PackedByteArray instead of PoolByteArray
|
||||||
|
- Use instantiate instead of instance
|
||||||
|
- You can't use enumerate(OBJECT). Instead, use "for i in len(OBJECT):"
|
||||||
|
|
||||||
|
Remember, this is not Python. It's GDScript for use in Godot.
|
||||||
|
|
||||||
|
You may only answer in code, never add any explanations. In your prompt, there will be an !INSERT_CODE_HERE! tag. Only respond with plausible code that may be inserted at that point. Never repeat the full script, only the parts to be inserted. Treat this as if it was an autocompletion. You may continue whatever word or expression was left unfinished before the tag. Make sure indentation matches the surrounding context."""
|
||||||
|
const INSERT_TAG = "!INSERT_CODE_HERE!"
|
||||||
|
const MAX_LENGTH = 8500
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
var role: String
|
||||||
|
var content: String
|
||||||
|
|
||||||
|
func get_json():
|
||||||
|
return {
|
||||||
|
"role": role,
|
||||||
|
"content": content
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROLES = {
|
||||||
|
"SYSTEM": "system",
|
||||||
|
"USER": "user",
|
||||||
|
"ASSISTANT": "assistant"
|
||||||
|
}
|
||||||
|
|
||||||
|
func _get_models():
|
||||||
|
return [
|
||||||
|
"gpt-3.5-turbo",
|
||||||
|
"gpt-4"
|
||||||
|
]
|
||||||
|
|
||||||
|
func _set_model(model_name):
|
||||||
|
model = model_name
|
||||||
|
|
||||||
|
func _send_user_prompt(user_prompt, user_suffix):
|
||||||
|
var messages = format_prompt(user_prompt, user_suffix)
|
||||||
|
get_completion(messages, user_prompt, user_suffix)
|
||||||
|
|
||||||
|
func format_prompt(prompt, suffix):
|
||||||
|
var messages = []
|
||||||
|
var system_prompt = SYSTEM_TEMPLATE
|
||||||
|
|
||||||
|
var combined_prompt = prompt + suffix
|
||||||
|
var diff = combined_prompt.length() - MAX_LENGTH
|
||||||
|
if diff > 0:
|
||||||
|
if suffix.length() > diff:
|
||||||
|
suffix = suffix.substr(0,diff)
|
||||||
|
else:
|
||||||
|
prompt = prompt.substr(diff - suffix.length())
|
||||||
|
suffix = ""
|
||||||
|
var user_prompt = prompt + INSERT_TAG + suffix
|
||||||
|
|
||||||
|
var msg = Message.new()
|
||||||
|
msg.role = ROLES.SYSTEM
|
||||||
|
msg.content = system_prompt
|
||||||
|
messages.append(msg.get_json())
|
||||||
|
|
||||||
|
msg = Message.new()
|
||||||
|
msg.role = ROLES.USER
|
||||||
|
msg.content = user_prompt
|
||||||
|
messages.append(msg.get_json())
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
func get_completion(messages, prompt, suffix):
|
||||||
|
var body = {
|
||||||
|
"model": model,
|
||||||
|
"messages": messages,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_tokens": 500,
|
||||||
|
"stop": "\n\n" if allow_multiline else "\n"
|
||||||
|
}
|
||||||
|
var headers = [
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Authorization: Bearer %s" % api_key
|
||||||
|
]
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.connect("request_completed",on_request_completed.bind(prompt, suffix, http_request))
|
||||||
|
var json_body = JSON.stringify(body)
|
||||||
|
var error = http_request.request(URL, headers, HTTPClient.METHOD_POST, json_body)
|
||||||
|
if error != OK:
|
||||||
|
emit_signal("completion_error", null)
|
||||||
|
|
||||||
|
|
||||||
|
func on_request_completed(result, response_code, headers, body, pre, post, http_request):
|
||||||
|
var test_json_conv = JSON.new()
|
||||||
|
test_json_conv.parse(body.get_string_from_utf8())
|
||||||
|
var json = test_json_conv.get_data()
|
||||||
|
var response = json
|
||||||
|
if !response.has("choices") :
|
||||||
|
emit_signal("completion_error", response)
|
||||||
|
return
|
||||||
|
var completion = response.choices[0].message
|
||||||
|
if is_instance_valid(http_request):
|
||||||
|
http_request.queue_free()
|
||||||
|
emit_signal("completion_received", completion.content, pre, post)
|
|
@ -0,0 +1,76 @@
|
||||||
|
@tool
|
||||||
|
extends "res://addons/copilot/LLM.gd"
|
||||||
|
|
||||||
|
const URL = "https://api.openai.com/v1/completions"
|
||||||
|
const PROMPT_PREFIX = """#This is a GDScript script using Godot 4.0.
|
||||||
|
#That means the new GDScript 2.0 syntax is used. Here's a couple of important changes that were introduced:
|
||||||
|
#- Use @export annotation for exports
|
||||||
|
#- Use Node3D instead of Spatial, and position instead of translation
|
||||||
|
#- Use randf_range and randi_range instead of rand_range
|
||||||
|
#- Connect signals via node.SIGNAL_NAME.connect(Callable(TARGET_OBJECT, TARGET_FUNC))
|
||||||
|
#- Connect signals via node.SIGNAL_NAME.connect(Callable(TARGET_OBJECT, TARGET_FUNC))
|
||||||
|
#- Use rad_to_deg instead of rad2deg
|
||||||
|
#- Use PackedByteArray instead of PoolByteArray
|
||||||
|
#- Use instantiate instead of instance
|
||||||
|
#- You can't use enumerate(OBJECT). Instead, use "for i in len(OBJECT):"
|
||||||
|
#
|
||||||
|
#Remember, this is not Python. It's GDScript for use in Godot.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
const MAX_LENGTH = 8500
|
||||||
|
|
||||||
|
func _get_models():
|
||||||
|
return [
|
||||||
|
"text-davinci-003"
|
||||||
|
]
|
||||||
|
|
||||||
|
func _set_model(model_name):
|
||||||
|
model = model_name
|
||||||
|
|
||||||
|
func _send_user_prompt(user_prompt, user_suffix):
|
||||||
|
get_completion(user_prompt, user_suffix)
|
||||||
|
|
||||||
|
func get_completion(_prompt, _suffix):
|
||||||
|
var prompt = _prompt
|
||||||
|
var suffix = _suffix
|
||||||
|
var combined_prompt = prompt + suffix
|
||||||
|
var diff = combined_prompt.length() - MAX_LENGTH
|
||||||
|
if diff > 0:
|
||||||
|
if suffix.length() > diff:
|
||||||
|
suffix = suffix.substr(0,diff)
|
||||||
|
else:
|
||||||
|
prompt = prompt.substr(diff - suffix.length())
|
||||||
|
suffix = ""
|
||||||
|
var body = {
|
||||||
|
"model": model,
|
||||||
|
"prompt": PROMPT_PREFIX + prompt,
|
||||||
|
"suffix": suffix,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_tokens": 500,
|
||||||
|
"stop": "\n\n" if allow_multiline else "\n"
|
||||||
|
}
|
||||||
|
var headers = [
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Authorization: Bearer %s" % api_key
|
||||||
|
]
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.connect("request_completed",on_request_completed.bind(prompt, suffix, http_request))
|
||||||
|
var json_body = JSON.stringify(body)
|
||||||
|
var error = http_request.request(URL, headers, HTTPClient.METHOD_POST, json_body)
|
||||||
|
if error != OK:
|
||||||
|
emit_signal("completion_error", null)
|
||||||
|
|
||||||
|
func on_request_completed(result, response_code, headers, body, pre, post, http_request):
|
||||||
|
var test_json_conv = JSON.new()
|
||||||
|
test_json_conv.parse(body.get_string_from_utf8())
|
||||||
|
var json = test_json_conv.get_data()
|
||||||
|
var response = json
|
||||||
|
if !response.has("choices"):
|
||||||
|
emit_signal("completion_error", response)
|
||||||
|
return
|
||||||
|
var completion = response.choices[0].text
|
||||||
|
if is_instance_valid(http_request):
|
||||||
|
http_request.queue_free()
|
||||||
|
emit_signal("completion_received", completion, pre, post)
|
|
@ -0,0 +1,21 @@
|
||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
const version = "1.0.0"
|
||||||
|
const scene_path = "res://addons/copilot/CopilotUI.tscn"
|
||||||
|
|
||||||
|
var dock
|
||||||
|
var editor_interface = get_editor_interface()
|
||||||
|
|
||||||
|
func _enter_tree() -> void:
|
||||||
|
if(!dock):
|
||||||
|
dock = load(scene_path).instantiate()
|
||||||
|
add_control_to_dock(EditorPlugin.DOCK_SLOT_RIGHT_UL, dock)
|
||||||
|
main_screen_changed.connect(Callable(dock, "on_main_screen_changed"))
|
||||||
|
dock.editor_interface = get_editor_interface()
|
||||||
|
dock.set_version(version)
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
remove_control_from_docks(dock)
|
||||||
|
dock.queue_free()
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
# From: https://github.com/binogure-studio/godot-uuid
|
||||||
|
# Credit: binogure-studio
|
||||||
|
|
||||||
|
# Note: The code might not be as pretty it could be, since it's written
|
||||||
|
# in a way that maximizes performance. Methods are inlined and loops are avoided.
|
||||||
|
extends Node
|
||||||
|
class_name UUID
|
||||||
|
|
||||||
|
const BYTE_MASK: int = 0b11111111
|
||||||
|
|
||||||
|
static func uuidbin():
|
||||||
|
# 16 random bytes with the bytes on index 6 and 8 modified
|
||||||
|
return [
|
||||||
|
randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK,
|
||||||
|
randi() & BYTE_MASK, randi() & BYTE_MASK, ((randi() & BYTE_MASK) & 0x0f) | 0x40, randi() & BYTE_MASK,
|
||||||
|
((randi() & BYTE_MASK) & 0x3f) | 0x80, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK,
|
||||||
|
randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK,
|
||||||
|
]
|
||||||
|
|
||||||
|
static func uuidbinrng(rng: RandomNumberGenerator):
|
||||||
|
return [
|
||||||
|
rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK,
|
||||||
|
rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, ((rng.randi() & BYTE_MASK) & 0x0f) | 0x40, rng.randi() & BYTE_MASK,
|
||||||
|
((rng.randi() & BYTE_MASK) & 0x3f) | 0x80, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK,
|
||||||
|
rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK,
|
||||||
|
]
|
||||||
|
|
||||||
|
static func v4():
|
||||||
|
# 16 random bytes with the bytes on index 6 and 8 modified
|
||||||
|
var b = uuidbin()
|
||||||
|
|
||||||
|
return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [
|
||||||
|
# low
|
||||||
|
b[0], b[1], b[2], b[3],
|
||||||
|
|
||||||
|
# mid
|
||||||
|
b[4], b[5],
|
||||||
|
|
||||||
|
# hi
|
||||||
|
b[6], b[7],
|
||||||
|
|
||||||
|
# clock
|
||||||
|
b[8], b[9],
|
||||||
|
|
||||||
|
# clock
|
||||||
|
b[10], b[11], b[12], b[13], b[14], b[15]
|
||||||
|
]
|
||||||
|
|
||||||
|
static func v4_rng(rng: RandomNumberGenerator):
|
||||||
|
# 16 random bytes with the bytes on index 6 and 8 modified
|
||||||
|
var b = uuidbinrng(rng)
|
||||||
|
|
||||||
|
return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [
|
||||||
|
# low
|
||||||
|
b[0], b[1], b[2], b[3],
|
||||||
|
|
||||||
|
# mid
|
||||||
|
b[4], b[5],
|
||||||
|
|
||||||
|
# hi
|
||||||
|
b[6], b[7],
|
||||||
|
|
||||||
|
# clock
|
||||||
|
b[8], b[9],
|
||||||
|
|
||||||
|
# clock
|
||||||
|
b[10], b[11], b[12], b[13], b[14], b[15]
|
||||||
|
]
|
||||||
|
|
||||||
|
var _uuid: Array
|
||||||
|
|
||||||
|
func _init(rng := RandomNumberGenerator.new()) -> void:
|
||||||
|
_uuid = uuidbinrng(rng)
|
||||||
|
|
||||||
|
func as_array() -> Array:
|
||||||
|
return _uuid.duplicate()
|
||||||
|
|
||||||
|
func as_dict(big_endian := true) -> Dictionary:
|
||||||
|
if big_endian:
|
||||||
|
return {
|
||||||
|
"low" : (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8 ) + _uuid[3],
|
||||||
|
"mid" : (_uuid[4] << 8 ) + _uuid[5],
|
||||||
|
"hi" : (_uuid[6] << 8 ) + _uuid[7],
|
||||||
|
"clock": (_uuid[8] << 8 ) + _uuid[9],
|
||||||
|
"node" : (_uuid[10] << 40) + (_uuid[11] << 32) + (_uuid[12] << 24) + (_uuid[13] << 16) + (_uuid[14] << 8 ) + _uuid[15]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"low" : _uuid[0] + (_uuid[1] << 8 ) + (_uuid[2] << 16) + (_uuid[3] << 24),
|
||||||
|
"mid" : _uuid[4] + (_uuid[5] << 8 ),
|
||||||
|
"hi" : _uuid[6] + (_uuid[7] << 8 ),
|
||||||
|
"clock": _uuid[8] + (_uuid[9] << 8 ),
|
||||||
|
"node" : _uuid[10] + (_uuid[11] << 8 ) + (_uuid[12] << 16) + (_uuid[13] << 24) + (_uuid[14] << 32) + (_uuid[15] << 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
func as_string() -> String:
|
||||||
|
return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [
|
||||||
|
# low
|
||||||
|
_uuid[0], _uuid[1], _uuid[2], _uuid[3],
|
||||||
|
|
||||||
|
# mid
|
||||||
|
_uuid[4], _uuid[5],
|
||||||
|
|
||||||
|
# hi
|
||||||
|
_uuid[6], _uuid[7],
|
||||||
|
|
||||||
|
# clock
|
||||||
|
_uuid[8], _uuid[9],
|
||||||
|
|
||||||
|
# node
|
||||||
|
_uuid[10], _uuid[11], _uuid[12], _uuid[13], _uuid[14], _uuid[15]
|
||||||
|
]
|
||||||
|
|
||||||
|
func is_equal(other) -> bool:
|
||||||
|
# Godot Engine compares Array recursively
|
||||||
|
# There's no need for custom comparison here.
|
||||||
|
return _uuid == other._uuid
|
|
@ -0,0 +1,45 @@
|
||||||
|
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://bl1rtf743e4l3"]
|
||||||
|
|
||||||
|
[sub_resource type="Shader" id="9"]
|
||||||
|
code = "shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform vec4 circle_color : source_color = vec4(0.0, 1.0, 1.0, 1.0);
|
||||||
|
uniform float circle_speed : hint_range(0.0, 10.0) = 1.0;
|
||||||
|
uniform float circle_width : hint_range(0.0, 1.0) = 0.1;
|
||||||
|
uniform float circle_count : hint_range(1.0, 20.0) = 6.0;
|
||||||
|
uniform float circle_size : hint_range(0.1, 2.0) = 0.5;
|
||||||
|
|
||||||
|
// Glow settings
|
||||||
|
uniform float glow_strength : hint_range(0.0, 1.0) = 0.5;
|
||||||
|
uniform float glow_radius : hint_range(0.0, 1.0) = 0.2;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 uv = UV * 3.0 - vec2(1.5, 1.5);
|
||||||
|
float len = length(uv);
|
||||||
|
|
||||||
|
float circle = 0.0;
|
||||||
|
for (float i = 0.0; i < circle_count; i++) {
|
||||||
|
float t = i / circle_count;
|
||||||
|
float time_offset = t * 6.28318; // 2 * PI
|
||||||
|
float radius = (1.0 - t * circle_size) * (1.0 + sin(TIME * circle_speed + time_offset) * 0.1);
|
||||||
|
float circle_strength = smoothstep(radius - circle_width, radius, len) - smoothstep(radius, radius + circle_width, len);
|
||||||
|
circle = max(circle, circle_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glow effect
|
||||||
|
float glow = smoothstep(circle_width, circle_width + glow_radius, circle);
|
||||||
|
circle += glow_strength * glow;
|
||||||
|
|
||||||
|
vec4 col = vec4(circle_color.rgb * circle, circle_color.a * circle);
|
||||||
|
COLOR = col;
|
||||||
|
}"
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
shader = SubResource("9")
|
||||||
|
shader_parameter/circle_color = Color(0.533, 0.60475, 0.82, 1)
|
||||||
|
shader_parameter/circle_speed = 2.881
|
||||||
|
shader_parameter/circle_width = 0.05
|
||||||
|
shader_parameter/circle_count = 4.0
|
||||||
|
shader_parameter/circle_size = 0.8
|
||||||
|
shader_parameter/glow_strength = 0.4
|
||||||
|
shader_parameter/glow_radius = 0.0
|
|
@ -0,0 +1,7 @@
|
||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Copilot"
|
||||||
|
description="Use large language models for AI assisted development in the Godot Engine."
|
||||||
|
author="Markus Sobkowski"
|
||||||
|
version="1.0"
|
||||||
|
script="Plugin.gd"
|
|
@ -0,0 +1,45 @@
|
||||||
|
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://cccmbprav6vgu"]
|
||||||
|
|
||||||
|
[sub_resource type="Shader" id="9"]
|
||||||
|
code = "shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform vec4 circle_color : source_color = vec4(0.0, 1.0, 1.0, 1.0);
|
||||||
|
uniform float circle_speed : hint_range(0.0, 10.0) = 1.0;
|
||||||
|
uniform float circle_width : hint_range(0.0, 1.0) = 0.1;
|
||||||
|
uniform float circle_count : hint_range(1.0, 20.0) = 6.0;
|
||||||
|
uniform float circle_size : hint_range(0.1, 2.0) = 0.5;
|
||||||
|
|
||||||
|
// Glow settings
|
||||||
|
uniform float glow_strength : hint_range(0.0, 1.0) = 0.5;
|
||||||
|
uniform float glow_radius : hint_range(0.0, 1.0) = 0.2;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 uv = UV * 3.0 - vec2(1.5, 1.5);
|
||||||
|
float len = length(uv);
|
||||||
|
|
||||||
|
float circle = 0.0;
|
||||||
|
for (float i = 0.0; i < circle_count; i++) {
|
||||||
|
float t = i / circle_count;
|
||||||
|
float time_offset = t * 6.28318; // 2 * PI
|
||||||
|
float radius = (1.0 - t * circle_size) * (1.0 + sin(TIME * circle_speed + time_offset) * 0.1);
|
||||||
|
float circle_strength = smoothstep(radius - circle_width, radius, len) - smoothstep(radius, radius + circle_width, len);
|
||||||
|
circle = max(circle, circle_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glow effect
|
||||||
|
float glow = smoothstep(circle_width, circle_width + glow_radius, circle);
|
||||||
|
circle += glow_strength * glow;
|
||||||
|
|
||||||
|
vec4 col = vec4(circle_color.rgb * circle, circle_color.a * circle);
|
||||||
|
COLOR = col;
|
||||||
|
}"
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
shader = SubResource("9")
|
||||||
|
shader_parameter/circle_color = Color(0.533, 0.60475, 0.82, 1)
|
||||||
|
shader_parameter/circle_speed = 4.0
|
||||||
|
shader_parameter/circle_width = 0.3
|
||||||
|
shader_parameter/circle_count = 2.0
|
||||||
|
shader_parameter/circle_size = 0.8
|
||||||
|
shader_parameter/glow_strength = 1.0
|
||||||
|
shader_parameter/glow_radius = 0.05
|
|
@ -2,17 +2,19 @@ extends Area2D
|
||||||
|
|
||||||
signal died
|
signal died
|
||||||
|
|
||||||
var start_pos = Vector2.ZERO
|
var start_pos: Vector2 = Vector2.ZERO
|
||||||
var speed = 0
|
var speed: float = 0
|
||||||
|
var is_exploded = false
|
||||||
|
|
||||||
var bullet_scene = preload("res://enemy_bullet/enemy_bullet.tscn")
|
var bullet_scene: PackedScene = preload("res://enemy_bullet/enemy_bullet.tscn")
|
||||||
|
|
||||||
@onready var screensize = get_viewport_rect().size
|
@onready var screensize: Vector2 = get_viewport_rect().size
|
||||||
|
|
||||||
func start(pos):
|
func start(pos):
|
||||||
speed = 0
|
speed = 0
|
||||||
position = Vector2(pos.x, -pos.y)
|
position = Vector2(pos.x, -pos.y)
|
||||||
start_pos = pos
|
start_pos = pos
|
||||||
|
is_exploded = false
|
||||||
await get_tree().create_timer(randf_range(0.25, 0.55)).timeout
|
await get_tree().create_timer(randf_range(0.25, 0.55)).timeout
|
||||||
var tween = create_tween().set_trans(Tween.TRANS_BACK)
|
var tween = create_tween().set_trans(Tween.TRANS_BACK)
|
||||||
tween.tween_property(self, "position:y", start_pos.y, 1.4)
|
tween.tween_property(self, "position:y", start_pos.y, 1.4)
|
||||||
|
@ -23,6 +25,10 @@ func start(pos):
|
||||||
$ShootTimer.start()
|
$ShootTimer.start()
|
||||||
|
|
||||||
func explode():
|
func explode():
|
||||||
|
if is_exploded:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_exploded = true
|
||||||
speed = 0
|
speed = 0
|
||||||
$AnimationPlayer.play("explode")
|
$AnimationPlayer.play("explode")
|
||||||
set_deferred("monitoring", false)
|
set_deferred("monitoring", false)
|
||||||
|
@ -44,3 +50,7 @@ func _on_shoot_timer_timeout():
|
||||||
b.start(position)
|
b.start(position)
|
||||||
$ShootTimer.wait_time = randf_range(4, 20)
|
$ShootTimer.wait_time = randf_range(4, 20)
|
||||||
$ShootTimer.start()
|
$ShootTimer.start()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_enemy_ready(pos):
|
||||||
|
start(pos)
|
||||||
|
|
19
main/main.gd
19
main/main.gd
|
@ -23,12 +23,19 @@ func spawn_enemies():
|
||||||
var e = enemy.instantiate()
|
var e = enemy.instantiate()
|
||||||
var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16)
|
var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16)
|
||||||
add_child(e)
|
add_child(e)
|
||||||
e.start(pos)
|
e.call_deferred("start", pos)
|
||||||
e.died.connect(_on_enemy_died)
|
e.connect("died", Callable(self, "_on_enemy_died"))
|
||||||
|
e.add_to_group("enemies")
|
||||||
|
|
||||||
|
enemies_count = 27
|
||||||
|
|
||||||
func _on_enemy_died(value):
|
func _on_enemy_died(value):
|
||||||
score += value
|
score += value
|
||||||
$CanvasLayer/UI.update_score(score)
|
$CanvasLayer/UI.update_score(score)
|
||||||
|
enemies_count -= 1
|
||||||
|
|
||||||
|
if enemies_count == 0:
|
||||||
|
call_deferred("spawn_enemies")
|
||||||
|
|
||||||
func _on_start_pressed():
|
func _on_start_pressed():
|
||||||
start_button.hide()
|
start_button.hide()
|
||||||
|
@ -41,11 +48,3 @@ func _on_player_died():
|
||||||
game_over.hide()
|
game_over.hide()
|
||||||
start_button.show()
|
start_button.show()
|
||||||
|
|
||||||
|
|
||||||
func _on_child_entered_tree(node):
|
|
||||||
if node.is_in_group("enemies"):
|
|
||||||
enemies_count += 1
|
|
||||||
|
|
||||||
func _on_child_exiting_tree(node):
|
|
||||||
if node.is_in_group("enemies"):
|
|
||||||
enemies_count -= 1
|
|
||||||
|
|
|
@ -5,11 +5,11 @@ signal shield_changed
|
||||||
|
|
||||||
@onready var screensize = get_viewport_rect().size
|
@onready var screensize = get_viewport_rect().size
|
||||||
|
|
||||||
@export var speed = 150
|
@export var speed: int = 150
|
||||||
@export var cooldown = 0.25
|
@export var cooldown: float = 0.25
|
||||||
@export var bullet_scene:PackedScene
|
@export var bullet_scene:PackedScene
|
||||||
|
|
||||||
@export var max_shield = 10
|
@export var max_shield: float = 10.0
|
||||||
|
|
||||||
var shield = max_shield:
|
var shield = max_shield:
|
||||||
set = set_shield
|
set = set_shield
|
||||||
|
@ -66,11 +66,11 @@ func _on_gun_cooldown_timeout():
|
||||||
func _on_player_area_entered(area):
|
func _on_player_area_entered(area):
|
||||||
if area.is_in_group("enemies"):
|
if area.is_in_group("enemies"):
|
||||||
area.explode()
|
area.explode()
|
||||||
shield -= max_shield / 2 # Replace with function body.
|
shield -= max_shield / 2.0 # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
func _on_area_entered(area):
|
func _on_area_entered(area):
|
||||||
if area.is_in_group("enemies"):
|
if area.is_in_group("enemies"):
|
||||||
area.explode()
|
area.explode()
|
||||||
shield -= max_shield / 2 # Replace with function body.
|
shield -= max_shield / 2.0 # Replace with function body.
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ window/size/window_width_override=480
|
||||||
window/size/window_height_override=640
|
window/size/window_height_override=640
|
||||||
window/stretch/mode="canvas_items"
|
window/stretch/mode="canvas_items"
|
||||||
|
|
||||||
|
[editor_plugins]
|
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/copilot/plugin.cfg")
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
left={
|
left={
|
||||||
|
|
Loading…
Reference in New Issue