Tornado summoning attack

This commit is contained in:
Snowram 2021-06-04 00:42:50 +02:00 committed by Robin Gilh
parent 97ce50e5d4
commit 3ba0500b90
18 changed files with 147 additions and 30 deletions

View File

@ -3,11 +3,16 @@ BasicSummon(
cast_duration: 1.0,
recover_duration: 0.5,
summon_amount: 6,
summon_distance: (1, 5),
summon_info: (
body: Object(Tornado),
scale: None,
health_scaling: 80,
health_scaling: None,
loadout_config: None,
skillset_config: None,
),
duration: Some((
secs: 5,
nanos: 0,
)),
)

View File

@ -3,14 +3,16 @@ BasicSummon(
cast_duration: 1.0,
recover_duration: 0.5,
summon_amount: 6,
summon_distance: (3, 3),
summon_info: (
body: BipedSmall((
species: Husk,
body_type: Male,
)),
scale: None,
health_scaling: 80,
health_scaling: Some(80),
loadout_config: Some(HuskSummon),
skillset_config: None,
),
duration: None,
)

View File

@ -3,11 +3,13 @@ BasicSummon(
cast_duration: 1.0,
recover_duration: 0.5,
summon_amount: 1,
summon_distance: (1, 1),
summon_info: (
body: Object(SeaLantern),
scale: None,
health_scaling: 0,
health_scaling: Some(0),
loadout_config: None,
skillset_config: None,
),
duration: None,
)

View File

