Compare commits

...

11 Commits

21 changed files with 534 additions and 64 deletions

View File

@ -2,6 +2,7 @@
[ext_resource type="PackedScene" uid="uid://ct70bpbca8one" path="res://scenes/player/local_player.tscn" id="1_yjvui"]
[ext_resource type="PackedScene" uid="uid://kfxc0migw80o" path="res://scenes/player/remote_player.tscn" id="2_kcncr"]
[ext_resource type="PackedScene" uid="uid://dfnbc8sbufa4j" path="res://scenes/misc/ball.tscn" id="3_e0nj3"]
[sub_resource type="ImageTexture" id="ImageTexture_3vyb7"]
@ -14,10 +15,15 @@ size = Vector2(100, 100)
[sub_resource type="BoxShape3D" id="BoxShape3D_1cgct"]
size = Vector3(100, 0.01, 100)
[node name="map" type="Map" unique_id=914577955 node_paths=PackedStringArray("spawner")]
[sub_resource type="BoxShape3D" id="BoxShape3D_3g557"]
size = Vector3(1000, 1, 1000)
[node name="map" type="Map" unique_id=914577955 node_paths=PackedStringArray("spawner", "ball_spawner")]
spawner = NodePath("spawner")
ball_spawner = NodePath("ball_spawner")
local_player = ExtResource("1_yjvui")
remote_player = ExtResource("2_kcncr")
ball = ExtResource("3_e0nj3")
[node name="net_stats_label" type="NetStatsLabel" parent="." unique_id=1821474986]
anchors_preset = 2
@ -44,3 +50,14 @@ transform = Transform3D(1, 0, 0, 0, 0.7274598, 0.6861503, 0, -0.6861503, 0.72745
[node name="spawner" type="Node3D" parent="." unique_id=1364438299]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.028572083, 3.9666176, 0.031027317)
[node name="ball_spawner" type="Node3D" parent="." unique_id=1149426253]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.080344, 4.78083, -9.363691)
[node name="killbox" type="Killbox" parent="." unique_id=1018578438]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5.367467, 0)
collision_layer = 7
collision_mask = 7
[node name="collision_shape_3d" type="CollisionShape3D" parent="killbox" unique_id=447460010]
shape = SubResource("BoxShape3D_3g557")

17
godot/mesh/ball_mesh.tscn Normal file
View File

@ -0,0 +1,17 @@
[gd_scene format=3 uid="uid://r13poamopmnv"]
[sub_resource type="FastNoiseLite" id="FastNoiseLite_yxhor"]
noise_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_yli5f"]
noise = SubResource("FastNoiseLite_yxhor")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3g557"]
albedo_color = Color(0, 1, 0, 1)
albedo_texture = SubResource("NoiseTexture2D_yli5f")
[sub_resource type="SphereMesh" id="SphereMesh_1cgct"]
material = SubResource("StandardMaterial3D_3g557")
[node name="mesh_instance_3d" type="MeshInstance3D" unique_id=37904686]
mesh = SubResource("SphereMesh_1cgct")

View File

@ -1,6 +1,6 @@
[gd_scene format=3 uid="uid://bwd6bj21x310s"]
[ext_resource type="PackedScene" uid="uid://ccd0wj2usx5os" path="res://scenes/player_listing.tscn" id="1_o1atq"]
[ext_resource type="PackedScene" uid="uid://ccd0wj2usx5os" path="res://scenes/ui/player_listing.tscn" id="1_o1atq"]
[sub_resource type="LabelSettings" id="LabelSettings_1cgct"]
font_size = 32

View File

@ -0,0 +1,16 @@
[gd_scene format=3 uid="uid://dfnbc8sbufa4j"]
[ext_resource type="PackedScene" uid="uid://r13poamopmnv" path="res://mesh/ball_mesh.tscn" id="1_texud"]
[sub_resource type="SphereShape3D" id="SphereShape3D_1cgct"]
[node name="ball" type="Ball" unique_id=816748600]
collision_layer = 4
collision_mask = 7
contact_monitor = true
max_contacts_reported = 10
[node name="mesh_instance_3d" parent="." unique_id=37904686 instance=ExtResource("1_texud")]
[node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1408432914]
shape = SubResource("SphereShape3D_1cgct")

View File

@ -0,0 +1,8 @@
[gd_scene format=3 uid="uid://ci8r4lgijtalh"]
[ext_resource type="PackedScene" uid="uid://r13poamopmnv" path="res://mesh/ball_mesh.tscn" id="1_5wscr"]
[node name="ball_weapon" type="BallWeapon" unique_id=53185750]
[node name="mesh_instance_3d" parent="." unique_id=37904686 instance=ExtResource("1_5wscr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.29060435)

View File

@ -8,7 +8,7 @@ glow_enabled = true
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oh8vq"]
[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "collider", "splorch", "dead_cam_target", "respawn_label")]
[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "collider", "splorch", "dead_cam_target", "respawn_label", "ball_origin")]
look_sensitivity = 1.5
camera = NodePath("camera_3d")
move_speed = 5.0
@ -17,6 +17,7 @@ collider = NodePath("collision_shape_3d2")
splorch = NodePath("splorch")
dead_cam_target = NodePath("dead_camera_target")
respawn_label = NodePath("respawn_label")
ball_origin = NodePath("camera_3d")
health = 100
collision_mask = 4

