mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Gave mindflayer AI.
Fixed particles and made them sync across network.
This commit is contained in:
parent
a5b7477e96
commit
720482d994
@ -1,10 +1,10 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.50,
|
||||
recover_duration: 0.50,
|
||||
beam_duration: 0.5,
|
||||
damage: 200,
|
||||
tick_rate: 2.0,
|
||||
range: 10.0,
|
||||
beam_duration: 1.0,
|
||||
damage: 350,
|
||||
tick_rate: 0.9,
|
||||
range: 20.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Cursed,
|
||||
|
@ -35,6 +35,7 @@ pub enum Tactic {
|
||||
Turret,
|
||||
FixedTurret,
|
||||
RotatingTurret,
|
||||
Mindflayer,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -130,6 +130,9 @@ impl Health {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fraction of health an entity has remaining
|
||||
pub fn fraction(&self) -> f32 { self.current as f32 / self.maximum as f32 }
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -92,6 +92,12 @@ impl CharacterBehavior for Data {
|
||||
)
|
||||
.build();
|
||||
|
||||
let alignment = if matches!(data.alignment, Some(comp::Alignment::Enemy)) {
|
||||
comp::Alignment::Enemy
|
||||
} else {
|
||||
comp::Alignment::Owned(*data.uid)
|
||||
};
|
||||
|
||||
update.server_events.push_front(ServerEvent::CreateNpc {
|
||||
pos: *data.pos,
|
||||
stats,
|
||||
@ -103,7 +109,7 @@ impl CharacterBehavior for Data {
|
||||
loadout,
|
||||
body,
|
||||
agent: Some(comp::Agent::new(None, false, None, &body, true)),
|
||||
alignment: comp::Alignment::Owned(*data.uid),
|
||||
alignment,
|
||||
scale: self
|
||||
.static_data
|
||||
.summon_info
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction, Controller,
|
||||
ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory, InventoryAction, Melee,
|
||||
Ori, PhysicsState, Pos, StateUpdate, Stats, Vel,
|
||||
self, item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction,
|
||||
Controller, ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory,
|
||||
InventoryAction, Melee, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel,
|
||||
},
|
||||
resources::DeltaTime,
|
||||
uid::Uid,
|
||||
@ -93,6 +93,7 @@ pub struct JoinData<'a> {
|
||||
pub stats: &'a Stats,
|
||||
pub msm: &'a MaterialStatManifest,
|
||||
pub combo: &'a Combo,
|
||||
pub alignment: Option<&'a comp::Alignment>,
|
||||
}
|
||||
|
||||
type RestrictedMut<'a, C> = PairedStorage<
|
||||
@ -121,6 +122,7 @@ pub struct JoinStruct<'a> {
|
||||
pub beam: Option<&'a Beam>,
|
||||
pub stat: &'a Stats,
|
||||
pub combo: &'a Combo,
|
||||
pub alignment: Option<&'a comp::Alignment>,
|
||||
}
|
||||
|
||||
impl<'a> JoinData<'a> {
|
||||
@ -150,6 +152,7 @@ impl<'a> JoinData<'a> {
|
||||
dt,
|
||||
msm,
|
||||
combo: j.combo,
|
||||
alignment: j.alignment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use specs::{
|
||||
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
inventory::{
|
||||
item::MaterialStatManifest,
|
||||
slot::{EquipSlot, Slot},
|
||||
@ -71,6 +72,7 @@ pub struct ReadData<'a> {
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
msm: Read<'a, MaterialStatManifest>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
alignments: ReadStorage<'a, comp::Alignment>,
|
||||
}
|
||||
|
||||
/// ## Character Behavior System
|
||||
@ -255,6 +257,7 @@ impl<'a> System<'a> for Sys {
|
||||
beam: read_data.beams.get(entity),
|
||||
stat: &stat,
|
||||
combo: &combo,
|
||||
alignment: read_data.alignments.get(entity),
|
||||
};
|
||||
|
||||
for action in actions {
|
||||
|
@ -60,6 +60,8 @@ struct AgentData<'a> {
|
||||
light_emitter: Option<&'a LightEmitter>,
|
||||
glider_equipped: bool,
|
||||
is_gliding: bool,
|
||||
health: &'a Health,
|
||||
char_state: &'a CharacterState,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -145,14 +147,17 @@ impl<'a> System<'a> for Sys {
|
||||
read_data.light_emitter.maybe(),
|
||||
read_data.groups.maybe(),
|
||||
read_data.mount_states.maybe(),
|
||||
&read_data.char_states,
|
||||
)
|
||||
.par_join()
|
||||
.filter(|(_, _, _, _, _, _, _, _, _, _, _, _, _, _, mount_state)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.filter(
|
||||
|(_, _, _, _, _, _, _, _, _, _, _, _, _, _, mount_state, _)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
},
|
||||
)
|
||||
.for_each_init(
|
||||
|| {
|
||||
prof_span!(guard, "agent rayon job");
|
||||
@ -175,6 +180,7 @@ impl<'a> System<'a> for Sys {
|
||||
light_emitter,
|
||||
groups,
|
||||
_,
|
||||
char_state,
|
||||
)| {
|
||||
//// Hack, replace with better system when groups are more sophisticated
|
||||
//// Override alignment if in a group unless entity is owned already
|
||||
@ -269,6 +275,8 @@ impl<'a> System<'a> for Sys {
|
||||
light_emitter,
|
||||
glider_equipped,
|
||||
is_gliding,
|
||||
health,
|
||||
char_state,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
@ -1189,7 +1197,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_pos: &Pos,
|
||||
tgt_body: Option<&Body>,
|
||||
dt: &DeltaTime,
|
||||
_read_data: &ReadData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
let min_attack_dist = self.body.map_or(3.0, |b| b.radius() * self.scale + 2.0);
|
||||
let tactic = match self
|
||||
@ -1236,6 +1244,7 @@ impl<'a> AgentData<'a> {
|
||||
Some(ToolKind::Unique(UniqueKind::TheropodBasic)) => Tactic::Theropod,
|
||||
Some(ToolKind::Unique(UniqueKind::TheropodBird)) => Tactic::Theropod,
|
||||
Some(ToolKind::Unique(UniqueKind::ObjectTurret)) => Tactic::Turret,
|
||||
Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => Tactic::Mindflayer,
|
||||
_ => Tactic::Melee,
|
||||
};
|
||||
|
||||
@ -2193,6 +2202,37 @@ impl<'a> AgentData<'a> {
|
||||
agent.target = None;
|
||||
}
|
||||
},
|
||||
Tactic::Mindflayer => {
|
||||
agent.action_timer += dt.0;
|
||||
const MINDFLAYER_ATTACK_DIST: f32 = 15.0;
|
||||
let mindflayer_is_far = dist_sqrd > MINDFLAYER_ATTACK_DIST.powi(2);
|
||||
if mindflayer_is_far && agent.action_timer / self.health.fraction() > 5.0 {
|
||||
if !self.char_state.is_attack() {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
agent.action_timer = 0.0;
|
||||
}
|
||||
} else if mindflayer_is_far {
|
||||
controller.actions.push(ControlAction::StartInput {
|
||||
input: InputKind::Ability(0),
|
||||
target_entity: agent
|
||||
.target
|
||||
.as_ref()
|
||||
.and_then(|t| read_data.uids.get(t.target))
|
||||
.copied(),
|
||||
select_pos: None,
|
||||
});
|
||||
} else if self.health.fraction() < 0.5 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,13 +190,12 @@ impl ParticleMgr {
|
||||
|
||||
// add new Particle
|
||||
self.maintain_body_particles(scene_data);
|
||||
self.maintain_boost_particles(scene_data);
|
||||
self.maintain_char_state_particles(scene_data);
|
||||
self.maintain_beam_particles(scene_data, lights);
|
||||
self.maintain_block_particles(scene_data, terrain);
|
||||
self.maintain_shockwave_particles(scene_data);
|
||||
self.maintain_aura_particles(scene_data);
|
||||
self.maintain_buff_particles(scene_data);
|
||||
self.maintain_spin_melee_particles(scene_data);
|
||||
} else {
|
||||
// remove all particle lifespans
|
||||
self.particles.clear();
|
||||
@ -412,11 +411,11 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_boost_particles(&mut self, scene_data: &SceneData) {
|
||||
fn maintain_char_state_particles(&mut self, scene_data: &SceneData) {
|
||||
span!(
|
||||
_guard,
|
||||
"boost_particles",
|
||||
"ParticleMgr::maintain_boost_particles"
|
||||
"char_state_particles",
|
||||
"ParticleMgr::maintain_char_state_particles"
|
||||
);
|
||||
let state = scene_data.state;
|
||||
let ecs = state.ecs();
|
||||
@ -431,19 +430,61 @@ impl ParticleMgr {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if let CharacterState::Boost(_) = character_state {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(10))),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_secs(15),
|
||||
time,
|
||||
ParticleMode::CampfireSmoke,
|
||||
pos.0 + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::<f32>()),
|
||||
)
|
||||
},
|
||||
);
|
||||
match character_state {
|
||||
CharacterState::Boost(_) => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(10))),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_secs(15),
|
||||
time,
|
||||
ParticleMode::CampfireSmoke,
|
||||
pos.0 + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::<f32>()),
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
CharacterState::SpinMelee(spin) => {
|
||||
if let Some(specifier) = spin.static_data.specifier {
|
||||
match specifier {
|
||||
states::spin_melee::FrontendSpecifier::CultistVortex => {
|
||||
if matches!(spin.stage_section, states::utils::StageSection::Swing)
|
||||
{
|
||||
let heartbeats =
|
||||
self.scheduler.heartbeats(Duration::from_millis(3));
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ spin.static_data.range.powi(2) as usize
|
||||
* usize::from(heartbeats)
|
||||
/ 150,
|
||||
|| {
|
||||
let rand_dist = spin.static_data.range
|
||||
* (1.0 - rng.gen::<f32>().powi(10));
|
||||
let init_pos = Vec3::new(
|
||||
2.0 * rng.gen::<f32>() - 1.0,
|
||||
2.0 * rng.gen::<f32>() - 1.0,
|
||||
0.0,
|
||||
)
|
||||
.normalized()
|
||||
* rand_dist
|
||||
+ pos.0
|
||||
+ Vec3::unit_z() * 0.05;
|
||||
Particle::new_directed(
|
||||
Duration::from_millis(900),
|
||||
time,
|
||||
ParticleMode::CultistFlame,
|
||||
init_pos,
|
||||
pos.0,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -863,56 +904,6 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_spin_melee_particles(&mut self, scene_data: &SceneData) {
|
||||
let state = scene_data.state;
|
||||
let ecs = state.ecs();
|
||||
let time = state.get_time();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for (pos, character_state) in (
|
||||
&ecs.read_storage::<Pos>(),
|
||||
&ecs.read_storage::<CharacterState>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if let CharacterState::SpinMelee(c) = character_state {
|
||||
if let Some(specifier) = c.static_data.specifier {
|
||||
match specifier {
|
||||
states::spin_melee::FrontendSpecifier::CultistVortex => {
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(3));
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ c.static_data.range.powi(2) as usize
|
||||
* usize::from(heartbeats)
|
||||
/ 150,
|
||||
|| {
|
||||
let rand_dist =
|
||||
c.static_data.range * (1.0 - rng.gen::<f32>().powi(10));
|
||||
let init_pos = Vec3::new(
|
||||
2.0 * rng.gen::<f32>() - 1.0,
|
||||
2.0 * rng.gen::<f32>() - 1.0,
|
||||
0.0,
|
||||
)
|
||||
.normalized()
|
||||
* rand_dist
|
||||
+ pos.0
|
||||
+ Vec3::unit_z() * 0.05;
|
||||
Particle::new_directed(
|
||||
Duration::from_millis(900),
|
||||
time,
|
||||
ParticleMode::CultistFlame,
|
||||
init_pos,
|
||||
pos.0,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn upload_particles(&mut self, renderer: &mut Renderer) {
|
||||
span!(_guard, "upload_particles", "ParticleMgr::upload_particles");
|
||||
let all_cpu_instances = self
|
||||
|
Loading…
Reference in New Issue
Block a user