Compare commits
10 Commits
79f223cb21
...
b2dbd2deee
| Author | SHA1 | Date | |
|---|---|---|---|
| b2dbd2deee | |||
| 18f298bf10 | |||
| a2cf76701f | |||
| 0678c56dae | |||
| d4f4dc43b0 | |||
| 33095ec1b9 | |||
| 0d7f5a6bf6 | |||
| c588b7cab4 | |||
| 9c412ef2ed | |||
| 95e7c6ded4 |
@ -1,19 +1,28 @@
|
|||||||
[gd_scene format=3 uid="uid://ct70bpbca8one"]
|
[gd_scene format=3 uid="uid://ct70bpbca8one"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_jd651"]
|
[ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_jd651"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cl66unms2gim0" path="res://scenes/player/splorch.tscn" id="2_ywmwa"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="Environment_3nx84"]
|
||||||
|
glow_enabled = true
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oh8vq"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oh8vq"]
|
||||||
|
|
||||||
[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh")]
|
[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "collider", "splorch")]
|
||||||
look_sensitivity = 1.5
|
look_sensitivity = 1.5
|
||||||
camera = NodePath("camera_3d")
|
camera = NodePath("camera_3d")
|
||||||
move_speed = 5.0
|
move_speed = 5.0
|
||||||
soldier_mesh = NodePath("soldier_m")
|
soldier_mesh = NodePath("soldier_m")
|
||||||
|
collider = NodePath("collision_shape_3d2")
|
||||||
|
splorch = NodePath("splorch")
|
||||||
|
health = 100
|
||||||
collision_mask = 4
|
collision_mask = 4
|
||||||
|
|
||||||
[node name="camera_3d" type="Camera3D" parent="." unique_id=1773137418]
|
[node name="camera_3d" type="Camera3D" parent="." unique_id=1773137418]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8724979, -0.2644562)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8724979, -0.2644562)
|
||||||
|
environment = SubResource("Environment_3nx84")
|
||||||
current = true
|
current = true
|
||||||
|
fov = 90.0
|
||||||
|
|
||||||
[node name="soldier_m" parent="." unique_id=281995630 instance=ExtResource("1_jd651")]
|
[node name="soldier_m" parent="." unique_id=281995630 instance=ExtResource("1_jd651")]
|
||||||
transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5, 0.15878421, 0, 0.19227558)
|
transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5, 0.15878421, 0, 0.19227558)
|
||||||
@ -21,3 +30,6 @@ transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5
|
|||||||
[node name="collision_shape_3d2" type="CollisionShape3D" parent="." unique_id=442150782]
|
[node name="collision_shape_3d2" type="CollisionShape3D" parent="." unique_id=442150782]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0310049, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0310049, 0)
|
||||||
shape = SubResource("CapsuleShape3D_oh8vq")
|
shape = SubResource("CapsuleShape3D_oh8vq")
|
||||||
|
|
||||||
|
[node name="splorch" parent="." unique_id=986677517 instance=ExtResource("2_ywmwa")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.16157162, 1.4223524, 0)
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
[gd_scene format=3 uid="uid://cw7duhhjebypt"]
|
[gd_scene format=3 uid="uid://cw7duhhjebypt"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://cg81784ct8gf5" path="res://mesh/raygun.tscn" id="1_ckadf"]
|
[ext_resource type="PackedScene" uid="uid://cg81784ct8gf5" path="res://mesh/raygun.tscn" id="1_ckadf"]
|
||||||
[ext_resource type="PackedScene" uid="uid://ctslratcr8h6v" path="res://scenes/player/raygun_bullet.tscn" id="1_m84jv"]
|
[ext_resource type="PackedScene" uid="uid://ctslratcr8h6v" path="res://scenes/player/raygun_beam.tscn" id="1_m84jv"]
|
||||||
[ext_resource type="AudioStream" uid="uid://xxpkjhaovyd" path="res://assets/LaserWithRecoveryEdited.ogg" id="2_lxble"]
|
[ext_resource type="AudioStream" uid="uid://xxpkjhaovyd" path="res://assets/LaserWithRecoveryEdited.ogg" id="2_lxble"]
|
||||||
|
|
||||||
[node name="raygun" type="Weapon" unique_id=751705927 node_paths=PackedStringArray("sound_emitter", "bullet_source")]
|
[node name="raygun" type="Raygun" unique_id=573018429 node_paths=PackedStringArray("sound_emitter", "bullet_source")]
|
||||||
sound_emitter = NodePath("sound_emitter")
|
sound_emitter = NodePath("sound_emitter")
|
||||||
bullet_prefab = ExtResource("1_m84jv")
|
bullet_prefab = ExtResource("1_m84jv")
|
||||||
bullet_source = NodePath("bullet_source")
|
bullet_source = NodePath("bullet_source")
|
||||||
|
|||||||
22
godot/scenes/player/raygun_beam.tscn
Normal file
22
godot/scenes/player/raygun_beam.tscn
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[gd_scene format=3 uid="uid://ctslratcr8h6v"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n6c2q"]
|
||||||
|
shading_mode = 2
|
||||||
|
disable_specular_occlusion = true
|
||||||
|
metallic_specular = 1.0
|
||||||
|
roughness = 0.0
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(1, 0, 1, 1)
|
||||||
|
emission_energy_multiplier = 10.0
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="CylinderMesh_1cgct"]
|
||||||
|
material = SubResource("StandardMaterial3D_n6c2q")
|
||||||
|
top_radius = 0.2
|
||||||
|
bottom_radius = 0.2
|
||||||
|
height = 1.0
|
||||||
|
|
||||||
|
[node name="raygun_beam" type="Beam" unique_id=2002340444]
|
||||||
|
|
||||||
|
[node name="mesh_instance_3d" type="MeshInstance3D" parent="." unique_id=1295653371]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 0, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_1cgct")
|
||||||
@ -1,40 +0,0 @@
|
|||||||
[gd_scene format=3 uid="uid://ctslratcr8h6v"]
|
|
||||||
|
|
||||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_3g557"]
|
|
||||||
particle_flag_align_y = true
|
|
||||||
radial_velocity_min = -500.0
|
|
||||||
radial_velocity_max = -500.0
|
|
||||||
scale_over_velocity_max = 1.0
|
|
||||||
|
|
||||||
[sub_resource type="Curve" id="Curve_dmbod"]
|
|
||||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
|
||||||
point_count = 2
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yxhor"]
|
|
||||||
emission_enabled = true
|
|
||||||
emission = Color(1, 0, 1, 1)
|
|
||||||
emission_energy_multiplier = 4.47
|
|
||||||
use_particle_trails = true
|
|
||||||
|
|
||||||
[sub_resource type="TubeTrailMesh" id="TubeTrailMesh_m77kh"]
|
|
||||||
material = SubResource("StandardMaterial3D_yxhor")
|
|
||||||
sections = 2
|
|
||||||
section_length = 0.5
|
|
||||||
section_rings = 5
|
|
||||||
cap_top = false
|
|
||||||
cap_bottom = false
|
|
||||||
curve = SubResource("Curve_dmbod")
|
|
||||||
|
|
||||||
[node name="raygun_bullet" type="Bullet" unique_id=1238833478 node_paths=PackedStringArray("emitter")]
|
|
||||||
emitter = NodePath("gpu_particles_3d")
|
|
||||||
|
|
||||||
[node name="gpu_particles_3d" type="GPUParticles3D" parent="." unique_id=1330763726]
|
|
||||||
emitting = false
|
|
||||||
amount = 1
|
|
||||||
lifetime = 3.8899999999999997
|
|
||||||
one_shot = true
|
|
||||||
fixed_fps = 100
|
|
||||||
trail_enabled = true
|
|
||||||
trail_lifetime = 0.5
|
|
||||||
process_material = SubResource("ParticleProcessMaterial_3g557")
|
|
||||||
draw_pass_1 = SubResource("TubeTrailMesh_m77kh")
|
|
||||||
@ -1,12 +1,16 @@
|
|||||||
[gd_scene format=3 uid="uid://kfxc0migw80o"]
|
[gd_scene format=3 uid="uid://kfxc0migw80o"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_e2ekk"]
|
[ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_e2ekk"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cl66unms2gim0" path="res://scenes/player/splorch.tscn" id="2_orxjf"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"]
|
||||||
|
|
||||||
[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh")]
|
[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh", "collider", "splorch")]
|
||||||
move_speed = 5.0
|
move_speed = 5.0
|
||||||
soldier_mesh = NodePath("soldier_m")
|
soldier_mesh = NodePath("soldier_m")
|
||||||
|
collider = NodePath("collision_shape_3d")
|
||||||
|
splorch = NodePath("gpu_particles_3d")
|
||||||
|
health = 100
|
||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
collision_mask = 4
|
collision_mask = 4
|
||||||
|
|
||||||
@ -16,3 +20,6 @@ transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5
|
|||||||
[node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1110995513]
|
[node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1110995513]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0310049, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0310049, 0)
|
||||||
shape = SubResource("CapsuleShape3D_1cgct")
|
shape = SubResource("CapsuleShape3D_1cgct")
|
||||||
|
|
||||||
|
[node name="gpu_particles_3d" parent="." unique_id=986677517 instance=ExtResource("2_orxjf")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.4540546, 0)
|
||||||
|
|||||||
34
godot/scenes/player/splorch.tscn
Normal file
34
godot/scenes/player/splorch.tscn
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cl66unms2gim0"]
|
||||||
|
|
||||||
|
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_1cgct"]
|
||||||
|
particle_flag_align_y = true
|
||||||
|
direction = Vector3(0, 1, 0)
|
||||||
|
spread = 90.0
|
||||||
|
initial_velocity_min = 2.0
|
||||||
|
initial_velocity_max = 5.0
|
||||||
|
|
||||||
|
[sub_resource type="Curve" id="Curve_yli5f"]
|
||||||
|
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.08643617, 1), -3.6585922, -3.6585922, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||||
|
point_count = 3
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_dmbod"]
|
||||||
|
albedo_color = Color(0.44894964, 1.3055467e-06, 9.62615e-08, 1)
|
||||||
|
use_particle_trails = true
|
||||||
|
|
||||||
|
[sub_resource type="TubeTrailMesh" id="TubeTrailMesh_wwb7k"]
|
||||||
|
material = SubResource("StandardMaterial3D_dmbod")
|
||||||
|
radius = 0.1
|
||||||
|
radial_steps = 20
|
||||||
|
section_length = 0.1
|
||||||
|
section_rings = 10
|
||||||
|
curve = SubResource("Curve_yli5f")
|
||||||
|
|
||||||
|
[node name="gpu_particles_3d" type="GPUParticles3D" unique_id=986677517]
|
||||||
|
emitting = false
|
||||||
|
amount = 20
|
||||||
|
one_shot = true
|
||||||
|
explosiveness = 0.78
|
||||||
|
trail_enabled = true
|
||||||
|
trail_lifetime = 0.5
|
||||||
|
process_material = SubResource("ParticleProcessMaterial_1cgct")
|
||||||
|
draw_pass_1 = SubResource("TubeTrailMesh_wwb7k")
|
||||||
@ -9,12 +9,12 @@ use crate::{
|
|||||||
net::{
|
net::{
|
||||||
network_manager::{
|
network_manager::{
|
||||||
NetworkManager,
|
NetworkManager,
|
||||||
Package::{self, SelfSync},
|
Package::{self, Kill, SelfSync, TakeDamage},
|
||||||
PeerKind, SyncPackage,
|
PeerKind, SyncPackage,
|
||||||
},
|
},
|
||||||
util::NetVector3,
|
util::NetVector3,
|
||||||
},
|
},
|
||||||
player::{IPlayer, NetPlayerTransform},
|
player::{IPlayer, LocalPlayer, NetPlayerTransform, RemotePlayer},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
||||||
@ -117,6 +117,11 @@ impl INode for Game {
|
|||||||
PeerKind::Server(peer, _) => {
|
PeerKind::Server(peer, _) => {
|
||||||
let mut sync_data = HashMap::new();
|
let mut sync_data = HashMap::new();
|
||||||
for player in &self.players {
|
for player in &self.players {
|
||||||
|
if let Some(character) = &player.character {
|
||||||
|
if character.dyn_bind().is_dead() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if let (Some(dir), Some(look_up), Some(transform)) = (
|
if let (Some(dir), Some(look_up), Some(transform)) = (
|
||||||
player.get_move_dir(),
|
player.get_move_dir(),
|
||||||
player.get_look_up(),
|
player.get_look_up(),
|
||||||
@ -266,22 +271,61 @@ impl Game {
|
|||||||
peer.send_reliable(to, Package::MapLoaded);
|
peer.send_reliable(to, Package::MapLoaded);
|
||||||
} else {
|
} else {
|
||||||
self.run_deferred(|s| {
|
self.run_deferred(|s| {
|
||||||
for player in &mut s.players {
|
for player in s.players.clone() {
|
||||||
if let Some(map) = &mut s.current_map {
|
let player_id = player.id;
|
||||||
let character = map.bind_mut().spawn_player(
|
if let Some(mut map) = s.current_map.clone() {
|
||||||
player,
|
let mut character = map.bind_mut().spawn_player(
|
||||||
|
&player,
|
||||||
player.id == s.self_id.unwrap_or(0),
|
player.id == s.self_id.unwrap_or(0),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(character) = &mut character {
|
||||||
|
if let Ok(mut local_player) =
|
||||||
|
character.clone().try_cast::<LocalPlayer>()
|
||||||
|
{
|
||||||
|
local_player
|
||||||
|
.bind_mut()
|
||||||
|
.signals()
|
||||||
|
.on_die()
|
||||||
|
.connect_other(s, move |s| s.on_death(player_id));
|
||||||
|
local_player
|
||||||
|
.bind_mut()
|
||||||
|
.signals()
|
||||||
|
.on_take_damage()
|
||||||
|
.connect_other(s, move |s, damage| {
|
||||||
|
s.on_take_damage(player_id, damage)
|
||||||
|
});
|
||||||
|
} else if let Ok(mut remote_player) =
|
||||||
|
character.clone().try_cast::<RemotePlayer>()
|
||||||
|
{
|
||||||
|
remote_player
|
||||||
|
.bind_mut()
|
||||||
|
.signals()
|
||||||
|
.on_die()
|
||||||
|
.connect_other(s, move |s| s.on_death(player_id));
|
||||||
|
remote_player
|
||||||
|
.bind_mut()
|
||||||
|
.signals()
|
||||||
|
.on_take_damage()
|
||||||
|
.connect_other(s, move |s, damage| {
|
||||||
|
s.on_take_damage(player_id, damage)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(player) = s.find_player_mut(player_id) {
|
||||||
player.character = character;
|
player.character = character;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_player(&mut self, id: u16, transform: NetPlayerTransform) {
|
// Method called on the client-side whenever a player has spawned
|
||||||
|
pub fn spawn_player_client(&mut self, id: u16, transform: NetPlayerTransform) {
|
||||||
if let Some(map) = &mut self.current_map {
|
if let Some(map) = &mut self.current_map {
|
||||||
if let Some(player) = self.players.iter_mut().find(|p| p.id == id) {
|
if let Some(player) = self.players.iter_mut().find(|p| p.id == id) {
|
||||||
let character = map.bind_mut().spawn_player(
|
let character = map.bind_mut().spawn_player(
|
||||||
@ -294,6 +338,7 @@ 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>) {
|
||||||
for (
|
for (
|
||||||
player_id,
|
player_id,
|
||||||
@ -324,6 +369,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle a jump, either from the server or from a client
|
||||||
pub fn handle_jump(&mut self, player_id: u16) {
|
pub fn handle_jump(&mut self, player_id: u16) {
|
||||||
if player_id != self.self_id.unwrap_or(0) {
|
if player_id != self.self_id.unwrap_or(0) {
|
||||||
if let Some(player) = self.find_player_mut(player_id) {
|
if let Some(player) = self.find_player_mut(player_id) {
|
||||||
@ -332,6 +378,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle shooting, either from the server of from a client
|
||||||
pub fn handle_shoot(
|
pub fn handle_shoot(
|
||||||
&mut self,
|
&mut self,
|
||||||
player_id: u16,
|
player_id: u16,
|
||||||
@ -340,11 +387,54 @@ impl Game {
|
|||||||
look_up: f32,
|
look_up: f32,
|
||||||
deal_damage: bool,
|
deal_damage: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let self_id = self.self_id.clone().unwrap_or(0);
|
||||||
if let Some(player) = self.find_player_mut(player_id) {
|
if let Some(player) = self.find_player_mut(player_id) {
|
||||||
|
if player.id != self_id {
|
||||||
player.try_shoot(to, transform, look_up, deal_damage)
|
player.try_shoot(to, transform, look_up, deal_damage)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle taking damage, from server to client
|
||||||
|
pub fn handle_take_damage(&mut self, player_id: u16, damage: i32) {
|
||||||
|
if let Some(player) = self.find_player_mut(player_id) {
|
||||||
|
if let Some(character) = &mut player.character {
|
||||||
|
character.dyn_bind_mut().take_damage(damage, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle dying, from server to client
|
||||||
|
pub fn handle_kill(&mut self, player_id: u16) {
|
||||||
|
godot_print!("Got kill!");
|
||||||
|
if let Some(player) = self.find_player_mut(player_id) {
|
||||||
|
if let Some(character) = &mut player.character {
|
||||||
|
character.dyn_bind_mut().kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called whenever a player takes damage on the server-side
|
||||||
|
pub fn on_take_damage(&mut self, player_id: u16, damage: i32) {
|
||||||
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
|
if let PeerKind::Server(peer, _) = peer {
|
||||||
|
peer.broadcast_reliable(TakeDamage(player_id, damage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called whenever a player dies on the server-side
|
||||||
|
pub fn on_death(&mut self, player_id: u16) {
|
||||||
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
|
if let PeerKind::Server(peer, _) = peer {
|
||||||
|
godot_print!("Sent kill!");
|
||||||
|
peer.broadcast_reliable(Kill(player_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,7 +533,7 @@ impl Player {
|
|||||||
.dyn_bind_mut()
|
.dyn_bind_mut()
|
||||||
.apply_rotation(transform.rotation.into());
|
.apply_rotation(transform.rotation.into());
|
||||||
character.dyn_bind_mut().apply_look_up(look_up);
|
character.dyn_bind_mut().apply_look_up(look_up);
|
||||||
character.dyn_bind_mut().try_shoot(to, true)
|
character.dyn_bind_mut().try_shoot(to, deal_damage)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -214,7 +214,7 @@ impl INode for NetworkManager {
|
|||||||
}
|
}
|
||||||
Package::SpawnPlayer(id, transform) => {
|
Package::SpawnPlayer(id, transform) => {
|
||||||
if let Some(game) = &mut Game::singleton() {
|
if let Some(game) = &mut Game::singleton() {
|
||||||
game.bind_mut().spawn_player(id, transform);
|
game.bind_mut().spawn_player_client(id, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Package::Sync(sync_data) => {
|
Package::Sync(sync_data) => {
|
||||||
@ -238,6 +238,16 @@ impl INode for NetworkManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Package::TakeDamage(player_id, damage) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().handle_take_damage(player_id, damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::Kill(player_id) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().handle_kill(player_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -541,6 +551,8 @@ pub enum Package {
|
|||||||
SelfSync(SyncPackage),
|
SelfSync(SyncPackage),
|
||||||
Jump(u16),
|
Jump(u16),
|
||||||
Shoot(u16, NetVector3, NetPlayerTransform, f32),
|
Shoot(u16, NetVector3, NetPlayerTransform, f32),
|
||||||
|
TakeDamage(u16, i32),
|
||||||
|
Kill(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
use godot::prelude::*;
|
use godot::{
|
||||||
|
classes::{PhysicsRayQueryParameters3D, World3D},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
@ -23,3 +26,43 @@ impl From<NetVector3> for Vector3 {
|
|||||||
Vector3::new(value.x, value.y, value.z)
|
Vector3::new(value.x, value.y, value.z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cast_ray(
|
||||||
|
from: Vector3,
|
||||||
|
to: Vector3,
|
||||||
|
world: Option<Gd<World3D>>,
|
||||||
|
exclude: Array<Rid>,
|
||||||
|
) -> Option<RaycastResult> {
|
||||||
|
let ray = PhysicsRayQueryParameters3D::create(from, to);
|
||||||
|
if let Some(mut ray) = ray {
|
||||||
|
ray.set_collision_mask(0b111);
|
||||||
|
ray.set_hit_back_faces(false);
|
||||||
|
ray.set_hit_from_inside(false);
|
||||||
|
ray.set_collide_with_bodies(true);
|
||||||
|
ray.set_exclude(&exclude);
|
||||||
|
if let Some(world) = world
|
||||||
|
&& let Some(mut space) = world.get_direct_space_state()
|
||||||
|
{
|
||||||
|
let results = space.intersect_ray(&ray);
|
||||||
|
if let (Some(position), Some(collider)) =
|
||||||
|
(results.get("position"), results.get("collider"))
|
||||||
|
{
|
||||||
|
Some(RaycastResult {
|
||||||
|
position: Vector3::from_variant(&position),
|
||||||
|
collider: Gd::from_variant(&collider),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RaycastResult {
|
||||||
|
pub position: Vector3,
|
||||||
|
pub collider: Gd<Object>,
|
||||||
|
}
|
||||||
|
|||||||
@ -2,8 +2,9 @@ use std::{f32::consts::PI, ops::Neg};
|
|||||||
|
|
||||||
use godot::{
|
use godot::{
|
||||||
classes::{
|
classes::{
|
||||||
AudioStreamPlayer3D, Camera3D, CharacterBody3D, ICharacterBody3D, Input, InputEvent,
|
AudioStreamPlayer3D, Camera3D, CharacterBody3D, CollisionShape3D, GpuParticles3D,
|
||||||
InputEventMouseMotion, PhysicsRayQueryParameters3D, input::MouseMode,
|
ICharacterBody3D, Input, InputEvent, InputEventMouseMotion, PhysicsRayQueryParameters3D,
|
||||||
|
input::MouseMode,
|
||||||
},
|
},
|
||||||
obj::WithBaseField,
|
obj::WithBaseField,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -17,11 +18,11 @@ use crate::{
|
|||||||
Package::{Jump, Shoot},
|
Package::{Jump, Shoot},
|
||||||
PeerKind,
|
PeerKind,
|
||||||
},
|
},
|
||||||
util::NetVector3,
|
util::{NetVector3, cast_ray},
|
||||||
},
|
},
|
||||||
soldier_mesh::SoldierMesh,
|
soldier_mesh::SoldierMesh,
|
||||||
ui::cli::CommandLinePanel,
|
ui::cli::CommandLinePanel,
|
||||||
weapon::Weapon,
|
weapon::{Raygun, Weapon},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
@ -44,6 +45,9 @@ pub trait IPlayer {
|
|||||||
fn get_transform(&self) -> NetPlayerTransform;
|
fn get_transform(&self) -> NetPlayerTransform;
|
||||||
fn jump(&mut self);
|
fn jump(&mut self);
|
||||||
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool;
|
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool;
|
||||||
|
fn take_damage(&mut self, damage: i32, kill: bool);
|
||||||
|
fn is_dead(&self) -> bool;
|
||||||
|
fn kill(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlayerMovement {
|
pub trait PlayerMovement {
|
||||||
@ -65,10 +69,14 @@ impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
|
|||||||
.rotated(Vector3::UP, self.base().get_rotation().y)
|
.rotated(Vector3::UP, self.base().get_rotation().y)
|
||||||
* self.get_move_speed();
|
* self.get_move_speed();
|
||||||
|
|
||||||
|
if self.is_dead() {
|
||||||
|
self.base_mut().set_velocity(Vector3::ZERO);
|
||||||
|
} else {
|
||||||
self.base_mut().set_velocity(speed);
|
self.base_mut().set_velocity(speed);
|
||||||
self.base_mut().move_and_slide();
|
self.base_mut().move_and_slide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=CharacterBody3D, init)]
|
#[class(base=CharacterBody3D, init)]
|
||||||
@ -81,9 +89,15 @@ pub struct LocalPlayer {
|
|||||||
move_speed: f32,
|
move_speed: f32,
|
||||||
#[export]
|
#[export]
|
||||||
soldier_mesh: Option<Gd<SoldierMesh>>,
|
soldier_mesh: Option<Gd<SoldierMesh>>,
|
||||||
|
#[export]
|
||||||
|
collider: Option<Gd<CollisionShape3D>>,
|
||||||
|
#[export]
|
||||||
|
splorch: Option<Gd<GpuParticles3D>>,
|
||||||
|
#[export]
|
||||||
|
health: i32,
|
||||||
|
|
||||||
pub player_id: Option<u16>,
|
pub player_id: Option<u16>,
|
||||||
pub weapon: Option<Gd<Weapon>>,
|
pub weapon: Option<DynGd<Node3D, dyn Weapon>>,
|
||||||
|
|
||||||
movement_dir: Vector3,
|
movement_dir: Vector3,
|
||||||
y_speed: f32,
|
y_speed: f32,
|
||||||
@ -91,6 +105,8 @@ pub struct LocalPlayer {
|
|||||||
|
|
||||||
shoot_cd: f64,
|
shoot_cd: f64,
|
||||||
|
|
||||||
|
is_dead: bool,
|
||||||
|
|
||||||
locked: bool,
|
locked: bool,
|
||||||
|
|
||||||
base: Base<CharacterBody3D>,
|
base: Base<CharacterBody3D>,
|
||||||
@ -155,9 +171,14 @@ impl IPlayer for LocalPlayer {
|
|||||||
fn apply_look_up(&mut self, _look: f32) {}
|
fn apply_look_up(&mut self, _look: f32) {}
|
||||||
|
|
||||||
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
||||||
|
if self.is_dead {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if self.shoot_cd <= 0. {
|
if self.shoot_cd <= 0. {
|
||||||
|
let rid = self.base().get_rid();
|
||||||
if let Some(weapon) = &mut self.weapon {
|
if let Some(weapon) = &mut self.weapon {
|
||||||
weapon.bind_mut().shoot(to, deal_damage);
|
weapon.dyn_bind_mut().shoot(rid, to, deal_damage);
|
||||||
}
|
}
|
||||||
self.shoot_cd = 0.2;
|
self.shoot_cd = 0.2;
|
||||||
true
|
true
|
||||||
@ -165,6 +186,35 @@ impl IPlayer for LocalPlayer {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_damage(&mut self, damage: i32, kill: bool) {
|
||||||
|
self.run_deferred(move |s| s.signals().on_take_damage().emit(damage));
|
||||||
|
self.health -= damage;
|
||||||
|
if kill && self.health <= 0 {
|
||||||
|
self.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dead(&self) -> bool {
|
||||||
|
self.is_dead
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self) {
|
||||||
|
self.run_deferred(move |s| s.signals().on_die().emit());
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if let Some(splorch) = &mut self.splorch {
|
||||||
|
splorch.set_emitting(true);
|
||||||
|
}
|
||||||
|
self.is_dead = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
@ -221,45 +271,30 @@ impl ICharacterBody3D for LocalPlayer {
|
|||||||
if event.is_action_pressed("shoot") {
|
if event.is_action_pressed("shoot") {
|
||||||
let pos = if let Some(camera) = &self.camera {
|
let pos = if let Some(camera) = &self.camera {
|
||||||
let forward = camera.get_global_basis().col_c().neg();
|
let forward = camera.get_global_basis().col_c().neg();
|
||||||
let ray = PhysicsRayQueryParameters3D::create(
|
match cast_ray(
|
||||||
camera.get_global_position(),
|
camera.get_global_position(),
|
||||||
camera.get_global_position() + forward * 10000.,
|
camera.get_global_position() + forward,
|
||||||
);
|
self.base().get_world_3d(),
|
||||||
if let Some(mut ray) = ray {
|
Array::from_iter(vec![self.base().get_rid()]),
|
||||||
ray.set_collision_mask(0b110);
|
) {
|
||||||
ray.set_hit_back_faces(false);
|
Some(res) => Some(res.position),
|
||||||
ray.set_hit_from_inside(false);
|
None => Some(camera.get_global_position() + forward * 100.),
|
||||||
ray.set_collide_with_bodies(true);
|
|
||||||
if let Some(world) = self.base().get_world_3d()
|
|
||||||
&& let Some(mut space) = world.get_direct_space_state()
|
|
||||||
{
|
|
||||||
let results = space.intersect_ray(&ray);
|
|
||||||
if let Some(position) = results.get("position") {
|
|
||||||
Some(Vector3::from_variant(&position))
|
|
||||||
} else {
|
|
||||||
Some(camera.get_global_position() + forward * 100.)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
godot_print!("{:?}", pos);
|
|
||||||
|
|
||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
match peer {
|
match peer {
|
||||||
PeerKind::Client(peer, addr) => {
|
PeerKind::Client(peer, addr) => {
|
||||||
|
if self.try_shoot(pos, false) {
|
||||||
peer.send_reliable(
|
peer.send_reliable(
|
||||||
addr,
|
addr,
|
||||||
Shoot(0, pos.into(), self.get_transform(), self.look_up),
|
Shoot(0, pos.into(), self.get_transform(), self.look_up),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PeerKind::Server(peer, _) => {
|
PeerKind::Server(peer, _) => {
|
||||||
if self.try_shoot(pos, true) {
|
if self.try_shoot(pos, true) {
|
||||||
peer.broadcast_reliable(Shoot(
|
peer.broadcast_reliable(Shoot(
|
||||||
@ -276,6 +311,7 @@ impl ICharacterBody3D for LocalPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(event) = event.try_cast::<InputEventMouseMotion>() {
|
if let Ok(event) = event.try_cast::<InputEventMouseMotion>() {
|
||||||
|
if !self.is_dead() {
|
||||||
let x = event.get_relative().x * 0.001 * self.look_sensitivity;
|
let x = event.get_relative().x * 0.001 * self.look_sensitivity;
|
||||||
let y = event.get_relative().y * 0.001 * self.look_sensitivity;
|
let y = event.get_relative().y * 0.001 * self.look_sensitivity;
|
||||||
|
|
||||||
@ -293,8 +329,15 @@ impl ICharacterBody3D for LocalPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
impl LocalPlayer {
|
impl LocalPlayer {
|
||||||
|
#[signal]
|
||||||
|
pub fn on_die();
|
||||||
|
#[signal]
|
||||||
|
pub fn on_take_damage(damage: i32);
|
||||||
|
|
||||||
fn set_lock(&mut self, locked: bool) {
|
fn set_lock(&mut self, locked: bool) {
|
||||||
self.locked = locked;
|
self.locked = locked;
|
||||||
if self.locked {
|
if self.locked {
|
||||||
@ -312,14 +355,22 @@ pub struct RemotePlayer {
|
|||||||
move_speed: f32,
|
move_speed: f32,
|
||||||
#[export]
|
#[export]
|
||||||
soldier_mesh: Option<Gd<SoldierMesh>>,
|
soldier_mesh: Option<Gd<SoldierMesh>>,
|
||||||
|
#[export]
|
||||||
|
collider: Option<Gd<CollisionShape3D>>,
|
||||||
|
#[export]
|
||||||
|
splorch: Option<Gd<GpuParticles3D>>,
|
||||||
|
#[export]
|
||||||
|
health: i32,
|
||||||
|
|
||||||
pub player_id: Option<u16>,
|
pub player_id: Option<u16>,
|
||||||
pub weapon: Option<Gd<Weapon>>,
|
pub weapon: Option<DynGd<Node3D, dyn Weapon>>,
|
||||||
|
|
||||||
movement_dir: Vector3,
|
movement_dir: Vector3,
|
||||||
y_speed: f32,
|
y_speed: f32,
|
||||||
look_up: f32,
|
look_up: f32,
|
||||||
|
|
||||||
|
is_dead: bool,
|
||||||
|
|
||||||
shoot_cd: f64,
|
shoot_cd: f64,
|
||||||
|
|
||||||
base: Base<CharacterBody3D>,
|
base: Base<CharacterBody3D>,
|
||||||
@ -382,9 +433,14 @@ impl IPlayer for RemotePlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
||||||
|
if self.is_dead {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if self.shoot_cd <= 0. {
|
if self.shoot_cd <= 0. {
|
||||||
|
let rid = self.base().get_rid();
|
||||||
if let Some(weapon) = &mut self.weapon {
|
if let Some(weapon) = &mut self.weapon {
|
||||||
weapon.bind_mut().shoot(to, deal_damage);
|
weapon.dyn_bind_mut().shoot(rid, to, deal_damage);
|
||||||
}
|
}
|
||||||
self.shoot_cd = 0.2;
|
self.shoot_cd = 0.2;
|
||||||
true
|
true
|
||||||
@ -392,6 +448,35 @@ impl IPlayer for RemotePlayer {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_damage(&mut self, damage: i32, kill: bool) {
|
||||||
|
self.run_deferred(move |s| s.signals().on_take_damage().emit(damage));
|
||||||
|
self.health -= damage;
|
||||||
|
if kill && self.health <= 0 {
|
||||||
|
self.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dead(&self) -> bool {
|
||||||
|
self.is_dead
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self) {
|
||||||
|
self.run_deferred(move |s| s.signals().on_die().emit());
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if let Some(splorch) = &mut self.splorch {
|
||||||
|
splorch.set_emitting(true);
|
||||||
|
}
|
||||||
|
self.is_dead = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
@ -415,3 +500,11 @@ impl ICharacterBody3D for RemotePlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl RemotePlayer {
|
||||||
|
#[signal]
|
||||||
|
pub fn on_die();
|
||||||
|
#[signal]
|
||||||
|
pub fn on_take_damage(damage: i32);
|
||||||
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use godot::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::weapon::Weapon;
|
use crate::weapon::{Raygun, Weapon};
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=Node3D, init)]
|
#[class(base=Node3D, init)]
|
||||||
@ -43,16 +43,19 @@ impl SoldierMesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_raygun(&mut self) -> Option<Gd<Weapon>> {
|
pub fn spawn_raygun(&mut self) -> Option<DynGd<Node3D, dyn Weapon>> {
|
||||||
if let Some(raygun) = &self.raygun {
|
if let Some(raygun) = &self.raygun {
|
||||||
let node = raygun.instantiate_as::<Weapon>();
|
let node = raygun.instantiate_as::<Raygun>();
|
||||||
self.spawn_weapon(node)
|
self.spawn_weapon(node.into_dyn().upcast())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_weapon(&mut self, node: Gd<Weapon>) -> Option<Gd<Weapon>> {
|
fn spawn_weapon(
|
||||||
|
&mut self,
|
||||||
|
node: DynGd<Node3D, dyn Weapon>,
|
||||||
|
) -> Option<DynGd<Node3D, dyn Weapon>> {
|
||||||
if let Some(hand) = &mut self.hand_attachment {
|
if let Some(hand) = &mut self.hand_attachment {
|
||||||
hand.add_child(&node);
|
hand.add_child(&node);
|
||||||
Some(node)
|
Some(node)
|
||||||
|
|||||||
@ -3,9 +3,15 @@ use godot::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{net::util::cast_ray, player::IPlayer};
|
||||||
|
|
||||||
|
pub trait Weapon {
|
||||||
|
fn shoot(&mut self, player: Rid, to: Vector3, deal_damage: bool);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=Node3D, init)]
|
#[class(base=Node3D, init)]
|
||||||
pub struct Weapon {
|
pub struct Raygun {
|
||||||
#[export]
|
#[export]
|
||||||
sound_emitter: Option<Gd<AudioStreamPlayer3D>>,
|
sound_emitter: Option<Gd<AudioStreamPlayer3D>>,
|
||||||
#[export]
|
#[export]
|
||||||
@ -16,55 +22,87 @@ pub struct Weapon {
|
|||||||
base: Base<Node3D>,
|
base: Base<Node3D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Weapon {
|
#[godot_dyn]
|
||||||
pub fn shoot(&mut self, to: Vector3, deal_damage: bool) {
|
impl Weapon for Raygun {
|
||||||
|
fn shoot(&mut self, player: Rid, to: Vector3, deal_damage: bool) {
|
||||||
if let Some(emitter) = &mut self.sound_emitter {
|
if let Some(emitter) = &mut self.sound_emitter {
|
||||||
emitter.play();
|
emitter.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(source) = &self.bullet_source {
|
||||||
|
let target = cast_ray(
|
||||||
|
source.get_global_position(),
|
||||||
|
to,
|
||||||
|
self.base().get_world_3d(),
|
||||||
|
Array::from_iter(vec![player]),
|
||||||
|
);
|
||||||
|
|
||||||
|
let dir = (to - source.get_global_position()).normalized();
|
||||||
|
let beam_end = target
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| r.position)
|
||||||
|
.unwrap_or(source.get_global_position() + dir * 100.);
|
||||||
|
|
||||||
if let Some(prefab) = &self.bullet_prefab {
|
if let Some(prefab) = &self.bullet_prefab {
|
||||||
let mut bullet = prefab.instantiate_as::<Bullet>();
|
let mut bullet = prefab.instantiate_as::<Beam>();
|
||||||
|
|
||||||
|
bullet.bind_mut().source = Some(source.get_global_position());
|
||||||
|
bullet.bind_mut().target = Some(beam_end);
|
||||||
|
|
||||||
if let Some(mut parent) = self.base().find_parent("map") {
|
if let Some(mut parent) = self.base().find_parent("map") {
|
||||||
parent.add_child(&bullet);
|
parent.add_child(&bullet);
|
||||||
if let Some(source) = &self.bullet_source {
|
|
||||||
bullet.set_global_position(source.get_global_position());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bullet.bind_mut().target = Some(to);
|
|
||||||
|
if deal_damage && let Some(target) = target {
|
||||||
|
if let Ok(mut player) = target.collider.try_dynify::<dyn IPlayer>() {
|
||||||
|
player.dyn_bind_mut().take_damage(100, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=Node3D, init)]
|
#[class(base=Node3D, init)]
|
||||||
pub struct Bullet {
|
pub struct Beam {
|
||||||
#[export]
|
pub source: Option<Vector3>,
|
||||||
pub emitter: Option<Gd<GpuParticles3D>>,
|
|
||||||
|
|
||||||
pub target: Option<Vector3>,
|
pub target: Option<Vector3>,
|
||||||
|
|
||||||
alive_for: f64,
|
distance: f32,
|
||||||
|
girth: f32,
|
||||||
|
|
||||||
base: Base<Node3D>,
|
base: Base<Node3D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
impl INode3D for Bullet {
|
impl INode3D for Beam {
|
||||||
fn ready(&mut self) {
|
fn ready(&mut self) {
|
||||||
if let Some(emitter) = &mut self.emitter {
|
if let (Some(target), Some(source)) = (self.target, self.source) {
|
||||||
emitter.set_emitting(true);
|
let midway_point = (target + source) / 2.;
|
||||||
|
self.distance = source.distance_to(target);
|
||||||
|
self.girth = 1.;
|
||||||
|
|
||||||
|
self.scale();
|
||||||
|
self.base_mut().look_at_from_position(source, target);
|
||||||
|
self.base_mut().set_global_position(midway_point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&mut self, delta: f64) {
|
fn process(&mut self, _delta: f32) {
|
||||||
self.alive_for += delta;
|
self.girth = self.girth.lerp(0., 0.07);
|
||||||
if self.alive_for > 0.05 {
|
self.scale();
|
||||||
let prev_pos = self.base().get_global_position();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(target) = self.target {
|
impl Beam {
|
||||||
let new_pos = prev_pos.lerp(target, 0.5);
|
fn scale(&mut self) {
|
||||||
self.base_mut().set_position(new_pos);
|
let length = Vector3::BACK * self.distance;
|
||||||
}
|
let girth_scale = (Vector3::UP + Vector3::RIGHT) * self.girth;
|
||||||
|
self.base_mut().set_scale(length + girth_scale);
|
||||||
|
|
||||||
|
if self.girth < 0.01 {
|
||||||
|
self.base_mut().queue_free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user