@ -1,13 +1,13 @@
SpinMelee(
buildup_duration: 0.2,
swing_duration: 0.6,
recover_duration: 0.2,
base_damage: 70,
base_poise_damage: 25,
knockback: ( strength: 0.0, direction: Away),
buildup_duration: 0.0,
swing_duration: 0.5,
recover_duration: 0.0,
base_damage: 15000,
base_poise_damage: 200,
knockback: ( strength: 200.0, direction: Away),
range: 3.5,
damage_effect: None,
energy_cost: 100,
energy_cost: 0,
is_infinite: true,
movement_behavior: AxeHover,
is_interruptible: false,

View File

@ -0,0 +1,19 @@
ItemDef(
name: "Tornado",
description: "Tornado weapon",
kind: Tool((
kind: Natural,
hands: Two,
stats: Direct((
equip_time_secs: 0.01,
power: 1.0,
poise_strength: 1.0,
speed: 1.0,
crit_chance: 0.0,
crit_mult: 0.0,
)),
)),
quality: Low,
tags: [],
ability_spec: Some(Custom("Tornado")),
)

View File

@ -280,7 +280,9 @@ pub enum CharacterAbility {
cast_duration: f32,
recover_duration: f32,
summon_amount: u32,
summon_distance: (f32, f32),
summon_info: basic_summon::SummonInfo,
duration: Option<Duration>,
},
SelfBuff {
buildup_duration: f32,
@ -1737,15 +1739,19 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
cast_duration,
recover_duration,
summon_amount,
summon_distance,
summon_info,
duration,
} => CharacterState::BasicSummon(basic_summon::Data {
static_data: basic_summon::StaticData {
buildup_duration: Duration::from_secs_f32(*buildup_duration),
cast_duration: Duration::from_secs_f32(*cast_duration),
recover_duration: Duration::from_secs_f32(*recover_duration),
summon_amount: *summon_amount,
summon_distance: *summon_distance,
summon_info: *summon_info,
ability_info,
duration: *duration,
},
summon_count: 0,
timer: Duration::default(),

View File

@ -306,6 +306,9 @@ fn default_main_tool(body: &Body) -> Item {
object::Body::SeaLantern => Some(Item::new_from_asset_expect(
"common.items.npc_weapons.unique.tidal_totem",
)),
object::Body::Tornado => Some(Item::new_from_asset_expect(
"common.items.npc_weapons.unique.tornado",
)),
_ => None,
},
Body::BipedSmall(biped_small) => match (biped_small.species, biped_small.body_type) {

View File

@ -117,7 +117,7 @@ pub enum ServerEvent {
pos: comp::Pos,
stats: comp::Stats,
skill_set: comp::SkillSet,
health: comp::Health,
health: Option<comp::Health>,
poise: comp::Poise,
loadout: comp::inventory::loadout::Loadout,
body: comp::Body,
@ -127,6 +127,7 @@ pub enum ServerEvent {
home_chunk: Option<comp::HomeChunk>,
drop_item: Option<Item>,
rtsim_entity: Option<RtSimEntity>,
projectile: Option<comp::Projectile>,
},
CreateShip {
pos: comp::Pos,

View File

@ -2,7 +2,7 @@ use crate::{
comp::{
self,
inventory::loadout_builder::{self, LoadoutBuilder},
Behavior, BehaviorCapability, CharacterState, StateUpdate,
Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate,
},
event::{LocalEvent, ServerEvent},
outcome::Outcome,
@ -11,9 +11,13 @@ use crate::{
behavior::{CharacterBehavior, JoinData},
utils::*,
},
terrain::Block,
vol::ReadVol,
};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::{f32::consts::PI, time::Duration};
use vek::*;
/// Separated out to condense update portions of character state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -26,10 +30,14 @@ pub struct StaticData {
pub recover_duration: Duration,
/// How many creatures the state should summon
pub summon_amount: u32,
/// Range of the summons relative to the summonner
pub summon_distance: (f32, f32),
/// Information about the summoned creature
pub summon_info: SummonInfo,
/// Miscellaneous information about the ability
pub ability_info: AbilityInfo,
/// Duration of the summoned entity
pub duration: Option<Duration>,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -102,15 +110,67 @@ impl CharacterBehavior for Data {
};
let stats = comp::Stats::new("Summon".to_string());
let health_scaling = self
.static_data
.summon_info
.health_scaling
.map(|health_scaling| comp::Health::new(body, health_scaling));
// Ray cast to check where summon should happen
let summon_frac =
self.summon_count as f32 / self.static_data.summon_amount as f32;
let length = rand::thread_rng().gen_range(
self.static_data.summon_distance.0..self.static_data.summon_distance.1,
);
// Summon in a clockwise fashion
let ray_vector = Vec3::new(
(summon_frac * 2.0 * PI).sin() * length,
(summon_frac * 2.0 * PI).cos() * length,
data.body.eye_height(),
);
// Check for collision on the xy plane
let obstacle_xy = data
.terrain
.ray(data.pos.0, data.pos.0 + length * ray_vector)
.until(Block::is_solid)
.cast()
.0;
let collision_vector = Vec3::new(
data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy,
data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy,
data.pos.0.z,
);
// Check for collision in z up to 50 blocks
let obstacle_z = data
.terrain
.ray(collision_vector, collision_vector - Vec3::unit_z() * 50.0)
.until(Block::is_solid)
.cast()
.0;
// If a duration is specified, create a projectile componenent for the npc
let projectile = self.static_data.duration.map(|duration| Projectile {
hit_solid: Vec::new(),
hit_entity: Vec::new(),
time_left: duration,
owner: Some(*data.uid),
ignore_group: true,
is_sticky: false,
is_point: false,
});
// Send server event to create npc
update.server_events.push_front(ServerEvent::CreateNpc {
pos: *data.pos,
pos: comp::Pos(collision_vector - Vec3::unit_z() * obstacle_z),
stats,
skill_set,
health: comp::Health::new(
body,
self.static_data.summon_info.health_scaling,
),
health: health_scaling,
poise: comp::Poise::new(body),
loadout,
body,
@ -129,6 +189,7 @@ impl CharacterBehavior for Data {
home_chunk: None,
drop_item: None,
rtsim_entity: None,
projectile,
});
// Send local event used for frontend shenanigans
@ -186,7 +247,7 @@ impl CharacterBehavior for Data {
pub struct SummonInfo {
body: comp::Body,
scale: Option<comp::Scale>,
health_scaling: u16,
health_scaling: Option<u16>,
// TODO: use assets for specifying skills and loadout?
loadout_config: Option<loadout_builder::Preset>,
skillset_config: Option<skillset_builder::Preset>,

View File

@ -5,6 +5,7 @@ use crate::{
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel,
},
resources::DeltaTime,
terrain::TerrainGrid,
uid::Uid,
};
use specs::{
@ -96,6 +97,7 @@ pub struct JoinData<'a> {
pub msm: &'a MaterialStatManifest,
pub combo: &'a Combo,
pub alignment: Option<&'a comp::Alignment>,
pub terrain: &'a TerrainGrid,
}
type RestrictedMut<'a, C> = PairedStorage<
@ -128,6 +130,7 @@ pub struct JoinStruct<'a> {
pub skill_set: &'a SkillSet,
pub combo: &'a Combo,
pub alignment: Option<&'a comp::Alignment>,
pub terrain: &'a TerrainGrid,
}
impl<'a> JoinData<'a> {
@ -161,6 +164,7 @@ impl<'a> JoinData<'a> {
msm,
combo: j.combo,
alignment: j.alignment,
terrain: j.terrain,
}
}
}

View File

@ -1,6 +1,6 @@
use specs::{
shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadStorage, SystemData, World, Write,
WriteStorage,
shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, SystemData,
World, Write, WriteStorage,
};
use common::{
@ -16,6 +16,7 @@ use common::{
self,
behavior::{CharacterBehavior, JoinData, JoinStruct},
},
terrain::TerrainGrid,
uid::Uid,
};
use common_ecs::{Job, Origin, Phase, System};
@ -67,6 +68,7 @@ pub struct ReadData<'a> {
msm: Read<'a, MaterialStatManifest>,
combos: ReadStorage<'a, Combo>,
alignments: ReadStorage<'a, comp::Alignment>,
terrain: ReadExpect<'a, TerrainGrid>,
}
/// ## Character Behavior System
@ -280,6 +282,7 @@ impl<'a> System<'a> for Sys {
skill_set: &skill_set,
combo: &combo,
alignment: read_data.alignments.get(entity),
terrain: &read_data.terrain,
};
for action in actions {

View File

@ -1014,7 +1014,7 @@ fn handle_spawn(
pos,
comp::Stats::new(get_npc_name(id, npc::BodyType::from_body(body))),
comp::SkillSet::default(),
comp::Health::new(body, 1),
Some(comp::Health::new(body, 1)),
comp::Poise::new(body),
inventory,
body,
@ -1116,7 +1116,7 @@ fn handle_spawn_training_dummy(
pos,
stats,
skill_set,
health,
Some(health),
poise,
Inventory::new_empty(),
body,

View File

@ -53,7 +53,7 @@ pub fn handle_create_npc(
pos: Pos,
stats: Stats,
skill_set: SkillSet,
health: Health,
health: Option<Health>,
poise: Poise,
loadout: Loadout,
body: Body,
@ -63,6 +63,7 @@ pub fn handle_create_npc(
drop_item: Option<Item>,
home_chunk: Option<HomeChunk>,
rtsim_entity: Option<RtSimEntity>,
projectile: Option<Projectile>,
) {
let inventory = Inventory::new_with_loadout(loadout);
@ -96,6 +97,12 @@ pub fn handle_create_npc(
entity
};
let entity = if let Some(projectile) = projectile {
entity.with(projectile)
} else {
entity
};
let new_entity = entity.build();
// Add to group system if a pet

View File

@ -147,6 +147,7 @@ impl Server {
home_chunk,
drop_item,
rtsim_entity,
projectile,
} => handle_create_npc(
self,
pos,
@ -162,6 +163,7 @@ impl Server {
drop_item,
home_chunk,
rtsim_entity,
projectile,
),
ServerEvent::CreateShip {
pos,

View File

@ -127,7 +127,7 @@ impl<'a> System<'a> for Sys {
pos: comp::Pos(spawn_pos),
stats: comp::Stats::new(entity.get_name()),
skill_set: comp::SkillSet::default(),
health: comp::Health::new(body, 10),
health: Some(comp::Health::new(body, 10)),
loadout: match body {
comp::Body::Humanoid(_) => entity.get_loadout(),
_ => LoadoutBuilder::new().build(),
@ -146,6 +146,7 @@ impl<'a> System<'a> for Sys {
drop_item: None,
home_chunk: None,
rtsim_entity,
projectile: None,
},
};
server_emitter.emit(event);

View File

@ -39,7 +39,7 @@ pub trait StateExt {
pos: comp::Pos,
stats: comp::Stats,
skill_set: comp::SkillSet,
health: comp::Health,
health: Option<comp::Health>,
poise: comp::Poise,
inventory: comp::Inventory,
body: comp::Body,
@ -175,7 +175,7 @@ impl StateExt for State {
pos: comp::Pos,
stats: comp::Stats,
skill_set: comp::SkillSet,
health: comp::Health,
health: Option<comp::Health>,
poise: comp::Poise,
inventory: comp::Inventory,
body: comp::Body,
@ -215,7 +215,7 @@ impl StateExt for State {
))
.with(stats)
.with(skill_set)
.with(health)
.maybe_with(health)
.with(poise)
.with(comp::Alignment::Npc)
.with(comp::CharacterState::default())

View File

@ -239,7 +239,7 @@ impl<'a> System<'a> for Sys {
loadout_builder.build()
};
let health = comp::Health::new(body, entity.level.unwrap_or(0));
let health = Some(comp::Health::new(body, entity.level.unwrap_or(0)));
let poise = comp::Poise::new(body);
let can_speak = match body {
@ -293,6 +293,7 @@ impl<'a> System<'a> for Sys {
home_chunk: Some(comp::HomeChunk(key)),
drop_item: entity.loot_drop,
rtsim_entity: None,
projectile: None,
})
}

View File

@ -209,7 +209,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
feed: match (body.species, body.body_type) {
(Phoenix, _) => (-0.65),
(Cockatrice, _) => (-0.5),
(Roc, _) => (-0.65),
(Roc, _) => (-0.4),
},
}
}