Add blood decals for clients as well

This commit is contained in:
Sofia 2026-08-07 23:54:07 +03:00
parent 3ef771d0b6
commit 5ccfc651a1
10 changed files with 89 additions and 20 deletions

View File

@ -31,6 +31,7 @@ use crate::{
buffs::{Buff, BuffKind},
local_player::LocalPlayer,
remote_player::RemotePlayer,
shootable::Shootable,
telegrenade::Telegrenade,
weapon::WeaponType,
},
@ -1087,6 +1088,25 @@ impl Game {
}
}
// Handled whenever a player is shot, run on client-side only
pub fn handle_on_shot(
&mut self,
player_id: u16,
source: DamageSource,
damage: i32,
delta: NetVector3,
) {
if let Some(player) = self.find_player_mut(player_id)
&& let Some(character) = &mut player.character
{
if let Ok(mut shootable) = character.clone().into_gd().try_dynify::<dyn Shootable>() {
shootable
.dyn_bind_mut()
.on_shoot_visual(source, damage, delta.into());
}
}
}
// Handle shooting, either from the server of from a client
pub fn handle_shoot(
&mut self,

View File

@ -181,6 +181,8 @@ impl Shootable for ShootableTrigger {
self.signals().on_shot().emit(id, damage, dir);
}
}
fn on_shoot_visual(&mut self, _: DamageSource, _: i32, _: Vector3) {}
}
#[derive(GodotClass)]

View File

@ -357,6 +357,7 @@ pub enum Package {
Jump(u16),
Shoot(u16, NetVector3, NetTransform, f32),
Punch(u16, NetVector3, NetTransform, f32),
OnShot(u16, DamageSource, i32, NetVector3),
TakeDamage(DamageSource, u16, i32),
Kill(DamageSource, u16),
SpawnBall(NetTransform, NetVector3, Option<u16>),

View File

@ -7,6 +7,7 @@ use crate::{
game::{Game, GameManager, opts::GameOption},
game_settings::GameSettingsManager,
net::network_manager::{NetworkManager, Package, PeerKind},
player,
popup_queue::{Popup, PopupQueue},
ui::cli::{CliColor, CommandLinePanel},
};
@ -262,6 +263,12 @@ impl NetworkManager {
game.bind_mut().on_trigger_deactivate(id);
}
}
Package::OnShot(player_id, source, damage, delta) => {
if let Some(game) = &mut Game::singleton() {
game.bind_mut()
.handle_on_shot(player_id, source, damage, delta);
}
}
_ => {}
},
},

View File

@ -100,4 +100,6 @@ impl Shootable for Ball {
self.base_mut()
.set_linear_velocity(prev_velocity + delta.normalized() * force);
}
fn on_shoot_visual(&mut self, _: DamageSource, _: i32, _: Vector3) {}
}

View File

