From b0e5e891260e01eca1a2f6467190e4bd8c3fffce Mon Sep 17 00:00:00 2001 From: Zergling_man Date: Mon, 23 Jun 2025 20:49:20 +1000 Subject: [PATCH] init commit. I should have done it a while ago though --- .gitignore | 6 ++ README | 4 ++ SConstruct | 43 ++++++++++++ build_scons.sh | 6 ++ icon.svg | 1 + icon.svg.import | 37 ++++++++++ lobby.gd | 82 ++++++++++++++++++++++ lobby.gd.uid | 1 + menu.gd | 50 ++++++++++++++ menu.gd.uid | 1 + player.tscn | 39 +++++++++++ project.godot | 52 ++++++++++++++ src/controller.cpp | 58 ++++++++++++++++ src/controller.h | 34 ++++++++++ src/example.cpp.no | 35 ++++++++++ src/example.h.no | 28 ++++++++ src/game.cpp | 45 +++++++++++++ src/game.h | 29 ++++++++ src/kyara.cpp | 81 ++++++++++++++++++++++ src/kyara.h | 51 ++++++++++++++ src/nodespawner.cpp | 9 +++ src/nodespawner.h | 21 ++++++ src/register_types.cpp | 37 ++++++++++ src/register_types.h | 10 +++ ui.tscn | 150 +++++++++++++++++++++++++++++++++++++++++ 25 files changed, 910 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 SConstruct create mode 100644 build_scons.sh create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 lobby.gd create mode 100644 lobby.gd.uid create mode 100644 menu.gd create mode 100644 menu.gd.uid create mode 100644 player.tscn create mode 100644 project.godot create mode 100644 src/controller.cpp create mode 100644 src/controller.h create mode 100644 src/example.cpp.no create mode 100644 src/example.h.no create mode 100644 src/game.cpp create mode 100644 src/game.h create mode 100644 src/kyara.cpp create mode 100644 src/kyara.h create mode 100644 src/nodespawner.cpp create mode 100644 src/nodespawner.h create mode 100644 src/register_types.cpp create mode 100644 src/register_types.h create mode 100644 ui.tscn diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e1b7f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Godot 4+ specific ignores +.godot/ +/android/ +src/*.os +bin +godot-cpp \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..62dad51 --- /dev/null +++ b/README @@ -0,0 +1,4 @@ +You must `scons` to create ./bin/whatever before running this + +Deps: Godot >=4.0 (probably) +Makedeps: sconstruct, godot-cpp (which should be symlinked from .) \ No newline at end of file diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..382bffb --- /dev/null +++ b/SConstruct @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import os +import sys + +env = SConscript("godot-cpp/SConstruct") + +# For reference: +# - CCFLAGS are compilation flags shared between C and C++ +# - CFLAGS are for C-specific compilation flags +# - CXXFLAGS are for C++-specific compilation flags +# - CPPFLAGS are for pre-processor flags +# - CPPDEFINES are for pre-processor defines +# - LINKFLAGS are for linking flags + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=["src/"]) +sources = Glob("src/*.cpp") + +if env["platform"] == "macos": + library = env.SharedLibrary( + "bin/libgdcharacter.{}.{}.framework/libgdexample.{}.{}".format( + env["platform"], env["target"], env["platform"], env["target"] + ), + source=sources, + ) +elif env["platform"] == "ios": + if env["ios_simulator"]: + library = env.StaticLibrary( + "bin/libgdcharacter.{}.{}.simulator.a".format(env["platform"], env["target"]), + source=sources, + ) + else: + library = env.StaticLibrary( + "bin/libgdcharacter.{}.{}.a".format(env["platform"], env["target"]), + source=sources, + ) +else: + library = env.SharedLibrary( + "bin/libgdcharacter{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), + source=sources, + ) + +Default(library) diff --git a/build_scons.sh b/build_scons.sh new file mode 100644 index 0000000..06d7c8d --- /dev/null +++ b/build_scons.sh @@ -0,0 +1,6 @@ +# Debug +scons + +# Release +scons target=template_release +scons platform=win target=template_release \ No newline at end of file diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..c34be4a --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5efnxljgii3e" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/lobby.gd b/lobby.gd new file mode 100644 index 0000000..cd3d8d3 --- /dev/null +++ b/lobby.gd @@ -0,0 +1,82 @@ +extends Control + +signal game_start +var pname="" +var players={} + +var PLAYER_SCENE=load("res://player.tscn") + +func _ready(): + multiplayer.peer_connected.connect(add_player) + multiplayer.peer_disconnected.connect(remove_player) + +func start(id,given_name): + push_warning(id) # Helps identify windows for print() output, but push_warning is more convenient anyway. + pname=given_name # Why can't I have push_notice/push_debug? + add_player(id) + show() + +# ESSENTIAL TENETS: +# The multiplayer authority of spawning basically always resides with 1. +# Whether this is tied to the authority of the spawner or its target node, I don't know, but it doesn't really matter. +# THEREFORE, any local data you want to pick up has to be fetched after RPC handoff. +# In addition: Any properties set on an object in the frame it's created probably won't sync or persist +# use call_deferred or await get_tree().process_frame to fix this. +func add_player(pid): + players[pid]={} + var a + if multiplayer.get_unique_id()==1: + a=PLAYER_SCENE.instantiate() + a.name=str(pid) + $fuck_layouts/player_list.add_child(a) + else: + a=$fuck_layouts/player_list.find_child(str(pid),false,false) + while a==null: + await get_tree().process_frame + a=$fuck_layouts/player_list.find_child(str(pid),false,false) + a.set_multiplayer_authority(pid) + a.get_node('PName').text=pname # This tries to force local name on every node but sync will fix it after + +func remove_player(pid): + players.erase(pid) + # Kinda works? Sometimes posts errors from other clients but still behaves correctly so idk + $fuck_layouts/player_list.remove_child($fuck_layouts/player_list.get_node(str(pid))) + +func _on_send_message_pressed(message=""): + var box=$fuck_layouts/menu_shiz/chats/shitpost/input + if message=="": message=box.text + rpc("receive_message",pname,message) + box.text="" + +@rpc("any_peer","call_local") +func receive_message(pid,message): + var a=Label.new() + a.text=str(pid)+": "+message + var scroll=$fuck_layouts/menu_shiz/chats/elder + scroll.get_node('messages').add_child(a) + # I don't really understand this magic number but it works on my machine. + # The offset is actually 128px, but this means it should grab even if you only see ~3/4 of the last message + var bump=scroll.scroll_vertical+134>scroll.get_v_scroll_bar().max_value + await get_tree().process_frame # This seems to not work if the window doesn't have focus. + # Not going to bother figuring it out unless it's important for something else. + if bump: scroll.ensure_control_visible(a) + +func _start_clicked(): + var pid=0 + for child in $fuck_layouts/player_list.get_children(true): + if child.name=='player_spawner': continue + pid=child.get_multiplayer_authority() + players[pid]['name']=child.get_node('PName').text + players[pid]['class']=child.get_node('PChar/Class').selected + if players.values().any(func(x):return x=={} or x['class']==-1): + receive_message('System','Not all players have finalised their options') + return + rpc("gamestart",players) + +@rpc("any_peer","call_local") +func gamestart(players): + emit_signal('game_start',players) + hide() + +func _idk_clicked(): + pass diff --git a/lobby.gd.uid b/lobby.gd.uid new file mode 100644 index 0000000..56f94c0 --- /dev/null +++ b/lobby.gd.uid @@ -0,0 +1 @@ +uid://uwpqjqvde2ot diff --git a/menu.gd b/menu.gd new file mode 100644 index 0000000..6bea11f --- /dev/null +++ b/menu.gd @@ -0,0 +1,50 @@ +extends Control + +const PORT=60001 +signal lobby_join + +func _ready(): + # Start paused. + # This actually breaks the buttons lol + #get_tree().paused = true + # You can save bandwidth by disabling server relay and peer notifications. + #multiplayer.server_relay = false + + # Automatically start the server in headless mode. + if DisplayServer.get_name() == "headless": + # Should attempt to pull port from config file + print("Automatically starting dedicated server.") + _on_host_pressed.call_deferred(true) + +func _on_host_pressed(headless=false): + # Start as server. + var peer = ENetMultiplayerPeer.new() + var port = $Net/Options/port.text + if port=="": port=PORT + peer.create_server(port) + if peer.get_connection_status() == MultiplayerPeer.CONNECTION_DISCONNECTED: + OS.alert("Failed to start multiplayer server.") + return + multiplayer.multiplayer_peer = peer + start_game(headless) + +func _on_join_pressed(): + # Start as client. + var port = $Net/Options/port.text + if port=="": port=PORT + var txt : String = $Net/Options/address.text + if txt == "": + OS.alert("Need a remote to connect to.") + return + var peer = ENetMultiplayerPeer.new() + peer.create_client(txt, port) + if peer.get_connection_status() == MultiplayerPeer.CONNECTION_DISCONNECTED: + OS.alert("Failed to start multiplayer client.") + return + multiplayer.multiplayer_peer = peer + start_game() # Can't run a headless client lol what are you trying to bot the game + +func start_game(headless=false): + if headless: return + emit_signal("lobby_join",multiplayer.get_unique_id(),$Net/pname/Name.text) + hide() diff --git a/menu.gd.uid b/menu.gd.uid new file mode 100644 index 0000000..5d889f1 --- /dev/null +++ b/menu.gd.uid @@ -0,0 +1 @@ +uid://baxkcpwuj61it diff --git a/player.tscn b/player.tscn new file mode 100644 index 0000000..e4ff561 --- /dev/null +++ b/player.tscn @@ -0,0 +1,39 @@ +[gd_scene load_steps=2 format=3 uid="uid://b25q27admm4le"] + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_sh265"] +properties/0/path = NodePath("PName:text") +properties/0/spawn = true +properties/0/replication_mode = 1 +properties/1/path = NodePath("PChar/Class:selected") +properties/1/spawn = true +properties/1/replication_mode = 1 + +[node name="Player" type="VBoxContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="PName" type="Label" parent="."] +layout_mode = 2 + +[node name="PChar" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Class" type="OptionButton" parent="PChar"] +layout_mode = 2 +item_count = 6 +popup/item_0/text = "Pyromancer" +popup/item_0/id = 0 +popup/item_1/text = "Rogue" +popup/item_1/id = 1 +popup/item_2/text = "Spelunker" +popup/item_2/id = 2 +popup/item_3/text = "Acrobat" +popup/item_3/id = 3 +popup/item_4/text = "Paladin" +popup/item_4/id = 4 +popup/item_5/text = "Medic" +popup/item_5/id = 5 + +[node name="sonq" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_sh265") diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..c271f9f --- /dev/null +++ b/project.godot @@ -0,0 +1,52 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="amb-crawl" +run/main_scene="res://ui.tscn" +config/features=PackedStringArray("4.4", "GL Compatibility") +config/icon="res://icon.svg" + +[dotnet] + +project/assembly_name="net-test" + +[input] + +move_up={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"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":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":85,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_down={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"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":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":69,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"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":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":78,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"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":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":73,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/src/controller.cpp b/src/controller.cpp new file mode 100644 index 0000000..e5716d0 --- /dev/null +++ b/src/controller.cpp @@ -0,0 +1,58 @@ +#include "controller.h" + +using namespace godot; + +void Controller::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_flags"), &Controller::get_flags); + ClassDB::bind_method(D_METHOD("set_flags","p_flags"), &Controller::set_flags); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags"), "set_flags", "get_flags"); + ClassDB::bind_method(D_METHOD("get_target"), &Controller::get_target); + ClassDB::bind_method(D_METHOD("set_target","p_target"), &Controller::set_target); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "target", PROPERTY_HINT_RESOURCE_TYPE, "Vector2"), "set_target", "get_target"); +} + +Controller::Controller() +{ + flags=0; + target=Vector2(0,0); +} +Controller::~Controller(){} + +void Controller::_process(double delta) +{ + if (get_multiplayer()->get_unique_id()!=get_multiplayer_authority()) return; + // Kinda janky but this might come in handy later + target=parent->get_global_mouse_position(); +} + +void Controller::_enter_tree() +{ + set_root_path(NodePath(".")); + Ref conf=memnew(SceneReplicationConfig); + set_replication_config(conf); + conf->add_property(":flags"); + conf->add_property(":target"); +} + +void Controller::_unhandled_key_input(const Ref &p_event) +{ + if (get_multiplayer()->get_unique_id()!=get_multiplayer_authority()) return; + UtilityFunctions::print("pre ",flags); + //これ嫌い + if (p_event->is_action_pressed("move_up",false,true)) flags|=1; + if (p_event->is_action_released("move_up",true)) flags&=-2; + if (p_event->is_action_pressed("move_down",false,true)) flags|=2; + if (p_event->is_action_released("move_down",true)) flags&=-3; + if (p_event->is_action_pressed("move_left",false,true)) flags|=4; + if (p_event->is_action_released("move_left",true)) flags&=-5; + if (p_event->is_action_pressed("move_right",false,true)) flags|=8; + if (p_event->is_action_released("move_right",true)) flags&=-9; + //Viewport.set_input_as_handled(); // I have no idea how this figures out which input to flag as handled. +} + +void Controller::set_flags(const int p_flags) { flags=p_flags; } +int Controller::get_flags() const { return flags; } + +void Controller::set_target(const Vector2 p_target) { target=p_target; } +Vector2 Controller::get_target() const { return target; } \ No newline at end of file diff --git a/src/controller.h b/src/controller.h new file mode 100644 index 0000000..e05997a --- /dev/null +++ b/src/controller.h @@ -0,0 +1,34 @@ +#ifndef GDCONTROLLER_H +#define GDCONTROLLER_H + +#include +// YOU MAY GET MYSTERIOUS "INVALID USE OF INCOMPLETE TYPE" ERRORS IF YOU DO NOT INCLUDE THE THINGS YOU NEED, BECAUSE THEY MIGHT BE BRIEFLY DEFINED IN OTHER THINGS. +#include +#include +#include +#include + +namespace godot +{ +class Controller : public MultiplayerSynchronizer +{ + GDCLASS(Controller,MultiplayerSynchronizer) +protected: + static void _bind_methods(); +public: + int flags; + Vector2 target; + CanvasItem *parent; + Controller(); + ~Controller(); + void _process(double delta) override; + void _enter_tree() override; + void _unhandled_key_input(const Ref &p_event); + // Garbage + void set_flags(const int p_flags); + int get_flags() const; + void set_target(const Vector2 p_target); + Vector2 get_target() const; +}; +} +#endif \ No newline at end of file diff --git a/src/example.cpp.no b/src/example.cpp.no new file mode 100644 index 0000000..325da13 --- /dev/null +++ b/src/example.cpp.no @@ -0,0 +1,35 @@ +#include "example.h" +#include + +using namespace godot; + +void Example::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_amplitude"), &Example::get_amplitude); + ClassDB::bind_method(D_METHOD("set_amplitude","p_amplitude"), &Example::set_amplitude); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amplitude"), "set_amplitude", "get_amplitude"); + + ClassDB::bind_method(D_METHOD("get_speed"), &Example::get_speed); + ClassDB::bind_method(D_METHOD("set_speed","p_speed"), &Example::set_speed); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed", PROPERTY_HINT_RANGE,"0,20,0.01"), "set_speed", "get_speed"); +} + +Example::Example() +{ + time_passed=0.0; + amplitude=10.0; + speed=1.0; +} +Example::~Example(){} +void Example::_process(double delta) +{ + time_passed+=speed*delta; + Vector2 new_position=Vector2(amplitude+(amplitude*sin(time_passed*2.0)),amplitude+(amplitude*cos(time_passed*1.5))); + set_position(new_position); +} + +void Example::set_amplitude(const double p_amplitude) { amplitude=p_amplitude; } +double Example::get_amplitude() const { return amplitude; } + +void Example::set_speed(const double p_speed) { speed=p_speed; } +double Example::get_speed() const { return speed; } \ No newline at end of file diff --git a/src/example.h.no b/src/example.h.no new file mode 100644 index 0000000..fb78e8c --- /dev/null +++ b/src/example.h.no @@ -0,0 +1,28 @@ +#ifndef GDEXAMPLE_H +#define GDEXAMPLE_H + +#include + +namespace godot +{ +class Example : public Sprite2D +{ + GDCLASS(Example, Sprite2D) +private: + double time_passed; + double amplitude; + double speed; +protected: + static void _bind_methods(); +public: + Example(); + ~Example(); + void _process(double delta) override; + void set_amplitude(const double p_amplitude); + double get_amplitude() const; + void set_speed(const double p_speed); + double get_speed() const; +}; +} + +#endif \ No newline at end of file diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..8512783 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,45 @@ +#include "game.h" +#include + +using namespace godot; + +void Game::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("start","players"),&Game::start); + ClassDB::bind_method(D_METHOD("kyaraspawn","data"),&Game::kyaraspawn); +} + +Game::Game() +{ + spawn=memnew(MultiplayerSpawner); + spawn->set_name("_spawn"); + spawn->set_spawn_function(callable_mp(this, &Game::kyaraspawn)); + spawn->set_spawn_path(".."); + add_child(spawn,false,INTERNAL_MODE_BACK); +} +Game::~Game(){} + +void Game::start(const Dictionary &players) +{ + show(); + if (get_multiplayer()->get_unique_id()!=1) return; + Array keys=players.keys(); + for (int i=0; i(players[keys[i]])["pid"]=keys[i]; + UtilityFunctions::print(get_multiplayer()->get_unique_id()," ",keys[i]," ",players[keys[i]]," ",static_cast(players[keys[i]])["name"]); + spawn->spawn(players[keys[i]]); + } +} + +Node* Game::kyaraspawn(const Variant &data) +{ + Dictionary dat=static_cast(data); + UtilityFunctions::print(get_multiplayer()->get_unique_id()," ",data); + Character* kya=memnew(Character); + kya->set_name(dat["name"]); + kya->multiplayerowner=dat["pid"]; + kya->set_position(Vector2(50,50)); + kya->set_texture(ImageTexture::create_from_image(Image::load_from_file("res://icon.svg"))); + return kya; +} \ No newline at end of file diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..163d7de --- /dev/null +++ b/src/game.h @@ -0,0 +1,29 @@ +#ifndef GDGAME_H +#define GDGAME_H + +#include + +#include "kyara.h" +#include +#include +#include +#include + +namespace godot +{ +class Game : public Node2D +{ + GDCLASS(Game, Node2D) +private: + MultiplayerSpawner *spawn; +protected: + static void _bind_methods(); +public: + Game(); + ~Game(); + void start(const Dictionary &players); + Node* kyaraspawn(const Variant &data); +}; +} + +#endif \ No newline at end of file diff --git a/src/kyara.cpp b/src/kyara.cpp new file mode 100644 index 0000000..7b6e270 --- /dev/null +++ b/src/kyara.cpp @@ -0,0 +1,81 @@ +#include "kyara.h" +#include + +using namespace godot; + +void Character::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_balance_radius"), &Character::get_balance_radius); + ClassDB::bind_method(D_METHOD("set_balance_radius","p_balance_radius"), &Character::set_balance_radius); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "balance_radius"), "set_balance_radius", "get_balance_radius"); + + ClassDB::bind_method(D_METHOD("get_speed"), &Character::get_speed); + ClassDB::bind_method(D_METHOD("set_speed","p_speed"), &Character::set_speed); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed", PROPERTY_HINT_RANGE,"0,20,0.01"), "set_speed", "get_speed"); + + ClassDB::bind_method(D_METHOD("get_texture"), &Character::get_texture); + ClassDB::bind_method(D_METHOD("set_texture","p_texture"), &Character::set_texture); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); +} + +Character::Character() +{ + set_motion_mode(MOTION_MODE_FLOATING); + speed=1.0; + balance_radius=64.0; + face=memnew(Sprite2D); + face->set_name("_face"); + add_child(face,false,INTERNAL_MODE_BACK); + input=memnew(Controller); + input->set_name("_input"); + input->parent=this; + add_child(input,false,INTERNAL_MODE_BACK); + sync=memnew(MultiplayerSynchronizer); + sync->set_name("_sync"); + add_child(sync,false,INTERNAL_MODE_BACK); + shape=memnew(CollisionShape2D); + shape->set_name("_shape"); + add_child(shape,false,INTERNAL_MODE_BACK); +} +Character::~Character(){} + +void Character::_process(double delta) +{ + // これも嫌い + if (input->flags&1) move(0.0,-speed); + if (input->flags&2) move(0.0,speed); + if (input->flags&4) move(-speed,0.0); + if (input->flags&8) move(speed,0.0); + look_at(input->target); + // Not sure if I should do this. Issues could occur if I'm wrong either side. + //CharacterBody2D::_process(delta); +} + +void Character::_ready() +{ + set_motion_mode(MOTION_MODE_FLOATING); +} + +void Character::_enter_tree() +{ + Ref conf=memnew(SceneReplicationConfig); + conf->add_property(":rotation"); + conf->add_property(":position"); + sync->set_replication_config(conf); + input->set_multiplayer_authority(multiplayerowner); +} + +void Character::move(double x,double y) +{ + // Collision detection needs to go in here, as well as adjusting and displaying balance radius, buncha shit + set_position(get_position()+Vector2(x,y)); +} + +void Character::set_balance_radius(const double p_balance_radius) { balance_radius=p_balance_radius; } +double Character::get_balance_radius() const { return balance_radius; } + +void Character::set_speed(const double p_speed) { speed=p_speed; } +double Character::get_speed() const { return speed; } + +void Character::set_texture(const Ref &p_texture) { face->set_texture(p_texture); } +Ref Character::get_texture() const { return face->get_texture(); } \ No newline at end of file diff --git a/src/kyara.h b/src/kyara.h new file mode 100644 index 0000000..a344544 --- /dev/null +++ b/src/kyara.h @@ -0,0 +1,51 @@ +#ifndef GDKYARA_H +#define GDKYARA_H + +#include + +// Children: +#include "controller.h" +#include +#include +#include +// Grandchildren: +#include +// YOU MAY GET MYSTERIOUS "INVALID USE OF INCOMPLETE TYPE" ERRORS IF YOU DO NOT INCLUDE THE THINGS YOU NEED, BECAUSE THEY MIGHT BE BRIEFLY DEFINED IN OTHER THINGS. +#include + +namespace godot +{ +class Character : public CharacterBody2D +{ + GDCLASS(Character, CharacterBody2D) +private: + double speed; + double balance_radius; + void move(double x,double y); + Sprite2D *face; + Controller *input; + MultiplayerSynchronizer *sync; + CollisionShape2D *shape; +protected: + static void _bind_methods(); +public: + Character(); + ~Character(); + void _process(double delta) override; + void _ready() override; + void _enter_tree() override; + int multiplayerowner; + // Garbage + void set_balance_radius(const double p_balance_radius); + double get_balance_radius() const; + void set_speed(const double p_speed); + double get_speed() const; + // This garbage ripped straight from sprite2d because I basically just want to pass it through. + // I don't really need to (I can just make the sprite public) but it's good practice + // (By good practice I mean this cost me at least an hour of debugging const Ref<>) + void set_texture(const Ref &p_texture); + Ref get_texture() const; +}; +} + +#endif \ No newline at end of file diff --git a/src/nodespawner.cpp b/src/nodespawner.cpp new file mode 100644 index 0000000..397575f --- /dev/null +++ b/src/nodespawner.cpp @@ -0,0 +1,9 @@ +#include "nodespawner.h" +#include + +using namespace godot; + +void NodeSpawner::_bind_methods() +{ + +} diff --git a/src/nodespawner.h b/src/nodespawner.h new file mode 100644 index 0000000..e737748 --- /dev/null +++ b/src/nodespawner.h @@ -0,0 +1,21 @@ +#ifndef GDNODESPAWNER_H +#define GDNODESPAWNER_H + +#include +// Probably won't need this. +//#include + +namespace godot +{ +class NodeSpawner : public MultiplayerSpawner +{ + GDCLASS(NodeSpawner,MultiplayerSpawner) +private: +String *spawnable_nodes; +protected: + static void _bind_methods(); +public: +}; +} + +#endif \ No newline at end of file diff --git a/src/register_types.cpp b/src/register_types.cpp new file mode 100644 index 0000000..9efd291 --- /dev/null +++ b/src/register_types.cpp @@ -0,0 +1,37 @@ +#include "register_types.h" +#include "kyara.h" +#include "controller.h" +#include "game.h" +#include "nodespawner.h" +#include +#include +#include + +using namespace godot; + +void initialize_character_module(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } + GDREGISTER_RUNTIME_CLASS(Character); + GDREGISTER_RUNTIME_CLASS(Controller); + GDREGISTER_RUNTIME_CLASS(Game); + GDREGISTER_RUNTIME_CLASS(NodeSpawner); +} + +void uninitialize_character_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } +} + +extern "C" +{ +GDExtensionBool GDE_EXPORT character_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) +{ + godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); + + init_obj.register_initializer(initialize_character_module); + init_obj.register_terminator(uninitialize_character_module); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + + return init_obj.init(); +} +} \ No newline at end of file diff --git a/src/register_types.h b/src/register_types.h new file mode 100644 index 0000000..e484a12 --- /dev/null +++ b/src/register_types.h @@ -0,0 +1,10 @@ +#ifndef CHARACTER_REGISTER_TYPES_H +#define CHARACTER_REGISTER_TYPES_H + +#include + +using namespace godot; +void initialize_character_module(ModuleInitializationLevel p_level); +void uninitialize_character_module(ModuleInitializationLevel p_level); + +#endif \ No newline at end of file diff --git a/ui.tscn b/ui.tscn new file mode 100644 index 0000000..f8089a0 --- /dev/null +++ b/ui.tscn @@ -0,0 +1,150 @@ +[gd_scene load_steps=3 format=3 uid="uid://1qpp4ng383lf"] + +[ext_resource type="Script" uid="uid://baxkcpwuj61it" path="res://menu.gd" id="1_fyqef"] +[ext_resource type="Script" uid="uid://uwpqjqvde2ot" path="res://lobby.gd" id="2_m6e0p"] + +[node name="scene" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="main_menu" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_fyqef") + +[node name="Net" type="VBoxContainer" parent="main_menu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -130.5 +offset_top = -64.0 +offset_right = 130.5 +offset_bottom = 64.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Direct" type="Label" parent="main_menu/Net"] +layout_mode = 2 +text = "Direct connect:" + +[node name="Options" type="HBoxContainer" parent="main_menu/Net"] +layout_mode = 2 + +[node name="address" type="LineEdit" parent="main_menu/Net/Options"] +custom_minimum_size = Vector2(96, 0) +layout_mode = 2 +placeholder_text = "127.0.0.1" + +[node name="port" type="LineEdit" parent="main_menu/Net/Options"] +layout_mode = 2 +placeholder_text = "60001" + +[node name="pname" type="HBoxContainer" parent="main_menu/Net"] +layout_mode = 2 + +[node name="Nickname" type="Label" parent="main_menu/Net/pname"] +layout_mode = 2 +text = "Nickname:" + +[node name="Name" type="LineEdit" parent="main_menu/Net/pname"] +custom_minimum_size = Vector2(128, 0) +layout_mode = 2 + +[node name="buttons" type="HBoxContainer" parent="main_menu/Net"] +layout_mode = 2 + +[node name="Host" type="Button" parent="main_menu/Net/buttons"] +layout_mode = 2 +text = "Host" + +[node name="Join" type="Button" parent="main_menu/Net/buttons"] +layout_mode = 2 +text = "Join" + +[node name="host_note" type="Label" parent="main_menu/Net/buttons"] +layout_mode = 2 +text = "(host ignores address)" + +[node name="Lobby" type="Control" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 0 +script = ExtResource("2_m6e0p") + +[node name="fuck_layouts" type="VBoxContainer" parent="Lobby"] +layout_mode = 0 +offset_right = 516.0 +offset_bottom = 167.0 + +[node name="menu_shiz" type="HBoxContainer" parent="Lobby/fuck_layouts"] +layout_mode = 2 + +[node name="chats" type="VBoxContainer" parent="Lobby/fuck_layouts/menu_shiz"] +layout_mode = 2 + +[node name="elder" type="ScrollContainer" parent="Lobby/fuck_layouts/menu_shiz/chats"] +custom_minimum_size = Vector2(512, 128) +layout_mode = 2 + +[node name="messages" type="VBoxContainer" parent="Lobby/fuck_layouts/menu_shiz/chats/elder"] +layout_mode = 2 + +[node name="shitpost" type="HBoxContainer" parent="Lobby/fuck_layouts/menu_shiz/chats"] +custom_minimum_size = Vector2(512, 0) +layout_mode = 2 + +[node name="input" type="LineEdit" parent="Lobby/fuck_layouts/menu_shiz/chats/shitpost"] +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "ni-" +keep_editing_on_text_submit = true + +[node name="send_message" type="Button" parent="Lobby/fuck_layouts/menu_shiz/chats/shitpost"] +layout_mode = 2 +text = "Shitpost" + +[node name="buttons" type="VBoxContainer" parent="Lobby/fuck_layouts/menu_shiz"] +layout_mode = 2 + +[node name="start" type="Button" parent="Lobby/fuck_layouts/menu_shiz/buttons"] +layout_mode = 2 +text = "Start" + +[node name="leave" type="Button" parent="Lobby/fuck_layouts/menu_shiz/buttons"] +layout_mode = 2 +text = "Leave" + +[node name="idk" type="Button" parent="Lobby/fuck_layouts/menu_shiz/buttons"] +layout_mode = 2 +text = "idk" + +[node name="player_list" type="HBoxContainer" parent="Lobby/fuck_layouts"] +layout_mode = 2 + +[node name="player_spawner" type="MultiplayerSpawner" parent="Lobby/fuck_layouts/player_list"] +_spawnable_scenes = PackedStringArray("uid://b25q27admm4le") +spawn_path = NodePath("..") + +[node name="Game" type="Game" parent="."] + +[connection signal="lobby_join" from="main_menu" to="Lobby" method="start"] +[connection signal="pressed" from="main_menu/Net/buttons/Host" to="main_menu" method="_on_host_pressed"] +[connection signal="pressed" from="main_menu/Net/buttons/Join" to="main_menu" method="_on_join_pressed"] +[connection signal="game_start" from="Lobby" to="Game" method="start"] +[connection signal="text_submitted" from="Lobby/fuck_layouts/menu_shiz/chats/shitpost/input" to="Lobby" method="_on_send_message_pressed"] +[connection signal="pressed" from="Lobby/fuck_layouts/menu_shiz/chats/shitpost/send_message" to="Lobby" method="_on_send_message_pressed"] +[connection signal="pressed" from="Lobby/fuck_layouts/menu_shiz/buttons/start" to="Lobby" method="_start_clicked"] +[connection signal="pressed" from="Lobby/fuck_layouts/menu_shiz/buttons/idk" to="Lobby" method="_idk_clicked"]