Implement friendly fire gamerule

This commit is contained in:
Sofia 2026-07-22 04:30:47 +03:00
parent f732472f7c
commit 4f9e24f5ab

View File

@ -11,7 +11,7 @@ use godot::{
use serde::{Deserialize, Serialize};
use crate::{
game_manager::Game,
game_manager::{Game, GameOption},
net::{
network_manager::{
NetworkManager,
@ -56,14 +56,15 @@ pub trait IPlayer {
fn get_ball_origin(&self) -> Option<Gd<Node3D>>;
}
pub trait PlayerMovement {
pub trait PlayerCommon {
fn player_update(&mut self, delta: f64);
fn try_jump(&mut self) -> bool;
fn drop_ball_common(&mut self, with_velocity: bool) -> bool;
fn get_look_dir(&self) -> Vector3;
fn can_take_damage(&self, source: &DamageSource) -> bool;
}
impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerCommon for T {
fn player_update(&mut self, delta: f64) {
self.set_y_speed(self.get_y_speed() - 9.8 * delta as f32);
if self.base().is_on_floor() && self.get_y_speed() < 0. {
@ -141,6 +142,30 @@ impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
Vector3::ZERO
}
}
fn can_take_damage(&self, source: &DamageSource) -> bool {
match source {
DamageSource::Player(source_player_id) => {
if let Some(game) = Game::singleton() {
let (self_team, source_team) = if let Some(self_player) =
game.bind().find_player(self.get_player_id().unwrap_or(0))
&& let Some(source_player) = game.bind().find_player(*source_player_id)
{
(self_player.data.team, source_player.data.team)
} else {
(0, 0)
};
if self_team == source_team
&& !game.bind().opts.is_true(GameOption::FriendlyFire)
{
return false;
}
}
}
_ => {}
}
true
}
}
#[derive(GodotClass)]
@ -271,13 +296,17 @@ impl IPlayer for LocalPlayer {
}
fn take_damage(&mut self, source: DamageSource, damage: i32, kill: bool) {
self.run_deferred(move |s| {
if s.can_take_damage(&source) {
let (signal, player) = source.into_signal();
self.run_deferred(move |s| s.signals().on_take_damage().emit(signal, player, damage));
self.health -= damage;
if kill && self.health <= 0 {
self.kill(source);
s.run_deferred(move |s| s.signals().on_take_damage().emit(signal, player, damage));
s.health -= damage;
if kill && s.health <= 0 {
s.kill(source);
}
}
});
}
fn is_dead(&self) -> bool {
self.is_dead
@ -645,13 +674,17 @@ impl IPlayer for RemotePlayer {
}
fn take_damage(&mut self, source: DamageSource, damage: i32, kill: bool) {
self.run_deferred(move |s| {
if s.can_take_damage(&source) {
let (signal, player) = source.into_signal();
self.run_deferred(move |s| s.signals().on_take_damage().emit(signal, player, damage));
self.health -= damage;
if kill && self.health <= 0 {
self.kill(source);
s.run_deferred(move |s| s.signals().on_take_damage().emit(signal, player, damage));
s.health -= damage;
if kill && s.health <= 0 {
s.kill(source);
}
}
});
}
fn is_dead(&self) -> bool {
self.is_dead