View File

@ -5,13 +5,14 @@
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"]
[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh", "collider", "splorch", "spectator_camera")]
[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh", "collider", "splorch", "spectator_camera", "ball_origin")]
move_speed = 5.0
soldier_mesh = NodePath("soldier_m")
collider = NodePath("collision_shape_3d")
splorch = NodePath("gpu_particles_3d")
health = 100
spectator_camera = NodePath("spectate_root/spectate_camera")
ball_origin = NodePath("ball_origin")
collision_layer = 2
collision_mask = 4
@ -30,3 +31,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7611488, 0)
[node name="spectate_camera" type="Camera3D" parent="spectate_root" unique_id=1408688016]
transform = Transform3D(1, 0, 0, 0, 0.89379096, 0.44848388, 0, -0.44848388, 0.89379096, 0, 0.6601318, 1.0407685)
[node name="ball_origin" type="Node3D" parent="." unique_id=1445680156]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8724979, -0.2644562)

View File

@ -2,6 +2,7 @@
[ext_resource type="PackedScene" uid="uid://cw7duhhjebypt" path="res://scenes/player/raygun.tscn" id="1_a50o4"]
[ext_resource type="PackedScene" uid="uid://ncxcmgdit2j1" path="res://mesh/soldier_m.tscn" id="1_sxefq"]
[ext_resource type="PackedScene" uid="uid://ci8r4lgijtalh" path="res://scenes/player/ball_weapon.tscn" id="2_jbny0"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_a50o4"]
animation = &"Soldier|StrafeLeft"
@ -48,6 +49,7 @@ hand_attachment = NodePath("bone_attachment_3d/hand")
animation_tree = NodePath("animation_tree")
skeleton = NodePath("SoldierM/Soldier/Skeleton3D")
raygun = ExtResource("1_a50o4")
ball = ExtResource("2_jbny0")
look_at = NodePath("look_at_root")
[node name="SoldierM" parent="." unique_id=1868587353 instance=ExtResource("1_sxefq")]
@ -55,10 +57,10 @@ look_at = NodePath("look_at_root")
[node name="Skeleton3D" parent="SoldierM/Soldier" parent_id_path=PackedInt32Array(1868587353, 803584260) index="0" unique_id=1574145981]
bones/0/position = Vector3(0, 0, 0.00071418716)
bones/1/position = Vector3(0, 0, -0.007)
bones/1/rotation = Quaternion(0.6933472, 0, 0, 0.7206036)
bones/1/rotation = Quaternion(0.69402224, 0, 0, 0.7199537)
bones/1/scale = Vector3(1, 1, 1)
bones/2/position = Vector3(0.002, 3.469447e-18, -1.4901118e-10)
bones/2/rotation = Quaternion(0.94524616, 2.1414793e-08, 2.3228983e-08, -0.3263583)
bones/2/rotation = Quaternion(0.9458209, 2.1373747e-08, 2.3266757e-08, -0.3246889)
bones/2/scale = Vector3(1, 1, 1)
bones/3/position = Vector3(0, 0.0069999984, 5.169879e-25)
bones/3/scale = Vector3(1, 0.99999994, 1)
@ -66,7 +68,7 @@ bones/4/position = Vector3(-2.39752e-11, 0.010049875, -1.4156722e-10)
bones/4/rotation = Quaternion(0.73982674, -2.6881349e-08, 7.0005948e-09, 0.67279744)
bones/4/scale = Vector3(1, 0.99999994, 0.99999994)
bones/6/position = Vector3(-0.002, 3.469447e-18, -1.4901205e-10)
bones/6/rotation = Quaternion(0.99965113, 1.8159536e-08, 1.8669295e-08, 0.026414612)
bones/6/rotation = Quaternion(0.9996778, 1.8178714e-08, 1.8650622e-08, 0.025387304)
bones/6/scale = Vector3(1, 1, 1)
bones/7/position = Vector3(0, 0.0069999984, -5.169879e-25)
bones/7/rotation = Quaternion(-0.21973656, 4.264305e-08, 9.604991e-09, 0.9755593)
@ -75,33 +77,33 @@ bones/8/position = Vector3(-2.3975525e-11, 0.010049875, -4.6138777e-11)
bones/8/rotation = Quaternion(0.86758894, -7.867721e-09, 3.036851e-08, 0.4972821)
bones/8/scale = Vector3(1, 0.99999994, 0.99999994)
bones/10/position = Vector3(0, 0.009, 0)
bones/10/rotation = Quaternion(-0.15285265, -0.13164668, 0.01958379, 0.9792455)
bones/10/rotation = Quaternion(-0.15427561, -0.13167602, 0.019766005, 0.97901475)
bones/10/scale = Vector3(1, 1, 1)
bones/11/position = Vector3(6.8186457e-17, 0.008, 0.001)
bones/11/rotation = Quaternion(0.064451575, 0.0001293997, -0.017637832, 0.99776495)
bones/11/rotation = Quaternion(0.058245, 0.0001169397, -0.01593934, 0.99817514)
bones/11/scale = Vector3(1, 1, 1)
bones/12/position = Vector3(-2.2285185e-16, 0.0035, 0.0015000002)
bones/12/rotation = Quaternion(-0.70710677, 0, 0, 0.70710677)
bones/12/scale = Vector3(1, 1, 1)
bones/14/position = Vector3(0.005, 0.005, -1.7336413e-21)
bones/14/rotation = Quaternion(0.8961373, 0.033810798, 0.43093914, -0.10043051)
bones/14/rotation = Quaternion(0.8948131, 0.033457648, 0.43200484, -0.10752621)
bones/14/scale = Vector3(1, 0.99999994, 1)
bones/15/position = Vector3(-1.3005262e-15, 0.0050000004, -6.938894e-18)
bones/15/rotation = Quaternion(1.0371126e-08, -6.748454e-09, 0.9316012, 0.36348197)
bones/15/rotation = Quaternion(1.035401e-08, -6.774687e-09, 0.93067807, 0.3658392)
bones/15/scale = Vector3(1, 1, 1)
bones/16/position = Vector3(7.632945e-11, 0.007071071, 6.938894e-18)
bones/16/rotation = Quaternion(0.0037409808, -0.10074904, -0.18467231, 0.9776154)
bones/16/rotation = Quaternion(0.0062737567, -0.0943689, -0.18486792, 0.978202)
bones/16/scale = Vector3(0.99999994, 0.9999999, 1)
bones/18/position = Vector3(-0.005, 0.005, -1.7303326e-21)
bones/18/rotation = Quaternion(0.6575101, 0.16476046, -0.1328446, -0.7231091)
bones/18/rotation = Quaternion(0.6564217, 0.1684678, -0.13023898, -0.7237176)
bones/18/scale = Vector3(1, 0.99999994, 1)
bones/19/position = Vector3(-1.3005262e-15, 0.0050000004, -6.938894e-18)
bones/19/rotation = Quaternion(-2.1579417e-08, -3.5349842e-08, -0.2771271, 0.9608333)
bones/19/rotation = Quaternion(-2.1246766e-08, -3.5550773e-08, -0.26809898, 0.9633914)
bones/19/scale = Vector3(1, 1, 1)
bones/20/position = Vector3(-2.2286711e-10, 0.007071072, 0)
bones/20/rotation = Quaternion(-0.10949359, -0.472547, -0.13018106, 0.86473316)
bones/20/rotation = Quaternion(-0.112210214, -0.47133064, -0.13382514, 0.8644924)
bones/20/scale = Vector3(1, 0.9999999, 1)
bones/22/position = Vector3(0.010811842, -0.0085772155, 0.0026515652)
bones/22/position = Vector3(0.011061782, -0.0085772155, 0.0025831666)
bones/22/rotation = Quaternion(3.090862e-08, 0.70710677, 0.70710677, 3.090862e-08)
bones/22/scale = Vector3(1, 1, 1)
bones/24/position = Vector3(-0.011000002, 0.0050000013, 0.0070000007)
@ -150,7 +152,7 @@ origin_from = 2
origin_external_node = NodePath("../../../../look_at_root")
[node name="bone_attachment_3d" type="BoneAttachment3D" parent="." unique_id=1964353847]
transform = Transform3D(-35.56741, 3.792272, -93.38403, -83.20849, 44.2147, 33.48737, 42.559406, 89.614044, -12.570532, -0.4718982, 3.3932464, 0.73897976)
transform = Transform3D(-35.07064, 3.824201, -93.57043, -83.59483, 43.760143, 33.120216, 42.213154, 89.83552, -12.150139, -0.47213513, 3.3899364, 0.7408379)
bone_name = "HAND.R_end"
bone_idx = 17
use_external_skeleton = true

82
rust/src/ball.rs Normal file
View File

@ -0,0 +1,82 @@
use godot::{
classes::{IRigidBody3D, RigidBody3D},
prelude::*,
};
use crate::{
net::{network_manager::BallSync, util::NetVector3},
player::{IPlayer, NetTransform},
};
#[derive(GodotClass)]
#[class(base=RigidBody3D, init)]
pub struct Ball {
pub dropper: Option<u16>,
alive_for: f64,
base: Base<RigidBody3D>,
}
#[godot_api]
impl IRigidBody3D for Ball {
fn ready(&mut self) {
self.base()
.signals()
.body_entered()
.connect_other(self, |s, body| s.on_collision(body));
}
fn process(&mut self, delta: f64) {
self.alive_for += delta;
}
}
#[godot_api]
impl Ball {
#[signal]
pub fn on_ball_pickup(player_id: u16);
pub fn get_transform(&self) -> NetTransform {
NetTransform {
location: self.base().get_global_position().into(),
rotation: self.base().get_global_rotation().into(),
}
}
pub fn get_velocity(&self) -> NetVector3 {
self.base().get_linear_velocity().into()
}
fn on_collision(&mut self, node: Gd<Node>) {
if let Ok(player) = node.try_dynify::<dyn IPlayer>()
&& let Some(player_id) = player.dyn_bind().get_player_id()
{
if let Some(dropper_id) = self.dropper
&& dropper_id == player_id
&& self.alive_for < 1.0
{
return;
}
self.run_deferred(move |s| s.signals().on_ball_pickup().emit(player_id));
}
}
pub fn get_sync(&self) -> BallSync {
BallSync {
transform: NetTransform {
location: self.base().get_global_position().into(),
rotation: self.base().get_global_rotation().into(),
},
velocity: self.base().get_linear_velocity().into(),
}
}
pub fn apply_sync(&mut self, sync: BallSync) {
self.base_mut()
.set_global_position(sync.transform.location.into());
self.base_mut()
.set_global_rotation(sync.transform.rotation.into());
self.base_mut().set_linear_velocity(sync.velocity.into());
}
}

View File

@ -4,17 +4,19 @@ use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name};
use serde::{Deserialize, Serialize};
use crate::{
ball::Ball,
map::Map,
map_resource::MapResource,
net::{
network_manager::{
NetworkManager,
BallSync, NetworkManager,
Package::{self, Kill, SelfSync, SpawnPlayer, TakeDamage},
PeerKind, SyncPackage,
},
util::NetVector3,
},
player::{DamageSource, IPlayer, LocalPlayer, NetPlayerTransform, RemotePlayer},
player::{DamageSource, IPlayer, LocalPlayer, NetTransform, RemotePlayer},
weapon::WeaponType,
};
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
@ -96,6 +98,7 @@ pub struct Game {
game_started: bool,
pub current_map: Option<Gd<Map>>,
pub world_ball: Option<Gd<Ball>>,
since_last_sync: f64,
base: Base<Node>,
@ -144,7 +147,10 @@ impl INode for Game {
);
}
}
peer.broadcast_unreliable(Package::Sync(sync_data));
peer.broadcast_unreliable(Package::Sync(
sync_data,
self.world_ball.as_ref().map(|b| b.bind().get_sync()),
));
}
}
}
@ -300,11 +306,92 @@ impl Game {
for player in s.players.clone() {
s.spawn_player_server(player.id);
}
s.spawn_ball(None, None, None);
});
}
}
}
pub fn spawn_ball(
&mut self,
transform: Option<NetTransform>,
velocity: Option<NetVector3>,
player_id: Option<u16>,
) {
if let Some(map) = &mut self.current_map {
let ball = map.bind_mut().spawn_ball(transform, velocity, player_id);
if let Some(ball) = &ball {
self.world_ball = Some(ball.clone());
ball.signals()
.on_ball_pickup()
.connect_other(self, |s, player_id| s.handle_ball_pickup(player_id));
}
self.run_deferred(move |_| {
if let Some(ball) = ball
&& let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
peer.broadcast_reliable(Package::SpawnBall(
ball.bind().get_transform(),
ball.bind().get_velocity(),
player_id,
));
}
});
}
}
pub fn despawn_ball(&mut self) {
if let Some(mut ball) = self.world_ball.take() {
ball.queue_free();
self.run_deferred(|_| {
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
peer.broadcast_reliable(Package::DespawnBall);
}
});
}
}
pub fn respawn_ball(&mut self) {
self.despawn_ball();
self.spawn_ball(None, None, None);
}
pub fn handle_ball_pickup(&mut self, player_id: u16) {
if NetworkManager::singleton().bind_mut().is_host() {
self.despawn_ball();
self.run_deferred(move |s| s.handle_swap_weapon(player_id, WeaponType::Ball, true));
}
}
// Handle a swap-weapon command package
pub fn handle_swap_weapon(
&mut self,
player_id: u16,
weapon_type: WeaponType,
allow_ball: bool,
) {
if weapon_type == WeaponType::Ball && !allow_ball {
return;
}
if let Some(player) = self.find_player_mut(player_id)
&& let Some(character) = &mut player.character
{
character.dyn_bind_mut().swap_weapon(weapon_type);
let player_id = player.id;
self.run_deferred(move |_| {
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
peer.broadcast_reliable(Package::SwapWeapon(player_id, weapon_type));
}
});
}
}
pub fn spawn_player_server(&mut self, id: u16) -> Option<DynGd<CharacterBody3D, dyn IPlayer>> {
if let Some(player) = self.find_player_mut(id)
&& let Some(character) = &mut player.character
@ -360,7 +447,7 @@ impl Game {
}
// Method called on the client-side whenever a player has spawned
pub fn spawn_player_client(&mut self, id: u16, transform: NetPlayerTransform) {
pub fn spawn_player_client(&mut self, id: u16, transform: NetTransform) {
if let Some(map) = &mut self.current_map {
if let Some(player) = self.players.iter_mut().find(|p| p.id == id) {
if let Some(character) = &mut player.character {
@ -379,7 +466,11 @@ impl Game {
}
// Handle a sync package from the server
pub fn handle_sync(&mut self, sync_data: HashMap<u16, SyncPackage>) {
pub fn handle_sync(
&mut self,
sync_data: HashMap<u16, SyncPackage>,
ball_sync: Option<BallSync>,
) {
for (
player_id,
SyncPackage {
@ -407,6 +498,12 @@ impl Game {
}
}
}
if let Some(ball_sync) = ball_sync
&& let Some(ball) = &mut self.world_ball
{
ball.bind_mut().apply_sync(ball_sync);
}
}
// Handle a jump, either from the server or from a client
@ -423,7 +520,7 @@ impl Game {
&mut self,
player_id: u16,
to: Vector3,
transform: NetPlayerTransform,
transform: NetTransform,
look_up: f32,
deal_damage: bool,
) -> bool {
@ -487,7 +584,7 @@ pub struct Player {
}
impl Player {
pub fn get_transform(&self) -> Option<NetPlayerTransform> {
pub fn get_transform(&self) -> Option<NetTransform> {
if let Some(character) = &self.character {
Some(character.dyn_bind().get_transform())
} else {
@ -495,7 +592,7 @@ impl Player {
}
}
pub fn set_transform(&mut self, transform: NetPlayerTransform) {
pub fn set_transform(&mut self, transform: NetTransform) {
if let Some(character) = &mut self.character {
character.dyn_bind_mut().apply_transform(transform);
}
@ -562,7 +659,7 @@ impl Player {
pub fn try_shoot(
&mut self,
to: Vector3,
transform: NetPlayerTransform,
transform: NetTransform,
look_up: f32,
deal_damage: bool,
) -> bool {

View File

@ -4,6 +4,8 @@ use godot::{
};
use crate::{
ball::Ball,
game_manager::Game,
net::network_manager::NetworkManager,
player::{DamageSource, IPlayer},
};
@ -26,12 +28,16 @@ impl IArea3D for Killbox {
impl Killbox {
pub fn kill(&mut self, node: Gd<Node3D>) {
if let Ok(mut player) = node.try_dynify::<dyn IPlayer>() {
if let Ok(mut player) = node.clone().try_dynify::<dyn IPlayer>() {
if NetworkManager::singleton().bind().is_host() {
player
.dyn_bind_mut()
.take_damage(DamageSource::Killbox, 1000, true);
}
} else if let Ok(_) = node.try_cast::<Ball>() {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().respawn_ball();
}
}
}
}

View File

@ -1,5 +1,6 @@
use godot::prelude::*;
pub mod ball;
pub mod game_manager;
pub mod killbox;
pub mod map;

View File

@ -1,8 +1,10 @@
use godot::{classes::CharacterBody3D, prelude::*};
use crate::{
ball::Ball,
game_manager::Game,
player::{IPlayer, LocalPlayer, NetPlayerTransform, RemotePlayer},
net::util::NetVector3,
player::{IPlayer, LocalPlayer, NetTransform, RemotePlayer},
};
#[derive(GodotClass)]
@ -11,9 +13,13 @@ pub struct Map {
#[export]
pub spawner: Option<Gd<Node3D>>,
#[export]
pub ball_spawner: Option<Gd<Node3D>>,
#[export]
pub local_player: Option<Gd<PackedScene>>,
#[export]
pub remote_player: Option<Gd<PackedScene>>,
#[export]
pub ball: Option<Gd<PackedScene>>,
base: Base<Node3D>,
}
@ -36,7 +42,7 @@ impl Map {
&mut self,
player_id: u16,
is_local: bool,
transform: Option<NetPlayerTransform>,
transform: Option<NetTransform>,
) -> Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>> {
if is_local {
if let Some(local_player) = &self.local_player {
@ -84,4 +90,31 @@ impl Map {
None
}
}
pub fn spawn_ball(
&mut self,
transform: Option<NetTransform>,
velocity: Option<NetVector3>,
player_id: Option<u16>,
) -> Option<Gd<Ball>> {
if let Some(ball_spawner) = self.ball_spawner.clone()
&& let Some(ball_prefab) = &self.ball
{
let mut ball = ball_prefab.instantiate_as::<Ball>();
ball.bind_mut().dropper = player_id;
self.base_mut().add_child(&ball);
if let Some(transform) = transform {
ball.set_global_position(transform.location.into());
ball.set_global_rotation(transform.rotation.into());
} else {
ball.set_global_position(ball_spawner.get_global_position());
}
if let Some(velocity) = velocity {
ball.set_linear_velocity(velocity.into());
}
Some(ball)
} else {
None
}
}
}

View File

@ -11,8 +11,9 @@ use teanet::{Peer, PeerConfig};
use crate::{
game_manager::{Game, GameManager, NetPlayer},
net::{net_stats::Stats, util::NetVector3},
player::{DamageSource, NetPlayerTransform},
player::{DamageSource, NetTransform},
ui::cli::{CliColor, CommandLinePanel},
weapon::WeaponType,
};
#[derive(GodotClass)]
@ -217,9 +218,9 @@ impl INode for NetworkManager {
game.bind_mut().spawn_player_client(id, transform);
}
}
Package::Sync(sync_data) => {
Package::Sync(sync_data, ball_sync) => {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().handle_sync(sync_data);
game.bind_mut().handle_sync(sync_data, ball_sync);
}
}
Package::Jump(player_id) => {
@ -257,6 +258,29 @@ impl INode for NetworkManager {
.handle_kill(source_player_id, target_player_id);
}
}
Package::SpawnBall(transform, velocity, player_id) => {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().spawn_ball(
Some(transform),
Some(velocity),
player_id,
);
}
}
Package::DespawnBall => {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().despawn_ball();
}
}
Package::SwapWeapon(id, weapon_type) => {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().handle_swap_weapon(
id,
weapon_type,
true,
);
}
}
_ => {}
},
}
@ -567,20 +591,29 @@ pub enum Package {
MapLoaded,
// Game packages
SpawnPlayer(u16, NetPlayerTransform),
Sync(HashMap<u16, SyncPackage>),
SpawnPlayer(u16, NetTransform),
Sync(HashMap<u16, SyncPackage>, Option<BallSync>),
SelfSync(SyncPackage),
Jump(u16),
Shoot(u16, NetVector3, NetPlayerTransform, f32),
Shoot(u16, NetVector3, NetTransform, f32),
TakeDamage(DamageSource, u16, i32),
Kill(DamageSource, u16),
SpawnBall(NetTransform, NetVector3, Option<u16>),
DespawnBall,
SwapWeapon(u16, WeaponType),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncPackage {
pub movement_direction: NetVector3,
pub look_up: f32,
pub transform: NetPlayerTransform,
pub transform: NetTransform,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BallSync {
pub transform: NetTransform,
pub velocity: NetVector3,
}
pub enum PeerKind {

View File

@ -15,18 +15,18 @@ use crate::{
net::{
network_manager::{
NetworkManager,
Package::{Jump, Shoot},
Package::{self, Jump, Shoot},
PeerKind,
},
util::{NetVector3, cast_ray},
},
soldier_mesh::SoldierMesh,
ui::cli::CommandLinePanel,
weapon::Weapon,
weapon::{BallWeapon, Weapon, WeaponType},
};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct NetPlayerTransform {
pub struct NetTransform {
pub location: NetVector3,
pub rotation: NetVector3,
}
@ -38,21 +38,27 @@ pub trait IPlayer {
fn get_movement_dir(&self) -> Vector3;
fn set_movement_dir(&mut self, dir: Vector3);
fn get_move_speed(&self) -> f32;
fn apply_transform(&mut self, transform: NetPlayerTransform);
fn apply_transform(&mut self, transform: NetTransform);
fn apply_rotation(&mut self, rot: Vector3);
fn apply_look_up(&mut self, rot: f32);
fn get_look_up(&self) -> f32;
fn get_transform(&self) -> NetPlayerTransform;
fn get_transform(&self) -> NetTransform;
fn jump(&mut self);
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool;
fn take_damage(&mut self, source_player: DamageSource, damage: i32, kill: bool);
fn is_dead(&self) -> bool;
fn kill(&mut self, source_player: DamageSource);
fn dead_for(&self) -> f64;
fn swap_weapon(&mut self, weapon: WeaponType);
fn get_weapon(&self) -> Option<DynGd<Node3D, dyn Weapon>>;
fn drop_ball(&mut self, with_velocity: bool) -> bool;
fn get_ball_origin(&self) -> Option<Gd<Node3D>>;
}
pub trait PlayerMovement {
fn player_update(&mut self, delta: f64);
fn drop_ball_common(&mut self, with_velocity: bool) -> bool;
fn get_look_dir(&self) -> Vector3;
}
impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
@ -77,6 +83,53 @@ impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
self.base_mut().move_and_slide();
}
}
fn drop_ball_common(&mut self, with_velocity: bool) -> bool {
if let Some(weapon) = self.get_weapon()
&& let Ok(_) = weapon.try_cast::<BallWeapon>()
{
self.run_deferred(move |s| {
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
s.swap_weapon(WeaponType::Raygun);
peer.broadcast_reliable(Package::SwapWeapon(
s.get_player_id().unwrap_or(0),
WeaponType::Raygun,
));
if let Some(mut game) = Game::singleton() {
if let Some(ball_origin) = &s.get_ball_origin() {
let velocity = if with_velocity {
s.get_look_dir() * 20.
} else {
Vector3::ZERO
};
game.bind_mut().spawn_ball(
Some(NetTransform {
location: ball_origin.get_global_position().into(),
rotation: ball_origin.get_global_rotation().into(),
}),
Some(velocity.into()),
s.get_player_id(),
);
}
}
}
});
true
} else {
false
}
}
fn get_look_dir(&self) -> Vector3 {
if let Some(ball_origin) = self.get_ball_origin() {
ball_origin.get_global_basis().col_c().neg()
} else {
Vector3::ZERO
}
}
}
#[derive(GodotClass)]
@ -99,10 +152,11 @@ pub struct LocalPlayer {
#[export]
respawn_label: Option<Gd<Label>>,
#[export]
ball_origin: Option<Gd<Node3D>>,
#[export]
health: i32,
pub player_id: Option<u16>,
pub weapon: Option<DynGd<Node3D, dyn Weapon>>,
movement_dir: Vector3,
y_speed: f32,
@ -143,7 +197,7 @@ impl IPlayer for LocalPlayer {
self.move_speed
}
fn apply_transform(&mut self, transform: NetPlayerTransform) {
fn apply_transform(&mut self, transform: NetTransform) {
self.base_mut()
.set_global_position(transform.location.into());
self.base_mut()
@ -152,8 +206,8 @@ impl IPlayer for LocalPlayer {
fn apply_rotation(&mut self, _rot: Vector3) {}
fn get_transform(&self) -> NetPlayerTransform {
NetPlayerTransform {
fn get_transform(&self) -> NetTransform {
NetTransform {
location: self.base().get_global_position().into(),
rotation: self.base().get_global_rotation().into(),
}
@ -182,9 +236,13 @@ impl IPlayer for LocalPlayer {
return false;
}
if self.drop_ball(true) {
return true;
}
if self.shoot_cd <= 0. {
let rid = self.base().get_rid();
if let Some(weapon) = &mut self.weapon {
if let Some(mut weapon) = self.get_weapon() {
weapon
.dyn_bind_mut()
.shoot(rid, self.player_id.unwrap_or(0), to, deal_damage);
@ -219,9 +277,6 @@ impl IPlayer for LocalPlayer {
if let Some(mut mesh) = self.soldier_mesh.take() {
mesh.queue_free();
}
if let Some(mut weapon) = self.weapon.take() {
weapon.queue_free();
}
if let Some(mut collider) = self.collider.take() {
collider.queue_free();
}
@ -233,6 +288,29 @@ impl IPlayer for LocalPlayer {
self.killer_id = Some(player_id);
}
}
fn swap_weapon(&mut self, weapon: WeaponType) {
if let Some(mesh) = &mut self.soldier_mesh {
match weapon {
WeaponType::Raygun => mesh.bind_mut().spawn_raygun(),
WeaponType::Ball => mesh.bind_mut().spawn_ball(),
};
}
}
fn get_weapon(&self) -> Option<DynGd<Node3D, dyn Weapon>> {
self.soldier_mesh
.as_ref()
.and_then(|m| m.bind().get_weapon())
}
fn drop_ball(&mut self, with_velocity: bool) -> bool {
self.drop_ball_common(with_velocity)
}
fn get_ball_origin(&self) -> Option<Gd<Node3D>> {
self.ball_origin.clone()
}
}
#[godot_api]
@ -253,9 +331,7 @@ impl ICharacterBody3D for LocalPlayer {
s.set_lock(true);
});
if let Some(mesh) = &mut self.soldier_mesh {
self.weapon = mesh.bind_mut().spawn_raygun();
}
self.swap_weapon(WeaponType::Raygun);
if let Some(camera) = &mut self.camera {
camera.set_current(true);
@ -428,9 +504,10 @@ pub struct RemotePlayer {
health: i32,
#[export]
spectator_camera: Option<Gd<Camera3D>>,
#[export]
ball_origin: Option<Gd<Node3D>>,
pub player_id: Option<u16>,
pub weapon: Option<DynGd<Node3D, dyn Weapon>>,
movement_dir: Vector3,
y_speed: f32,
@ -470,7 +547,7 @@ impl IPlayer for RemotePlayer {
self.player_id
}
fn apply_transform(&mut self, transform: NetPlayerTransform) {
fn apply_transform(&mut self, transform: NetTransform) {
self.base_mut()
.set_global_position(transform.location.into());
self.base_mut()
@ -481,8 +558,8 @@ impl IPlayer for RemotePlayer {
self.base_mut().set_global_rotation(rot);
}
fn get_transform(&self) -> NetPlayerTransform {
NetPlayerTransform {
fn get_transform(&self) -> NetTransform {
NetTransform {
location: self.base().get_global_position().into(),
rotation: self.base().get_global_rotation().into(),
}
@ -505,9 +582,15 @@ impl IPlayer for RemotePlayer {
return false;
}
if self.drop_ball(true) {
return true;
}
if self.shoot_cd <= 0. {
let rid = self.base().get_rid();
if let Some(weapon) = &mut self.weapon {
if let Some(mesh) = &mut self.soldier_mesh
&& let Some(mut weapon) = mesh.bind().get_weapon()
{
weapon
.dyn_bind_mut()
.shoot(rid, self.player_id.unwrap_or(0), to, deal_damage);
@ -542,9 +625,6 @@ impl IPlayer for RemotePlayer {
if let Some(mut mesh) = self.soldier_mesh.take() {
mesh.queue_free();
}
if let Some(mut weapon) = self.weapon.take() {
weapon.queue_free();
}
if let Some(mut collider) = self.collider.take() {
collider.queue_free();
}
@ -553,14 +633,35 @@ impl IPlayer for RemotePlayer {
}
self.is_dead = true;
}
fn swap_weapon(&mut self, weapon: WeaponType) {
if let Some(mesh) = &mut self.soldier_mesh {
match weapon {
WeaponType::Raygun => mesh.bind_mut().spawn_raygun(),
WeaponType::Ball => mesh.bind_mut().spawn_ball(),
};
}
}
fn get_weapon(&self) -> Option<DynGd<Node3D, dyn Weapon>> {
self.soldier_mesh
.as_ref()
.and_then(|m| m.bind().get_weapon())
}
fn drop_ball(&mut self, with_velocity: bool) -> bool {
self.drop_ball_common(with_velocity)
}
fn get_ball_origin(&self) -> Option<Gd<Node3D>> {
self.ball_origin.clone()
}
}
#[godot_api]
impl ICharacterBody3D for RemotePlayer {
fn ready(&mut self) {
if let Some(mesh) = &mut self.soldier_mesh {
self.weapon = mesh.bind_mut().spawn_raygun();
}
self.swap_weapon(WeaponType::Raygun);
}
fn process(&mut self, delta: f64) {
@ -578,6 +679,10 @@ impl ICharacterBody3D for RemotePlayer {
mesh.bind_mut().update_y_look(self.look_up);
}
if let Some(ball_origin) = &mut self.ball_origin {
ball_origin.set_rotation(Vector3::RIGHT * self.look_up);
}
}
}

View File

@ -3,7 +3,7 @@ use godot::{
prelude::*,
};
use crate::weapon::{Raygun, Weapon};
use crate::weapon::{BallWeapon, Raygun, Weapon};
#[derive(GodotClass)]
#[class(base=Node3D, init)]
@ -17,8 +17,12 @@ pub struct SoldierMesh {
#[export]
raygun: Option<Gd<PackedScene>>,
#[export]
ball: Option<Gd<PackedScene>>,
#[export]
look_at: Option<Gd<Node3D>>,
pub current_weapon: Option<DynGd<Node3D, dyn Weapon>>,
base: Base<Node3D>,
}
@ -49,15 +53,32 @@ impl SoldierMesh {
}
}
pub fn spawn_ball(&mut self) -> Option<DynGd<Node3D, dyn Weapon>> {
if let Some(ball) = &self.ball {
let node = ball.instantiate_as::<BallWeapon>();
self.spawn_weapon(node.into_dyn().upcast())
} else {
None
}
}
fn spawn_weapon(
&mut self,
node: DynGd<Node3D, dyn Weapon>,
) -> Option<DynGd<Node3D, dyn Weapon>> {
if let Some(mut weapon) = self.current_weapon.take() {
weapon.queue_free();
}
if let Some(hand) = &mut self.hand_attachment {
hand.add_child(&node);
self.current_weapon = Some(node.clone());
Some(node)
} else {
None
}
}
pub fn get_weapon(&self) -> Option<DynGd<Node3D, dyn Weapon>> {
self.current_weapon.clone()
}
}

View File

@ -1,4 +1,5 @@
use godot::{classes::AudioStreamPlayer3D, prelude::*};
use serde::{Deserialize, Serialize};
use crate::{
game_manager::Game,
@ -8,6 +9,13 @@ use crate::{
pub trait Weapon {
fn shoot(&mut self, player_rid: Rid, player_id: u16, to: Vector3, deal_damage: bool);
fn get_type(&self) -> WeaponType;
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, PartialOrd)]
pub enum WeaponType {
Raygun,
Ball,
}
#[derive(GodotClass)]
@ -68,6 +76,10 @@ impl Weapon for Raygun {
}
}
}
fn get_type(&self) -> WeaponType {
WeaponType::Raygun
}
}
#[derive(GodotClass)]
@ -113,3 +125,18 @@ impl Beam {
}
}
}
#[derive(GodotClass)]
#[class(base=Node3D, init)]
pub struct BallWeapon {
base: Base<Node3D>,
}
#[godot_dyn]
impl Weapon for BallWeapon {
fn shoot(&mut self, player_rid: Rid, player_id: u16, to: Vector3, deal_damage: bool) {}
fn get_type(&self) -> WeaponType {
WeaponType::Ball
}
}