commit b0e5e891260e01eca1a2f6467190e4bd8c3fffce Author: Zergling_man Date: Mon Jun 23 20:49:20 2025 +1000 init commit. I should have done it a while ago though 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"]