@ -112,14 +112,15 @@ impl PlayerEffects {
s.base().get_basis().col_c(),
&s.base().get_world_3d(),
&Array::new(),
(20. / delta.length()) as u8,
10,
// Calculate the particular spread of blood
(((PI / 4.) * force) / delta.length()).min(PI / 2.),
10. + delta.length() / 3.,
None,
);
let mut decal_results = Vec::new();
for _ in 0..(force * 5.) as usize {
for _ in 0..(force * force * 4.) as usize {
if let Some(result) = results
.get(s.rng.randi_range(0, results.len() as i32 - 1) as usize)
.cloned()
@ -127,7 +128,6 @@ impl PlayerEffects {
decal_results.push(result);
}
}
godot_print!("{:?}", decal_results);
for result in decal_results {
let mut decal = prefab.instantiate_as::<Node3D>();

View File

@ -940,11 +940,27 @@ impl LocalPlayer {
#[godot_dyn]
impl Shootable for LocalPlayer {
fn on_shoot(&mut self, source: DamageSource, damage: i32, delta: Vector3) {
if self.can_take_damage(&source)
&& let Some(fx) = &mut self.effects
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
fx.bind_mut().shoot_blood(delta, damage as f32 / 100.);
peer.broadcast_reliable(Package::OnShot(
self.player_id.unwrap_or(0),
source,
damage,
delta.into(),
));
self.on_shoot_visual(source, damage, delta);
}
self.take_damage(source, damage, true);
}
fn on_shoot_visual(&mut self, source: DamageSource, damage: i32, delta: Vector3) {
self.run_deferred(move |s| {
if s.can_take_damage(&source)
&& let Some(fx) = &mut s.effects
{
fx.bind_mut().shoot_blood(delta, damage as f32 / 100.);
}
});
}
}

View File

@ -10,6 +10,7 @@ use godot::{
use crate::{
game::{Game, opts::GameOption},
map::{killbox::KillboxKind, pickup::PickupKind},
net::network_manager::{NetworkManager, Package, PeerKind},
player::{
DamageSource, DamageSourceSignal, IPlayer, NetTransform, PlayerCommon,
buffs::{Buff, Buffs},
@ -502,11 +503,27 @@ impl RemotePlayer {
#[godot_dyn]
impl Shootable for RemotePlayer {
fn on_shoot(&mut self, source: DamageSource, damage: i32, delta: Vector3) {
if self.can_take_damage(&source)
&& let Some(fx) = &mut self.effects
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
&& let PeerKind::Server(peer, _) = peer
{
fx.bind_mut().shoot_blood(delta, damage as f32 / 100.);
peer.broadcast_reliable(Package::OnShot(
self.player_id.unwrap_or(0),
source,
damage,
delta.into(),
));
self.on_shoot_visual(source, damage, delta);
}
self.take_damage(source, damage, true);
}
fn on_shoot_visual(&mut self, source: DamageSource, damage: i32, delta: Vector3) {
self.run_deferred(move |s| {
if s.can_take_damage(&source)
&& let Some(fx) = &mut s.effects
{
fx.bind_mut().shoot_blood(delta, damage as f32 / 100.);
}
});
}
}

View File

@ -2,5 +2,6 @@ use crate::player::DamageSource;
use godot::prelude::*;
pub trait Shootable {
fn on_shoot(&mut self, source: DamageSource, damage: i32, dir: Vector3);
fn on_shoot(&mut self, source: DamageSource, damage: i32, delta: Vector3);
fn on_shoot_visual(&mut self, source: DamageSource, damage: i32, delta: Vector3);
}

View File

@ -1,4 +1,7 @@
use std::{collections::HashSet, f32::consts::PI};
use std::{
collections::{HashMap, HashSet},
f32::consts::PI,
};
use godot::{
classes::{AudioStreamPlayer3D, GpuParticles3D, RandomNumberGenerator},
@ -224,7 +227,7 @@ impl Weapon for Shotgun {
vfx.set_emitting(true);
}
let mut colliders = HashSet::new();
let mut colliders: HashMap<Gd<Object>, Vec<Vector3>> = HashMap::new();
let results = shotgun_ray(
from,
to,
@ -255,17 +258,17 @@ impl Weapon for Shotgun {
}
for result in &results {
colliders.insert(result.collider.clone());
if let Some(positions) = colliders.get_mut(&result.collider) {
positions.push(result.position)
} else {
colliders.insert(result.collider.clone(), vec![result.position]);
}
}
if deal_damage {
for collider in colliders {
let collider_pos = if let Ok(node) = collider.clone().try_cast::<Node3D>() {
node.get_global_position()
} else {
Vector3::ZERO
};
let delta = collider_pos - from;
for (collider, positions) in colliders {
let position = positions.iter().sum::<Vector3>() / positions.len() as f32;
let delta = position - from;
if let Ok(mut shootable) = collider.clone().try_dynify::<dyn Shootable>() {
self.run_deferred(move |_| {