829 lines
27 KiB
Rust
829 lines
27 KiB
Rust
use std::{f32::consts::PI, ops::Neg};
|
|
|
|
use godot::{
|
|
classes::{
|
|
AudioStreamPlayer, Camera3D, CharacterBody3D, CollisionShape3D, ColorRect,
|
|
ICharacterBody3D, Input, InputEvent, InputEventMouseMotion, Label, input::MouseMode,
|
|
},
|
|
obj::WithBaseField,
|
|
prelude::*,
|
|
};
|
|
|
|
use crate::{
|
|
game::{Game, GameManager, opts::GameOption},
|
|
game_settings::{GameSettings, GameSettingsManager},
|
|
killbox::KillboxKind,
|
|
net::{
|
|
network_manager::{
|
|
NetworkManager,
|
|
Package::{self, Jump, Punch, Shoot, ThrowTelegrenade},
|
|
PeerKind,
|
|
},
|
|
util::cast_ray,
|
|
},
|
|
pickup::PickupKind,
|
|
player::{
|
|
DamageSource, DamageSourceSignal, IPlayer, NetTransform, PlayerCommon,
|
|
buffs::{Buff, Buffs},
|
|
effects::PlayerEffects,
|
|
remote_player::RemotePlayer,
|
|
shootable::Shootable,
|
|
soldier_mesh::SoldierMesh,
|
|
telegrenade::Telegrenade,
|
|
weapon::{BallWeapon, Raygun, Shotgun, Weapon, WeaponType},
|
|
},
|
|
replay::ReplayEvent,
|
|
ui::{cli::CommandLinePanel, hud::Hud},
|
|
};
|
|
|
|
#[derive(GodotClass)]
|
|
#[class(base=CharacterBody3D, init)]
|
|
pub struct LocalPlayer {
|
|
#[export]
|
|
look_sensitivity: f32,
|
|
#[export]
|
|
camera: Option<Gd<Camera3D>>,
|
|
#[export]
|
|
vignette: Option<Gd<ColorRect>>,
|
|
#[export]
|
|
move_speed: f32,
|
|
#[export]
|
|
soldier_mesh: Option<Gd<SoldierMesh>>,
|
|
#[export]
|
|
collider: Option<Gd<CollisionShape3D>>,
|
|
#[export]
|
|
effects: Option<Gd<PlayerEffects>>,
|
|
#[export]
|
|
dead_cam_target: Option<Gd<Node3D>>,
|
|
#[export]
|
|
respawn_label: Option<Gd<Label>>,
|
|
#[export]
|
|
camera_origin: Option<Gd<Node3D>>,
|
|
#[export]
|
|
weapon_node: Option<Gd<Node3D>>,
|
|
|
|
#[export]
|
|
hud: Option<Gd<Hud>>,
|
|
|
|
#[export]
|
|
hit_sound: Option<Gd<AudioStreamPlayer>>,
|
|
#[export]
|
|
health: i32,
|
|
#[export]
|
|
punch_cooldown: f64,
|
|
|
|
pub player_id: Option<u16>,
|
|
|
|
pub weapon: Option<DynGd<Node3D, dyn Weapon>>,
|
|
|
|
movement_dir: Vector3,
|
|
y_speed: f32,
|
|
y_rot: f32,
|
|
look_up: f32,
|
|
|
|
is_dead: bool,
|
|
dead_for: f64,
|
|
killer_id: Option<u16>,
|
|
|
|
time_passed: f64,
|
|
|
|
locked: bool,
|
|
|
|
punch_cd_remaining: f64,
|
|
|
|
#[export]
|
|
telegrenade_cd: f64,
|
|
telegrenade: Option<Gd<Telegrenade>>,
|
|
telegrenade_cd_remaining: f64,
|
|
telegrenades_allowed: bool,
|
|
|
|
buffs: Buffs,
|
|
|
|
base: Base<CharacterBody3D>,
|
|
}
|
|
|
|
#[godot_dyn]
|
|
impl IPlayer for LocalPlayer {
|
|
fn get_player_id(&self) -> Option<u16> {
|
|
self.player_id
|
|
}
|
|
|
|
fn get_y_speed(&self) -> f32 {
|
|
self.y_speed
|
|
}
|
|
|
|
fn set_y_speed(&mut self, speed: f32) {
|
|
self.y_speed = speed;
|
|
}
|
|
|
|
fn get_movement_dir(&self) -> Vector3 {
|
|
self.movement_dir
|
|
}
|
|
|
|
fn set_movement_dir(&mut self, _dir: Vector3) {}
|
|
|
|
fn get_move_speed(&self) -> f32 {
|
|
self.move_speed
|
|
}
|
|
|
|
fn apply_transform(&mut self, transform: NetTransform) {
|
|
self.base_mut()
|
|
.set_global_position(transform.location.into());
|
|
self.base_mut()
|
|
.set_global_rotation(transform.rotation.into());
|
|
}
|
|
|
|
fn apply_position(&mut self, pos: Vector3) {
|
|
self.base_mut().set_global_position(pos);
|
|
}
|
|
|
|
fn apply_rotation(&mut self, _rot: Vector3) {}
|
|
|
|
fn get_transform(&self) -> NetTransform {
|
|
NetTransform {
|
|
location: self.base().get_global_position().into(),
|
|
rotation: self.base().get_global_rotation().into(),
|
|
}
|
|
}
|
|
|
|
fn jump(&mut self) {
|
|
if self.try_jump() {
|
|
if let Some(game) = &mut Game::singleton()
|
|
&& let Some(recorder) = game.bind_mut().recorder()
|
|
{
|
|
recorder
|
|
.bind_mut()
|
|
.record_event(ReplayEvent::Jump(self.player_id.unwrap_or(0)));
|
|
}
|
|
if let Some(effects) = &mut self.effects {
|
|
effects.bind_mut().jump();
|
|
}
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, addr) => peer.send_reliable(&addr, Jump(0)),
|
|
PeerKind::Server(peer, _) => {
|
|
peer.broadcast_reliable(Jump(self.player_id.unwrap_or(0)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_look_up(&self) -> f32 {
|
|
self.look_up
|
|
}
|
|
|
|
fn apply_look_up(&mut self, _look: f32) {}
|
|
|
|
fn try_throw_telegrenade(&mut self, to: Vector3) -> bool {
|
|
if !self.telegrenades_allowed {
|
|
return false;
|
|
}
|
|
|
|
if self.telegrenade_cd_remaining > 0. {
|
|
return false;
|
|
}
|
|
|
|
if self.throw_telegrenade_common(to) {
|
|
self.telegrenade_cd_remaining = self.telegrenade_cd;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
|
if self.try_shoot_common(to, deal_damage) {
|
|
if let Some(game) = &mut Game::singleton()
|
|
&& let Some(recorder) = game.bind_mut().recorder()
|
|
{
|
|
recorder
|
|
.bind_mut()
|
|
.record_event(ReplayEvent::Shoot(self.player_id.unwrap_or(0), to.into()));
|
|
}
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn try_punch(&mut self, to: Vector3, deal_damage: bool) -> bool {
|
|
if self.punch_cd_remaining <= 0. {
|
|
if let Some(weapon) = &mut self.weapon {
|
|
weapon.set_position(Vector3::FORWARD);
|
|
weapon.set_rotation(Vector3::UP);
|
|
}
|
|
self.punch_cd_remaining = self.punch_cooldown;
|
|
if self.try_punch_common(to, deal_damage) {
|
|
if let Some(game) = &mut Game::singleton()
|
|
&& let Some(recorder) = game.bind_mut().recorder()
|
|
{
|
|
recorder
|
|
.bind_mut()
|
|
.record_event(ReplayEvent::Punch(self.player_id.unwrap_or(0), to.into()));
|
|
}
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
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, kind) = source.into_signal();
|
|
s.run_deferred(move |s| {
|
|
s.signals()
|
|
.on_take_damage()
|
|
.emit(signal, player, kind, damage)
|
|
});
|
|
s.health -= damage;
|
|
if kill && s.health <= 0 {
|
|
s.kill(source);
|
|
} else {
|
|
if let Some(effects) = &mut s.effects {
|
|
effects.bind_mut().hit();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn is_dead(&self) -> bool {
|
|
self.is_dead
|
|
}
|
|
|
|
fn dead_for(&self) -> f64 {
|
|
self.dead_for
|
|
}
|
|
|
|
fn kill(&mut self, source: DamageSource) {
|
|
self.drop_ball(false);
|
|
|
|
let (signal, player, kind) = source.into_signal();
|
|
self.run_deferred(move |s| s.signals().on_die().emit(signal, player, kind));
|
|
if let Some(mut mesh) = self.soldier_mesh.take() {
|
|
mesh.queue_free();
|
|
}
|
|
if let Some(mut collider) = self.collider.take() {
|
|
collider.queue_free();
|
|
}
|
|
if let Some(effects) = &mut self.effects {
|
|
effects.bind_mut().death();
|
|
}
|
|
if let Some(mut weapon) = self.weapon.take() {
|
|
weapon.queue_free();
|
|
}
|
|
self.is_dead = true;
|
|
if let DamageSource::Player(player_id) = source {
|
|
self.killer_id = Some(player_id);
|
|
}
|
|
}
|
|
|
|
fn swap_weapon(&mut self, weapon: WeaponType, drop_ball: bool) {
|
|
if drop_ball {
|
|
self.drop_ball(true);
|
|
}
|
|
|
|
if let Some(mesh) = self.soldier_mesh.clone() {
|
|
match weapon {
|
|
WeaponType::Raygun => {
|
|
if let Some(raygun) = &mesh.bind().raygun {
|
|
self.spawn_weapon(raygun.instantiate_as::<Raygun>().into_dyn().upcast());
|
|
}
|
|
}
|
|
WeaponType::Shotgun => {
|
|
if let Some(raygun) = &mesh.bind().shotgun {
|
|
self.spawn_weapon(raygun.instantiate_as::<Shotgun>().into_dyn().upcast());
|
|
}
|
|
}
|
|
WeaponType::Ball => {
|
|
if let Some(ball) = &mesh.bind().ball {
|
|
self.spawn_weapon(ball.instantiate_as::<BallWeapon>().into_dyn().upcast());
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
fn get_weapon(&self) -> Option<DynGd<Node3D, dyn Weapon>> {
|
|
self.weapon.clone()
|
|
}
|
|
|
|
fn take_weapon(&mut self) -> Option<DynGd<Node3D, dyn Weapon>> {
|
|
self.weapon.take()
|
|
}
|
|
|
|
fn drop_ball(&mut self, with_velocity: bool) -> bool {
|
|
self.drop_ball_common(with_velocity)
|
|
}
|
|
|
|
fn get_camera_origin(&self) -> Option<Gd<Node3D>> {
|
|
self.camera_origin.clone()
|
|
}
|
|
|
|
fn is_replay_mode(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn get_health(&self) -> i32 {
|
|
self.health
|
|
}
|
|
|
|
fn heal(&mut self, amount: i32) {
|
|
self.run_deferred(move |s| {
|
|
let delta = (s.health + amount).min(200) - s.health;
|
|
s.run_deferred(move |s| s.signals().on_heal().emit(delta));
|
|
s.health += delta;
|
|
});
|
|
}
|
|
|
|
fn on_pickup(&mut self, kind: PickupKind) {
|
|
match kind {
|
|
PickupKind::Health => self.heal(100),
|
|
}
|
|
}
|
|
|
|
fn set_telegrenade(&mut self, grenade: Option<Gd<Telegrenade>>) {
|
|
self.telegrenade = grenade;
|
|
}
|
|
|
|
fn get_telegrenade(&self) -> Option<Gd<Telegrenade>> {
|
|
self.telegrenade.clone()
|
|
}
|
|
|
|
fn telegrenade_teleport(&mut self, to: Vector3) -> bool {
|
|
self.telegrenade_teleport_common(to)
|
|
}
|
|
|
|
fn as_gd(&self) -> DynGd<CharacterBody3D, dyn IPlayer> {
|
|
self.to_gd().into_dyn().upcast()
|
|
}
|
|
|
|
fn set_bubble_color(&mut self, color: Color) {
|
|
self.set_vignette_color(color);
|
|
}
|
|
|
|
fn get_buffs(&self) -> &Buffs {
|
|
&self.buffs
|
|
}
|
|
|
|
fn get_buffs_mut(&mut self) -> &mut Buffs {
|
|
&mut self.buffs
|
|
}
|
|
|
|
fn new_buff(&mut self, buff: Buff) {
|
|
self.buffs.new_buff(buff);
|
|
self.buffs.clone().update_effects(self);
|
|
}
|
|
}
|
|
|
|
#[godot_api]
|
|
impl ICharacterBody3D for LocalPlayer {
|
|
fn ready(&mut self) {
|
|
self.update_settings(&GameSettingsManager::singleton().bind().settings);
|
|
GameSettingsManager::singleton()
|
|
.bind_mut()
|
|
.signals()
|
|
.on_settings_changed()
|
|
.connect_other(self, |s| {
|
|
s.update_settings(&GameSettingsManager::singleton().bind().settings)
|
|
});
|
|
|
|
if !CommandLinePanel::singleton().bind().opened {
|
|
self.set_lock(true);
|
|
}
|
|
|
|
self.swap_weapon(WeaponType::Raygun, false);
|
|
|
|
if let Some(camera) = &mut self.camera {
|
|
camera.set_current(true);
|
|
}
|
|
|
|
if let Some(mesh) = &mut self.soldier_mesh
|
|
&& let Some(remover) = &mut mesh.bind_mut().hand_remover
|
|
{
|
|
remover.bind_mut().active = true;
|
|
}
|
|
|
|
if let Some(mut hud) = self.hud.clone() {
|
|
hud.bind_mut().player_id = self.player_id.unwrap_or(0);
|
|
hud.bind_mut()
|
|
.signals()
|
|
.on_lock_changed()
|
|
.connect_other(self, |s, lock| s.set_lock(lock));
|
|
}
|
|
|
|
self.run_deferred(|s| {
|
|
s.telegrenades_allowed = if let Some(game) = Game::singleton() {
|
|
game.bind().opts.is_true(GameOption::TelegrenadesAllowed)
|
|
} else {
|
|
false
|
|
};
|
|
|
|
if let Some(game) = Game::singleton()
|
|
&& let Some(player) = game.bind().find_player(s.player_id.unwrap_or(0))
|
|
&& let Some(team) = game.bind().get_team(player.data.team)
|
|
{
|
|
if let Some(mesh) = &mut s.soldier_mesh {
|
|
mesh.bind_mut().set_color(team.bind().color);
|
|
}
|
|
}
|
|
});
|
|
|
|
self.buffs.clone().update_effects(self);
|
|
}
|
|
|
|
fn process(&mut self, delta: f64) {
|
|
self.time_passed += delta;
|
|
self.punch_cd_remaining -= delta;
|
|
self.telegrenade_cd_remaining -= delta;
|
|
|
|
if self.is_dead {
|
|
self.dead_for += delta;
|
|
|
|
if let Some(label) = &mut self.respawn_label {
|
|
let respawn_timer = if let Some(game) = Game::singleton() {
|
|
game.bind().opts.get_float(GameOption::RespawnTimer)
|
|
} else {
|
|
0.
|
|
};
|
|
|
|
let time_remaining = respawn_timer - self.dead_for;
|
|
label.set_text(&format!("Respawn in\n{:.2}", time_remaining));
|
|
}
|
|
|
|
if let Some(camera) = &self.camera {
|
|
if camera.is_current() && self.dead_for > 3. {
|
|
if let Some(mut game) = Game::singleton()
|
|
&& let Some(killer_id) = self.killer_id
|
|
{
|
|
let mut game = game.bind_mut();
|
|
let remote_player = game.find_player(killer_id);
|
|
if let Some(remote_player) = &remote_player
|
|
&& let Some(remote_character) = remote_player.character.clone()
|
|
&& let Ok(mut remote_character) =
|
|
remote_character.try_cast::<RemotePlayer>()
|
|
&& let Some(camera) = &mut remote_character.bind_mut().spectator_camera
|
|
{
|
|
camera.set_current(true);
|
|
}
|
|
|
|
game.start_playback(-5., killer_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let input_direction = if self.locked {
|
|
let input = Input::singleton();
|
|
|
|
if input.is_action_just_pressed("jump") {
|
|
self.jump();
|
|
}
|
|
|
|
Vector3::RIGHT * input.is_action_pressed("right") as u32 as f32
|
|
- Vector3::RIGHT * input.is_action_pressed("left") as u32 as f32
|
|
+ Vector3::FORWARD * input.is_action_pressed("forward") as u32 as f32
|
|
- Vector3::FORWARD * input.is_action_pressed("backward") as u32 as f32
|
|
} else {
|
|
Vector3::ZERO
|
|
};
|
|
self.movement_dir = input_direction;
|
|
|
|
if let Some(mesh) = &mut self.soldier_mesh {
|
|
mesh.bind_mut()
|
|
.update_movement(Vector2::new(self.movement_dir.x, -self.movement_dir.z));
|
|
|
|
mesh.bind_mut().update_y_look(self.look_up);
|
|
}
|
|
|
|
if self.is_dead
|
|
&& let Some(target) = &self.dead_cam_target
|
|
&& let Some(camera) = &mut self.camera
|
|
{
|
|
let camera_prev_pos = camera.get_global_position();
|
|
let new_pos = camera_prev_pos.lerp(target.get_global_position(), 0.1);
|
|
camera.set_global_position(new_pos);
|
|
}
|
|
|
|
if self.movement_dir.length_squared() > 0. && self.base().is_on_floor() && !self.is_dead {
|
|
if let Some(effects) = &mut self.effects {
|
|
effects.bind_mut().walk();
|
|
}
|
|
}
|
|
if let Some(weapon) = &mut self.weapon {
|
|
let (target_y, target_x) = if self.movement_dir.length_squared() > 0. {
|
|
(
|
|
(self.time_passed * 8.).sin() / 30.,
|
|
(self.time_passed * 4.).sin() / 40.,
|
|
)
|
|
} else {
|
|
(0., 0.)
|
|
};
|
|
let done_cd_percent = if weapon.dyn_bind().get_max_cd() > 0. {
|
|
1. - (weapon.dyn_bind().get_remaining_cd() / weapon.dyn_bind().get_max_cd())
|
|
} else {
|
|
1.
|
|
};
|
|
let new_weapon_rot = weapon
|
|
.get_rotation()
|
|
.lerp(Vector3::ZERO, 0.03 * done_cd_percent as f32);
|
|
weapon.set_rotation(new_weapon_rot);
|
|
|
|
let prev_weapon_pos = weapon.get_position();
|
|
let new_y = prev_weapon_pos.y.lerp(target_y as f32, 0.2);
|
|
let new_x = prev_weapon_pos.x.lerp(target_x as f32, 0.2);
|
|
let new_z = prev_weapon_pos.z.lerp(0., 0.2);
|
|
let new_weapon_pos = Vector3 {
|
|
y: new_y,
|
|
x: new_x,
|
|
z: new_z,
|
|
};
|
|
weapon.set_position(new_weapon_pos);
|
|
}
|
|
|
|
if self.buffs.update(delta) {
|
|
self.buffs.clone().update_effects(self);
|
|
}
|
|
}
|
|
|
|
fn input(&mut self, event: Gd<InputEvent>) {
|
|
if self.locked {
|
|
if event.is_action_pressed("throw_telegrenade") && self.telegrenades_allowed {
|
|
if let Some(telegrenade) = &mut self.telegrenade {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, socket_addr) => {
|
|
peer.send_reliable(socket_addr, Package::TelegrenadeActivate);
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
let position = telegrenade.get_global_position();
|
|
self.telegrenade_teleport(position);
|
|
peer.broadcast_reliable(Package::TelegrenadeTeleport(
|
|
self.player_id.unwrap_or(0),
|
|
position.into(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(pos) = self.raycast_from_camera() {
|
|
let dir = if let Some(origin) = &self.camera_origin {
|
|
pos - origin.get_global_position()
|
|
} else {
|
|
Vector3::ZERO
|
|
};
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, addr) => {
|
|
if self.try_throw_telegrenade(dir) {
|
|
peer.send_reliable(addr, ThrowTelegrenade(0, dir.into()));
|
|
}
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
if self.try_throw_telegrenade(dir) {
|
|
peer.broadcast_reliable(ThrowTelegrenade(
|
|
self.player_id.unwrap_or(0),
|
|
dir.into(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if event.is_action_pressed("shoot") {
|
|
if let Some(pos) = self.raycast_from_camera() {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, addr) => {
|
|
if self.try_shoot(pos, false) {
|
|
peer.send_reliable(
|
|
addr,
|
|
Shoot(0, pos.into(), self.get_transform(), self.look_up),
|
|
);
|
|
}
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
if self.try_shoot(pos, true) {
|
|
peer.broadcast_reliable(Shoot(
|
|
self.player_id.unwrap_or(0),
|
|
pos.into(),
|
|
self.get_transform(),
|
|
self.look_up,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if event.is_action_pressed("punch") {
|
|
if let Some(pos) = self.raycast_from_camera() {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, addr) => {
|
|
if self.try_punch(pos, false) {
|
|
peer.send_reliable(
|
|
addr,
|
|
Punch(0, pos.into(), self.get_transform(), self.look_up),
|
|
);
|
|
}
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
if self.try_punch(pos, true) {
|
|
peer.broadcast_reliable(Punch(
|
|
self.player_id.unwrap_or(0),
|
|
pos.into(),
|
|
self.get_transform(),
|
|
self.look_up,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
let weapon_type = if event.is_action_pressed("weapon_1") {
|
|
Some(WeaponType::Raygun)
|
|
} else if event.is_action_pressed("weapon_2") {
|
|
Some(WeaponType::Shotgun)
|
|
} else if (event.is_action_pressed("swap_weapon_up")
|
|
|| event.is_action_pressed("swap_weapon_down"))
|
|
&& let Some(game) = Game::singleton()
|
|
&& let Some(player) = game.bind().find_player(self.player_id.unwrap_or(0))
|
|
{
|
|
match player.last_held_weapon {
|
|
WeaponType::Raygun => Some(WeaponType::Shotgun),
|
|
WeaponType::Shotgun => Some(WeaponType::Raygun),
|
|
WeaponType::Ball => None,
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
if let Some(weapon_type) = weapon_type {
|
|
self.swap_weapon(weapon_type, true);
|
|
match peer {
|
|
PeerKind::Client(peer, socket_addr) => {
|
|
peer.send_reliable(socket_addr, Package::SwapWeapon(0, weapon_type));
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
if let Some(game) = &mut Game::singleton()
|
|
&& let Some(player) =
|
|
game.bind_mut().find_player_mut(self.player_id.unwrap_or(0))
|
|
{
|
|
player.last_held_weapon = weapon_type;
|
|
}
|
|
peer.broadcast_reliable(Package::SwapWeapon(
|
|
self.player_id.unwrap_or(0),
|
|
weapon_type,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Ok(event) = event.try_cast::<InputEventMouseMotion>() {
|
|
if !self.is_dead() {
|
|
let x = event.get_relative().x * 0.001 * self.look_sensitivity;
|
|
let y = event.get_relative().y * 0.001 * self.look_sensitivity;
|
|
|
|
if let Some(camera) = self.camera.as_mut() {
|
|
let curr_rotation = camera.get_rotation();
|
|
let x_rot =
|
|
(curr_rotation.x - y * self.look_sensitivity).clamp(-PI / 2., PI / 2.);
|
|
camera.set_rotation(Vector3::RIGHT * x_rot);
|
|
self.look_up = x_rot;
|
|
}
|
|
|
|
let y_rot = -x * self.look_sensitivity;
|
|
self.y_rot = self.base().get_global_rotation().y + y_rot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn physics_process(&mut self, delta: f64) {
|
|
let rot = self.y_rot;
|
|
let new_rot = Vector3 {
|
|
y: rot,
|
|
..self.base().get_global_rotation()
|
|
};
|
|
self.base_mut().set_global_rotation(new_rot);
|
|
|
|
self.player_update(delta);
|
|
}
|
|
|
|
fn exit_tree(&mut self) {
|
|
if let Some(game) = Game::singleton()
|
|
&& !game.bind().game_started
|
|
{
|
|
self.set_lock(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[godot_api]
|
|
impl LocalPlayer {
|
|
#[signal]
|
|
pub fn on_die(source: DamageSourceSignal, player: u16, killbox_kind: KillboxKind);
|
|
#[signal]
|
|
pub fn on_take_damage(
|
|
source: DamageSourceSignal,
|
|
source_player: u16,
|
|
killbox_kind: KillboxKind,
|
|
damage: i32,
|
|
);
|
|
#[signal]
|
|
pub fn on_heal(amount: i32);
|
|
|
|
fn set_lock(&mut self, locked: bool) {
|
|
self.locked = locked;
|
|
if self.locked {
|
|
Input::singleton().set_mouse_mode(MouseMode::CAPTURED);
|
|
} else {
|
|
Input::singleton().set_mouse_mode(MouseMode::VISIBLE);
|
|
}
|
|
}
|
|
|
|
fn spawn_weapon(&mut self, mut weapon: DynGd<Node3D, dyn Weapon>) {
|
|
let prev_cd = if let Some(mut weapon) = self.weapon.take() {
|
|
let prev_cd = weapon.dyn_bind().get_remaining_cd();
|
|
weapon.queue_free();
|
|
prev_cd
|
|
} else {
|
|
0.
|
|
};
|
|
if let Some(weapon_node) = &mut self.weapon_node {
|
|
weapon.dyn_bind_mut().set_remaining_cd(prev_cd);
|
|
weapon_node.add_child(&weapon);
|
|
self.weapon = Some(weapon);
|
|
}
|
|
}
|
|
|
|
#[func]
|
|
pub fn exit_to_lobby(&mut self) {
|
|
if let Some(game) = &mut Game::singleton() {
|
|
game.bind_mut().finish_game();
|
|
}
|
|
}
|
|
|
|
#[func]
|
|
pub fn exit(&mut self) {
|
|
GameManager::singleton().bind_mut().exit_game();
|
|
NetworkManager::singleton().bind_mut().disconnect();
|
|
}
|
|
|
|
pub fn update_settings(&mut self, settings: &GameSettings) {
|
|
if let Some(camera) = &mut self.camera {
|
|
camera.set_fov(settings.fov as f32);
|
|
}
|
|
}
|
|
|
|
pub fn play_local_hit_sound(&mut self) {
|
|
if let Some(hit_sound) = &mut self.hit_sound {
|
|
hit_sound.play();
|
|
}
|
|
}
|
|
|
|
fn raycast_from_camera(&self) -> Option<Vector3> {
|
|
if let Some(camera) = &self.camera {
|
|
let forward = camera.get_global_basis().col_c().neg();
|
|
match cast_ray(
|
|
camera.get_global_position(),
|
|
camera.get_global_position() + forward,
|
|
&self.base().get_world_3d(),
|
|
&Array::from_iter(vec![self.base().get_rid()]),
|
|
) {
|
|
Some(res) => Some(res.position),
|
|
None => Some(camera.get_global_position() + forward * 100.),
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn set_vignette_color(&mut self, color: Color) {
|
|
if let Some(rect) = &mut self.vignette
|
|
&& let Some(mut mat) = rect.get_material()
|
|
{
|
|
mat.set("shader_parameter/Color", &color.to_variant());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[godot_dyn]
|
|
impl Shootable for LocalPlayer {
|
|
fn on_shoot(&mut self, source: DamageSource, damage: i32, _: Vector3) {
|
|
self.take_damage(source, damage, true);
|
|
}
|
|
}
|