use std::f32::consts::PI;
use bevy::{math::vec2, prelude::*};
use bevy_rapier2d::prelude::*;
use component::*;
use rand::Rng;
use starkingdoms_common::packet::Packet;
use starkingdoms_common::packet::Part;
use starkingdoms_common::proto_part_flags;
use starkingdoms_common::proto_transform;
use crate::ws::PacketMessageConvert;
use crate::{
capacity, config::StkConfig, part, planet::PlanetType, player::component::Player, ws::WsEvent,
};
use starkingdoms_common::PartType as c_PartType;
use starkingdoms_common::PlanetType as c_PlanetType;
pub mod component;
pub mod save;
pub mod thruster;
// half size of hearty
pub static PART_HALF_SIZE: f32 = 25.0;
pub fn module_spawn(
mut commands: Commands,
time: Res<Time>,
mut module_timer: ResMut<ModuleTimer>,
part_query: Query<&PartType, Without<Attach>>,
mut packet_send: EventWriter<WsEvent>,
server_config: Res<StkConfig>,
) {
if module_timer.0.tick(time.delta()).just_finished()
&& part_query.iter().count() < server_config.server.max_free_parts
{
let angle: f32 = {
let mut rng = rand::thread_rng();
rng.gen::<f32>() * std::f32::consts::PI * 2.
};
let mut transform = Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0);
transform.rotate_z(angle);
transform.translation += Vec3::new(6000.0, 0.0, 0.0);
let flags = PartFlags { attached: false };
let mut entity = commands.spawn(PartBundle {
part_type: c_PartType::Cargo.into(),
transform: TransformBundle::from(transform),
flags,
..default()
});
entity
.with_children(|children| {
children
.spawn(Collider::cuboid(0.375, 0.46875))
.insert(TransformBundle::from(Transform::from_xyz(0., 0.03125, 0.)));
})
.insert(AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: vec2(0.0, 0.0),
mass: part!(c_PartType::Cargo.into()).mass,
principal_inertia: 7.5,
}));
let packet = Packet::SpawnPart {
id: entity.id().index(),
part: Part {
part_type: c_PartType::Cargo.into(),
transform: proto_transform!(transform),
flags: proto_part_flags!(flags),
},
};
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
}
}
pub fn detach_recursive(
commands: &mut Commands,
this: Entity,
attached_query: &mut Query<
(
Entity,
&PartType,
&mut Transform,
&mut Attach,
&Velocity,
Option<&CanAttach>,
Option<&LooseAttach>,
&mut PartFlags,
),
(Without<PlanetType>, Without<Player>),
>,
player_query: &mut Query<
(
Entity,
&mut Player,
&Transform,
&Velocity,
&mut Attach,
&mut PartFlags,
),
Without<PlanetType>,
>,
) -> u32 {
let mut energy = 0;
let f_attach = if attached_query.contains(this) {
attached_query.get(this).unwrap().3.clone()
} else {
player_query.get(this).unwrap().4.clone()
};
for child in f_attach.children.iter().flatten() {
energy += detach_recursive(commands, *child, attached_query, player_query);
}
let (entity, part_type, attach, mut flags) = if attached_query.contains(this) {
let (entity, part_type, _, attach, _, _, _, flags) = attached_query.get_mut(this).unwrap();
(entity, part_type, attach, flags)
} else {
let (entity, _, _, _, attach, part_flags) = player_query.get_mut(this).unwrap();
(entity, &c_PartType::Hearty.into(), attach, part_flags)
};
energy += capacity!(*part_type);
commands.entity(entity).remove::<Attach>();
flags.attached = false;
if *part_type == c_PartType::LandingThrusterSuspension.into() {
let parent = attach.parent.unwrap();
let parent_attach = attached_query.get(parent).unwrap().3;
commands.entity(parent).insert(LooseAttach {
children: parent_attach.children,
});
} else {
commands.entity(entity).remove::<ImpulseJoint>();
}
let parent = f_attach.parent.unwrap();
if attached_query.contains(parent) {
let mut parent_attach = attached_query.get_mut(parent).unwrap().3;
let children = parent_attach.children;
for (i, child) in children.iter().enumerate() {
if let Some(child) = child {
if *child == entity {
parent_attach.children[i] = None;
}
}
}
} else if player_query.contains(parent) {
let mut parent_attach = player_query.get_mut(parent).unwrap().4;
for (i, child) in parent_attach.children.clone().iter().enumerate() {
if let Some(child) = child {
if *child == entity {
parent_attach.children[i] = None;
}
}
}
}
energy
}
pub fn despawn_module_tree(
commands: &mut Commands,
attach: &Attach,
attached_query: &Query<&Attach, With<PartType>>,
part_query: &Query<&PartType>,
packets: &mut Vec<WsEvent>,
) {
for child in attach.children.iter().flatten() {
commands.entity(*child).despawn_recursive();
let packet = Packet::DespawnPart {
id: (*child).index(),
};
packets.push(WsEvent::Broadcast {
message: packet.into_message(),
});
let attach = match attached_query.get(*child) {
Ok(s) => s,
Err(_) => match part_query.get(*child) {
Ok(_) => {
continue;
}
Err(e) => panic!("{}", e),
},
};
despawn_module_tree(commands, attach, attached_query, part_query, packets);
}
}
pub fn attach_on_module_tree(
x: f32,
y: f32,
commands: &mut Commands,
this: Entity,
select: Entity,
player_id: Entity,
attached_query: &mut Query<
(
Entity,
&PartType,
&mut Transform,
&mut Attach,
&Velocity,
Option<&CanAttach>,
Option<&LooseAttach>,
&mut PartFlags,
),
(Without<PlanetType>, Without<Player>),
>,
part_query: &mut Query<
(
Entity,
&PartType,
&mut Transform,
&mut Velocity,
Option<&LooseAttach>,
&mut PartFlags,
),
(Without<PlanetType>, Without<Player>, Without<Attach>),
>,
player_query: &mut Query<
(
Entity,
&mut Player,
&Transform,
&Velocity,
&mut Attach,
&mut PartFlags,
),
Without<PlanetType>,
>,
) -> bool {
let mut ret = false;
let attach = if attached_query.contains(this) {
attached_query.get(this).unwrap().3
} else {
player_query.get(this).unwrap().4
};
for this in attach.clone().children.iter().flatten() {
let module = part_query.get(select).unwrap();
let part_type = *module.1;
ret |= if part_type != c_PartType::LandingThrusterSuspension.into() {
attach_on_module_tree(
x,
y,
commands,
*this,
select,
player_id,
attached_query,
part_query,
player_query,
)
} else {
false
};
}
let is_player = player_query.contains(this);
let (entity, transform, mut attach, velocity, can_attach) = if attached_query.contains(this) {
let (entity, _, transform, attach, velocity, can_attach, _, _) =
attached_query.get_mut(this).unwrap();
(entity, *transform, attach, velocity, can_attach)
} else {
let (entity, _, transform, velocity, attach, _) = player_query.get_mut(this).unwrap();
(entity, *transform, attach, velocity, Some(&CanAttach(15)))
};
let p_pos = transform.translation;
let (rel_x, rel_y) = (p_pos.x - x, p_pos.y - y);
let angle = transform.rotation.to_euler(EulerRot::ZYX).0;
let (rel_x, rel_y) = (
rel_x * (-angle).cos() - rel_y * (-angle).sin(),
rel_x * (-angle).sin() + rel_y * (-angle).cos(),
);
let mut module = part_query.get_mut(select).unwrap();
let attachable = match can_attach {
Some(s) => s.0,
None => return false,
};
let mut attachment_slot = 5;
let mut offset = Vec2::ZERO;
let mut angle_offset = 0.;
if attach.children[2].is_none() // top
&& attachable & 4 != 0
&& -(rel_x.abs()) + rel_y > 0.0
&& rel_x.abs() + rel_y < 1.0
{
attachment_slot = 2;
offset = Vec2::new(0., -1.06);
angle_offset = 0.;
} else if attach.children[0].is_none() // bottom
&& rel_x.abs() + rel_y < 0.0
&& -(rel_x.abs()) + rel_y > -1.0
{
attachment_slot = 0;
offset = Vec2::new(0., 1.06);
angle_offset = PI;
} else if attach.children[1].is_none() // left
&& rel_x + rel_y.abs() < 0.0
&& rel_x - rel_y.abs() > -1.0
{
attachment_slot = 1;
offset = Vec2::new(1.06, 0.);
angle_offset = PI / 2.;
} else if attach.children[3].is_none() // right
&& rel_x - rel_y.abs() > 0.0
&& rel_x + rel_y.abs() < 1.0
{
attachment_slot = 3;
offset = Vec2::new(-1.06, 0.);
angle_offset = -PI / 2.;
}
let new_transform = Transform::from_xyz(
p_pos.x + offset.x * angle.cos() - offset.y * angle.sin(),
p_pos.y + offset.x * angle.sin() + offset.y * angle.cos(),
0.,
)
.with_rotation(Quat::from_euler(
EulerRot::ZYX,
angle + angle_offset,
0.,
0.,
));
if attachment_slot != 5 {
let associated_player = if is_player {
Some(entity)
} else {
attach.associated_player
};
*module.2 = new_transform;
module.3.linvel = velocity.linvel;
let joint = FixedJointBuilder::new()
.local_anchor1(offset)
.local_basis1(angle_offset);
let mut children = [None, None, None, None];
if let Some(loose_attach) = module.4 {
if *module.1 == c_PartType::LandingThruster.into() {
commands
.entity(loose_attach.children[2].unwrap())
.insert(Attach {
associated_player,
parent: Some(module.0),
children: [None, None, None, None],
});
}
children = loose_attach.children;
commands.entity(entity).remove::<LooseAttach>();
}
let mut module_entity = commands.entity(module.0);
module_entity.insert(ImpulseJoint::new(entity, joint));
module_entity.insert(Attach {
associated_player,
parent: Some(entity),
children,
});
module_entity.insert(CanAttach(15));
attach.children[attachment_slot] = Some(module.0);
module.5.attached = true;
player_query.get_mut(player_id).unwrap().1.energy_capacity += capacity!(*module.1);
if *module.1 == c_PartType::LandingThruster.into() {
let loose_attach = module.4.unwrap().clone();
let mut transform = part_query
.get_mut(loose_attach.children[2].unwrap())
.unwrap()
.2;
*transform = new_transform;
}
ret = true;
}
ret
}
pub fn convert_modules(
mut commands: Commands,
rapier_context: Res<RapierContext>,
planet_query: Query<(Entity, &PlanetType, &Children)>,
mut player_query: Query<(&Attach, &mut Player)>,
mut attached_query: Query<
(
Entity,
&mut PartType,
&mut Attach,
&mut AdditionalMassProperties,
&Children,
&Transform,
&PartFlags,
),
Without<Player>,
>,
mut collider_query: Query<
(&mut Collider, &mut Transform, &Parent),
(Without<Player>, Without<Attach>),
>,
mut packet_send: EventWriter<WsEvent>,
) {
for (_planet_entity, planet_type, children) in &planet_query {
for (entity1, entity2, intersecting) in
rapier_context.intersection_pairs_with(*children.first().unwrap())
{
if intersecting {
let other = if *children.first().unwrap() == entity1 {
entity2
} else {
entity1
};
let player_entity = if player_query.contains(other) {
other
} else if attached_query.contains(other) {
attached_query
.get(other)
.unwrap()
.2
.associated_player
.unwrap()
} else if collider_query.contains(other) {
let parent = collider_query.get(other).unwrap().2.get();
if attached_query.contains(parent) {
attached_query
.get(parent)
.unwrap()
.2
.associated_player
.unwrap()
} else {
continue;
}
} else {
continue;
};
let mut player = player_query.get_mut(player_entity).unwrap();
let increase_capacity_by = convert_modules_recursive(
&mut commands,
*planet_type,
player.0.clone(),
&mut attached_query,
&mut collider_query,
&mut packet_send,
);
player.1.energy_capacity += increase_capacity_by;
player.1.energy = player.1.energy_capacity;
}
}
}
}
fn convert_modules_recursive(
commands: &mut Commands,
planet_type: PlanetType,
attach: Attach,
attached_query: &mut Query<
(
Entity,
&mut PartType,
&mut Attach,
&mut AdditionalMassProperties,
&Children,
&Transform,
&PartFlags,
),
Without<Player>,
>,
collider_query: &mut Query<
(&mut Collider, &mut Transform, &Parent),
(Without<Player>, Without<Attach>),
>,
packet_send: &mut EventWriter<WsEvent>,
) -> u32 {
let mut increase_capacity_by = 0;
for child in attach.children.iter().flatten() {
let (
module_entity,
mut part_type,
mut attach,
mut mass_prop,
children,
module_transform,
part_flags,
) = attached_query.get_mut(*child).unwrap();
if *part_type == c_PartType::Cargo.into() {
match planet_type.0 {
c_PlanetType::Mars => {
*part_type = c_PartType::Hub.into();
*mass_prop = AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: Vec2::new(0.0, 0.0),
mass: part!(c_PartType::Hub.into()).mass,
principal_inertia: 7.5,
});
let (mut collider, mut transform, _) =
collider_query.get_mut(*children.first().unwrap()).unwrap();
*collider = Collider::cuboid(0.5, 0.5);
*transform = Transform::from_xyz(0., 0., 0.);
commands
.get_entity(module_entity)
.unwrap()
.remove::<CanAttach>();
commands
.get_entity(module_entity)
.unwrap()
.insert(CanAttach(15));
increase_capacity_by += part!(c_PartType::Hub.into()).energy_capacity;
let packet = Packet::DespawnPart { id: child.index() };
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
let packet = Packet::SpawnPart {
id: child.index(),
part: Part {
part_type: c_PartType::Hub.into(),
transform: proto_transform!(transform),
flags: proto_part_flags!(part_flags),
},
};
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
}
c_PlanetType::Moon => {
*part_type = c_PartType::LandingThruster.into();
*mass_prop = AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: Vec2::new(0.0, 0.0),
mass: part!(c_PartType::LandingThruster.into()).mass,
principal_inertia: 7.5,
});
let (mut collider, mut transform, _) =
collider_query.get_mut(*children.first().unwrap()).unwrap();
*collider = Collider::cuboid(0.5, 0.375);
*transform = Transform::from_xyz(0., 0.125, 0.);
//commands.get_entity(module_entity).unwrap().remove::<CanAttach>();
let joint = PrismaticJointBuilder::new(Vec2::new(0., 1.))
.local_anchor1(Vec2::new(0., 0.))
.local_anchor2(Vec2::new(0., 0.))
.set_motor(0., 0., 9000., 3000.)
.limits([0., 1.])
.build();
let mut suspension = commands.spawn(PartBundle {
transform: TransformBundle::from(*module_transform),
part_type: c_PartType::LandingThrusterSuspension.into(),
flags: PartFlags { attached: false },
..default()
});
suspension
.with_children(|children| {
children
.spawn(Collider::cuboid(0.5, 0.02))
.insert(TransformBundle::from(Transform::from_xyz(0., -0.48, 0.)));
})
.insert(ImpulseJoint::new(module_entity, joint))
.insert(AdditionalMassProperties::MassProperties(MassProperties {
local_center_of_mass: vec2(0.0, 0.0),
mass: 0.00000000000001,
principal_inertia: 0.00000000000001,
}))
.insert(Attach {
associated_player: attach.associated_player,
parent: Some(module_entity),
children: [None, None, None, None],
});
attach.children[2] = Some(suspension.id());
increase_capacity_by +=
part!(c_PartType::LandingThruster.into()).energy_capacity;
let packet = Packet::DespawnPart { id: child.index() };
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
let packet = Packet::SpawnPart {
id: child.index(),
part: Part {
part_type: c_PartType::LandingThruster.into(),
transform: proto_transform!(transform),
flags: proto_part_flags!(part_flags),
},
};
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
let packet = Packet::SpawnPart {
id: suspension.id().index(),
part: Part {
part_type: c_PartType::LandingThrusterSuspension.into(),
transform: proto_transform!(transform),
flags: proto_part_flags!(part_flags),
},
};
packet_send.send(WsEvent::Broadcast {
message: packet.into_message(),
});
}
_ => {}
}
}
if part_type.0 != c_PartType::LandingThruster {
increase_capacity_by += convert_modules_recursive(
commands,
planet_type,
attach.clone(),
attached_query,
collider_query,
packet_send,
);
}
}
increase_capacity_by
}
pub fn break_modules(
mut commands: Commands,
rapier_context: Res<RapierContext>,
mut attached_query: Query<
(
Entity,
&PartType,
&mut Transform,
&mut Attach,
&Velocity,
Option<&CanAttach>,
Option<&LooseAttach>,
&mut PartFlags,
),
(Without<PlanetType>, Without<Player>),
>,
mut player_query: Query<
(
Entity,
&mut Player,
&Transform,
&Velocity,
&mut Attach,
&mut PartFlags,
),
Without<PlanetType>,
>,
) {
let joints = rapier_context.entity2impulse_joint();
let mut detach_list = Vec::new();
for (entity, part_type, _, attach, _, _, _, mut flags) in &mut attached_query {
if part_type.0 == c_PartType::LandingThrusterSuspension {
continue;
}
let handle = match joints.get(&entity) {
Some(handle) => handle,
None => continue,
};
let joint = rapier_context.impulse_joints.get(*handle).unwrap();
if joint.impulses.magnitude() > 2.0 {
flags.attached = false;
detach_list.push((entity, *part_type, attach.clone()));
}
}
for (entity, _part_type, attach) in detach_list {
let lost_energy_capacity = detach_recursive(
&mut commands,
entity,
&mut attached_query,
&mut player_query,
);
let id = attach.associated_player.unwrap();
let mut player = player_query.get_mut(id).unwrap().1;
player.energy_capacity -= lost_energy_capacity;
player.energy = std::cmp::min(player.energy, player.energy_capacity);
}
}