mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'seachapel_npc_work' into 'master'
seachapel npc work See merge request veloren/veloren!3599
This commit is contained in:
commit
59d8d287d9
@ -381,13 +381,17 @@
|
|||||||
Custom("Dagon"): (
|
Custom("Dagon"): (
|
||||||
primary: "common.abilities.custom.dagon.dagonbombs",
|
primary: "common.abilities.custom.dagon.dagonbombs",
|
||||||
secondary: "common.abilities.custom.dagon.seaurchins",
|
secondary: "common.abilities.custom.dagon.seaurchins",
|
||||||
abilities: [],
|
abilities: [
|
||||||
|
(None, "common.abilities.custom.dagon.steamwave"),
|
||||||
|
(None, "common.abilities.custom.cardinal.steambeam"),
|
||||||
|
(None, "common.abilities.custom.dagon.steamheal"),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Custom("Cardinal"): (
|
Custom("Cardinal"): (
|
||||||
primary: "common.abilities.sceptre.lifestealbeam",
|
primary: "common.abilities.sceptre.lifestealbeam",
|
||||||
secondary: "common.abilities.sceptre.healingaura",
|
secondary: "common.abilities.sceptre.healingaura",
|
||||||
abilities: [
|
abilities: [
|
||||||
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
|
(None, "common.abilities.custom.cardinal.steambeam"),
|
||||||
(None, "common.abilities.custom.cardinal.summonseacrocs"),
|
(None, "common.abilities.custom.cardinal.summonseacrocs"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
19
assets/common/abilities/custom/cardinal/steambeam.ron
Normal file
19
assets/common/abilities/custom/cardinal/steambeam.ron
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
BasicBeam(
|
||||||
|
buildup_duration: 0.3,
|
||||||
|
recover_duration: 1.0,
|
||||||
|
beam_duration: 1.0,
|
||||||
|
damage: 22.5,
|
||||||
|
tick_rate: 5.0,
|
||||||
|
range: 8.0,
|
||||||
|
max_angle: 15.0,
|
||||||
|
damage_effect: Some(Buff((
|
||||||
|
kind: Burning,
|
||||||
|
dur_secs: 3.0,
|
||||||
|
strength: Value(0.5),
|
||||||
|
chance: 1.0,
|
||||||
|
))),
|
||||||
|
energy_regen: 2,
|
||||||
|
energy_drain: 0,
|
||||||
|
ori_rate: 0.5,
|
||||||
|
specifier: Steam,
|
||||||
|
)
|
@ -1,7 +1,7 @@
|
|||||||
BasicSummon(
|
BasicSummon(
|
||||||
buildup_duration: 0.5,
|
buildup_duration: 0.2,
|
||||||
cast_duration: 1.0,
|
cast_duration: 0.3,
|
||||||
recover_duration: 0.5,
|
recover_duration: 0.3,
|
||||||
summon_amount: 4,
|
summon_amount: 4,
|
||||||
summon_distance: (4, 4),
|
summon_distance: (4, 4),
|
||||||
summon_info: (
|
summon_info: (
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
BasicRanged(
|
BasicRanged(
|
||||||
energy_cost: 0,
|
energy_cost: 0,
|
||||||
buildup_duration: 0.4,
|
buildup_duration: 0.5,
|
||||||
recover_duration: 0.6,
|
recover_duration: 1.5,
|
||||||
projectile: DagonBomb(
|
projectile: DagonBomb(
|
||||||
damage: 32.0,
|
damage: 32.0,
|
||||||
knockback: 25.0,
|
knockback: 15.0,
|
||||||
radius: 10.0,
|
radius: 5.0,
|
||||||
min_falloff: 0.6,
|
min_falloff: 0.1,
|
||||||
),
|
),
|
||||||
projectile_body: Object(DagonBomb),
|
projectile_body: Object(DagonBomb),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_speed: 30.0,
|
projectile_speed: 20.0,
|
||||||
num_projectiles: 1,
|
num_projectiles: 1,
|
||||||
projectile_spread: 0.0,
|
projectile_spread: 0.0,
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,6 @@ SpriteSummon(
|
|||||||
cast_duration: 0.1,
|
cast_duration: 0.1,
|
||||||
recover_duration: 0.9,
|
recover_duration: 0.9,
|
||||||
sprite: SeaUrchin,
|
sprite: SeaUrchin,
|
||||||
summon_distance: (3, 3.1),
|
summon_distance: (5, 3.1),
|
||||||
sparseness: 0.2,
|
sparseness: 0.2,
|
||||||
)
|
)
|
19
assets/common/abilities/custom/dagon/steamheal.ron
Normal file
19
assets/common/abilities/custom/dagon/steamheal.ron
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
BasicAura(
|
||||||
|
buildup_duration: 0.2,
|
||||||
|
cast_duration: 0.4,
|
||||||
|
recover_duration: 5.0,
|
||||||
|
targets: InGroup,
|
||||||
|
auras: [
|
||||||
|
(
|
||||||
|
kind: Regeneration,
|
||||||
|
strength: 10.0,
|
||||||
|
duration: Some(5),
|
||||||
|
category: Magical,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
aura_duration: 2.0,
|
||||||
|
range: 10.0,
|
||||||
|
energy_cost: 0.0,
|
||||||
|
scales_with_combo: false,
|
||||||
|
specifier: Some(HealingAura),
|
||||||
|
)
|
17
assets/common/abilities/custom/dagon/steamwave.ron
Normal file
17
assets/common/abilities/custom/dagon/steamwave.ron
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Shockwave(
|
||||||
|
energy_cost: 0,
|
||||||
|
buildup_duration: 0.3,
|
||||||
|
swing_duration: 0.3,
|
||||||
|
recover_duration: 0.0,
|
||||||
|
damage: 20.0,
|
||||||
|
poise_damage: 10,
|
||||||
|
knockback: (strength: 18.0, direction: Away),
|
||||||
|
shockwave_angle: 360.0,
|
||||||
|
shockwave_vertical_angle: 90.0,
|
||||||
|
shockwave_speed: 15.0,
|
||||||
|
shockwave_duration: 2.0,
|
||||||
|
requires_ground: true,
|
||||||
|
move_efficiency: 0.0,
|
||||||
|
damage_kind: Crushing,
|
||||||
|
specifier: Steam,
|
||||||
|
)
|
13
assets/common/entity/dungeon/sea_chapel/prisoner.ron
Normal file
13
assets/common/entity/dungeon/sea_chapel/prisoner.ron
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#![enable(implicit_some)]
|
||||||
|
(
|
||||||
|
name: Name("Prisoner"),
|
||||||
|
body: RandomWith("humanoid"),
|
||||||
|
alignment: Alignment(Npc),
|
||||||
|
loot: LootTable("common.loot_tables.creature.humanoid"),
|
||||||
|
inventory: (
|
||||||
|
loadout: Inline((
|
||||||
|
inherit: Asset("common.loadout.village.villager"),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
meta: [],
|
||||||
|
)
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "Seemlessly transitions...",
|
description: "Seemlessly transitions...",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Belt,
|
kind: Belt,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(24.0)),
|
|
||||||
poise_resilience: Some(Normal(3.0)),
|
|
||||||
energy_max: Some(20),
|
|
||||||
energy_reward: Some(0.025),
|
|
||||||
crit_power: Some(0.06),
|
|
||||||
stealth: Some(0.0),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "A part of the cardinal's exquisite cloak.",
|
description: "A part of the cardinal's exquisite cloak.",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Chest,
|
kind: Chest,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(60.0)),
|
|
||||||
poise_resilience: Some(Normal(18.0)),
|
|
||||||
energy_max: Some(120),
|
|
||||||
energy_reward: Some(0.060),
|
|
||||||
crit_power: Some(0.375),
|
|
||||||
stealth: Some(0.0),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "The boots with millions of steps.",
|
description: "The boots with millions of steps.",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Foot,
|
kind: Foot,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(24.0)),
|
|
||||||
poise_resilience: Some(Normal(6.0)),
|
|
||||||
energy_max: Some(85),
|
|
||||||
energy_reward: Some(0.105),
|
|
||||||
crit_power: Some(0.12),
|
|
||||||
stealth: Some(0.0),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "Bloodstained and rugged.",
|
description: "Bloodstained and rugged.",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Hand,
|
kind: Hand,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(20.0)),
|
|
||||||
poise_resilience: Some(Normal(6.0)),
|
|
||||||
energy_max: Some(75),
|
|
||||||
energy_reward: Some(0.09),
|
|
||||||
crit_power: Some(0.12),
|
|
||||||
stealth: Some(0.0),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "Pants with many experiences.",
|
description: "Pants with many experiences.",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Pants,
|
kind: Pants,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(45.0)),
|
|
||||||
poise_resilience: Some(Normal(12.0)),
|
|
||||||
energy_max: Some(150.0),
|
|
||||||
energy_reward: Some(0.05),
|
|
||||||
crit_power: Some(0.24),
|
|
||||||
stealth: Some(0.00),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -3,14 +3,7 @@ ItemDef(
|
|||||||
description: "The other was lost in a vicious fight.",
|
description: "The other was lost in a vicious fight.",
|
||||||
kind: Armor((
|
kind: Armor((
|
||||||
kind: Shoulder,
|
kind: Shoulder,
|
||||||
stats: Direct((
|
stats: FromSet("Cardinal"),
|
||||||
protection: Some(Normal(30.0)),
|
|
||||||
poise_resilience: Some(Normal(15.0)),
|
|
||||||
energy_max: Some(90),
|
|
||||||
energy_reward: Some(0.05),
|
|
||||||
crit_power: Some(0.24),
|
|
||||||
stealth: Some(0.0),
|
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
quality: Legendary,
|
quality: Legendary,
|
||||||
tags: [
|
tags: [
|
||||||
|
13
assets/common/items/npc_armor/quadruped_low/dagon.ron
Normal file
13
assets/common/items/npc_armor/quadruped_low/dagon.ron
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
ItemDef(
|
||||||
|
name: "Dagon's Scales",
|
||||||
|
description: "Rigid enough to withstand the pressure of the deep ocean.",
|
||||||
|
kind: Armor((
|
||||||
|
kind: Chest,
|
||||||
|
stats: Direct((
|
||||||
|
protection: Some(Normal(150.0)),
|
||||||
|
poise_resilience: Some(Normal(5.0)),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
quality: Epic,
|
||||||
|
tags: [],
|
||||||
|
)
|
@ -1,6 +1,6 @@
|
|||||||
ItemDef(
|
ItemDef(
|
||||||
name: "Dagon Kit",
|
name: "Dagon Kit",
|
||||||
description: "Placeholder",
|
description: "Ocean Power!",
|
||||||
kind: Tool((
|
kind: Tool((
|
||||||
kind: Natural,
|
kind: Natural,
|
||||||
hands: Two,
|
hands: Two,
|
||||||
|
@ -310,5 +310,12 @@
|
|||||||
energy_reward: Some(0.5),
|
energy_reward: Some(0.5),
|
||||||
crit_power: Some(0.4),
|
crit_power: Some(0.4),
|
||||||
),
|
),
|
||||||
|
"Cardinal": (
|
||||||
|
protection: Some(Normal(666.0)),
|
||||||
|
poise_resilience: Some(Normal(60.0)),
|
||||||
|
energy_max: Some(45.0),
|
||||||
|
energy_reward: Some(0.5),
|
||||||
|
crit_power: Some(0.8),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1260,6 +1260,13 @@
|
|||||||
],
|
],
|
||||||
threshold: 1.0,
|
threshold: 1.0,
|
||||||
),
|
),
|
||||||
|
Utterance(Hurt, Dagon): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.dagon_hurt1",
|
||||||
|
"voxygen.audio.sfx.utterance.dagon_hurt2",
|
||||||
|
],
|
||||||
|
threshold: 1.0,
|
||||||
|
),
|
||||||
Utterance(Angry, Asp): (
|
Utterance(Angry, Asp): (
|
||||||
files: [
|
files: [
|
||||||
"voxygen.audio.sfx.utterance.asp_angry1",
|
"voxygen.audio.sfx.utterance.asp_angry1",
|
||||||
|
BIN
assets/voxygen/audio/sfx/utterance/dagon_hurt1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/dagon_hurt1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/dagon_hurt2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/dagon_hurt2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -216,4 +216,10 @@ npc-speech-cultist_low_health_fleeing =
|
|||||||
.a2 = Curse you!
|
.a2 = Curse you!
|
||||||
.a3 = I will curse you in the afterlife!
|
.a3 = I will curse you in the afterlife!
|
||||||
.a4 = I must rest!
|
.a4 = I must rest!
|
||||||
.a5 = They're too strong!
|
.a5 = They're too strong!
|
||||||
|
npc-speech-prisoner =
|
||||||
|
.a0 = Them scoundrels took away my pickaxe!
|
||||||
|
.a1 = Being trapped is no fun.
|
||||||
|
.a3 = That Cardinal can't be trusted.
|
||||||
|
.a4 = These Clerics are up to no good.
|
||||||
|
.a5 = I wish i still had my pick!
|
@ -76,6 +76,8 @@ const int ENERGY_BUFFING = 35;
|
|||||||
const int WEB_STRAND = 36;
|
const int WEB_STRAND = 36;
|
||||||
const int BLACK_SMOKE = 37;
|
const int BLACK_SMOKE = 37;
|
||||||
const int LIGHTNING = 38;
|
const int LIGHTNING = 38;
|
||||||
|
const int STEAM = 39;
|
||||||
|
const int BARRELORGAN = 40;
|
||||||
|
|
||||||
// meters per second squared (acceleration)
|
// meters per second squared (acceleration)
|
||||||
const float earth_gravity = 9.807;
|
const float earth_gravity = 9.807;
|
||||||
@ -614,6 +616,27 @@ void main() {
|
|||||||
identity()//spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
|
identity()//spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case STEAM:
|
||||||
|
f_reflect = 0.0; // Magic steam doesn't reflect light, it emits it
|
||||||
|
float steam_size = 8.0 * (1 - slow_start(0.1)) * slow_end(0.15);
|
||||||
|
attr = Attr(
|
||||||
|
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||||
|
vec3(steam_size),
|
||||||
|
vec4(vec3(0.7, 2.7, 1.3), 1),
|
||||||
|
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case BARRELORGAN:
|
||||||
|
attr = Attr(
|
||||||
|
linear_motion(
|
||||||
|
vec3(rand0 * 0.25, rand1 * 0.25, 1.7 + rand5),
|
||||||
|
vec3(rand2 * 0.1, rand3 * 0.1, 1.0 + rand4 * 0.5)
|
||||||
|
),
|
||||||
|
vec3(exp_scale(-0.2)) * rand0,
|
||||||
|
vec4(vec3(0.7, 2.7, 1.3), 1),
|
||||||
|
spin_in_axis(vec3(1,0,0),0)
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
attr = Attr(
|
attr = Attr(
|
||||||
linear_motion(
|
linear_motion(
|
||||||
|
@ -52,6 +52,7 @@ pub enum FrontendSpecifier {
|
|||||||
Cultist,
|
Cultist,
|
||||||
ClayGolem,
|
ClayGolem,
|
||||||
Bubbles,
|
Bubbles,
|
||||||
|
Steam,
|
||||||
Frost,
|
Frost,
|
||||||
WebStrand,
|
WebStrand,
|
||||||
}
|
}
|
||||||
|
@ -909,6 +909,7 @@ impl LoadoutBuilder {
|
|||||||
| quadruped_low::Species::Sandshark => {
|
| quadruped_low::Species::Sandshark => {
|
||||||
Some("common.items.npc_armor.quadruped_low.generic")
|
Some("common.items.npc_armor.quadruped_low.generic")
|
||||||
},
|
},
|
||||||
|
quadruped_low::Species::Dagon => Some("common.items.npc_armor.quadruped_low.dagon"),
|
||||||
quadruped_low::Species::Tortoise => {
|
quadruped_low::Species::Tortoise => {
|
||||||
Some("common.items.npc_armor.quadruped_low.shell")
|
Some("common.items.npc_armor.quadruped_low.shell")
|
||||||
},
|
},
|
||||||
|
@ -504,7 +504,7 @@ impl ProjectileConstructor {
|
|||||||
let explosion = Explosion {
|
let explosion = Explosion {
|
||||||
effects: vec![
|
effects: vec![
|
||||||
RadiusEffect::Attack(attack),
|
RadiusEffect::Attack(attack),
|
||||||
RadiusEffect::TerrainDestruction(5.0),
|
RadiusEffect::TerrainDestruction(75.0),
|
||||||
],
|
],
|
||||||
radius,
|
radius,
|
||||||
reagent: Some(Reagent::Blue),
|
reagent: Some(Reagent::Blue),
|
||||||
|
@ -50,4 +50,5 @@ pub enum FrontendSpecifier {
|
|||||||
Fire,
|
Fire,
|
||||||
Water,
|
Water,
|
||||||
IceSpikes,
|
IceSpikes,
|
||||||
|
Steam,
|
||||||
}
|
}
|
||||||
|
@ -2401,7 +2401,7 @@ impl<'a> AgentData<'a> {
|
|||||||
.is_some()
|
.is_some()
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
// Use ward if target is far enough away, self is not buffed, and have
|
// Use steam beam if target is far enough away, self is not buffed, and have
|
||||||
// sufficient energy
|
// sufficient energy
|
||||||
controller.push_basic_input(InputKind::Ability(0));
|
controller.push_basic_input(InputKind::Ability(0));
|
||||||
} else {
|
} else {
|
||||||
@ -2414,9 +2414,8 @@ impl<'a> AgentData<'a> {
|
|||||||
&& self.energy.current() > CharacterAbility::default_roll().get_energy_cost()
|
&& self.energy.current() > CharacterAbility::default_roll().get_energy_cost()
|
||||||
&& !matches!(self.char_state, CharacterState::BasicAura(c) if !matches!(c.stage_section, StageSection::Recover))
|
&& !matches!(self.char_state, CharacterState::BasicAura(c) if !matches!(c.stage_section, StageSection::Recover))
|
||||||
{
|
{
|
||||||
// Else roll away if can roll and have enough energy, and not using aura or in
|
// Else use steam beam
|
||||||
// recover
|
controller.push_basic_input(InputKind::Ability(0));
|
||||||
controller.push_basic_input(InputKind::Roll);
|
|
||||||
} else if attack_data.angle < 15.0 {
|
} else if attack_data.angle < 15.0 {
|
||||||
controller.push_basic_input(InputKind::Primary);
|
controller.push_basic_input(InputKind::Primary);
|
||||||
}
|
}
|
||||||
@ -2501,29 +2500,40 @@ impl<'a> AgentData<'a> {
|
|||||||
tgt_data: &TargetData,
|
tgt_data: &TargetData,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) {
|
) {
|
||||||
// if close to target, shoot dagon bombs and lay out sea urchins
|
if agent.action_state.timer > 2.5 {
|
||||||
if attack_data.angle < 70.0
|
agent.action_state.timer = 0.0;
|
||||||
&& attack_data.dist_sqrd < (1.3 * attack_data.min_attack_dist).powi(2)
|
}
|
||||||
{
|
// if close to target lay out sea urchins, use steambeam and shoot dagon bombs
|
||||||
|
if attack_data.dist_sqrd < (1.3 * attack_data.min_attack_dist).powi(2) {
|
||||||
controller.inputs.move_dir = Vec2::zero();
|
controller.inputs.move_dir = Vec2::zero();
|
||||||
if agent.action_state.timer > 1.0 {
|
if agent.action_state.timer > 2.0 {
|
||||||
controller.push_basic_input(InputKind::Primary);
|
controller.push_basic_input(InputKind::Primary);
|
||||||
agent.action_state.timer += read_data.dt.0;
|
agent.action_state.timer += read_data.dt.0;
|
||||||
|
} else if agent.action_state.timer > 1.0 {
|
||||||
|
controller.push_basic_input(InputKind::Ability(1));
|
||||||
} else {
|
} else {
|
||||||
controller.push_basic_input(InputKind::Secondary);
|
controller.push_basic_input(InputKind::Secondary);
|
||||||
agent.action_state.timer += read_data.dt.0;
|
agent.action_state.timer += read_data.dt.0;
|
||||||
}
|
}
|
||||||
} else if attack_data.angle < 30.0
|
} else if attack_data.dist_sqrd > (3.0 * attack_data.min_attack_dist).powi(2) {
|
||||||
&& entities_have_line_of_sight(
|
// if enemy is far, heal
|
||||||
self.pos,
|
controller.push_basic_input(InputKind::Ability(2));
|
||||||
self.body,
|
agent.action_state.timer += read_data.dt.0;
|
||||||
tgt_data.pos,
|
} else if entities_have_line_of_sight(
|
||||||
tgt_data.body,
|
self.pos,
|
||||||
read_data,
|
self.body,
|
||||||
)
|
tgt_data.pos,
|
||||||
{
|
tgt_data.body,
|
||||||
// if in range, angle and sight, shoot dagon bombs at target
|
read_data,
|
||||||
controller.push_basic_input(InputKind::Primary);
|
) {
|
||||||
|
// if in range shoot dagon bombs and steamwave
|
||||||
|
if agent.action_state.timer > 1.0 {
|
||||||
|
controller.push_basic_input(InputKind::Primary);
|
||||||
|
agent.action_state.timer += read_data.dt.0;
|
||||||
|
} else {
|
||||||
|
controller.push_basic_input(InputKind::Ability(0));
|
||||||
|
agent.action_state.timer += read_data.dt.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// chase
|
// chase
|
||||||
let path = if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
let path = if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||||
|
@ -26,7 +26,7 @@ pub struct Entity {
|
|||||||
pub brain: Brain,
|
pub brain: Brain,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, strum::EnumIter)]
|
#[derive(Clone, Copy, strum::EnumIter, PartialEq)]
|
||||||
pub enum RtSimEntityKind {
|
pub enum RtSimEntityKind {
|
||||||
Wanderer,
|
Wanderer,
|
||||||
Cultist,
|
Cultist,
|
||||||
@ -36,6 +36,7 @@ pub enum RtSimEntityKind {
|
|||||||
Blacksmith,
|
Blacksmith,
|
||||||
Chef,
|
Chef,
|
||||||
Alchemist,
|
Alchemist,
|
||||||
|
Prisoner,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BIRD_MEDIUM_ROSTER: &[comp::bird_medium::Species] = &[
|
const BIRD_MEDIUM_ROSTER: &[comp::bird_medium::Species] = &[
|
||||||
@ -101,6 +102,7 @@ impl Entity {
|
|||||||
| RtSimEntityKind::Chef
|
| RtSimEntityKind::Chef
|
||||||
| RtSimEntityKind::Alchemist
|
| RtSimEntityKind::Alchemist
|
||||||
| RtSimEntityKind::Blacksmith
|
| RtSimEntityKind::Blacksmith
|
||||||
|
| RtSimEntityKind::Prisoner
|
||||||
| RtSimEntityKind::Merchant => {
|
| RtSimEntityKind::Merchant => {
|
||||||
let species = *comp::humanoid::ALL_SPECIES
|
let species = *comp::humanoid::ALL_SPECIES
|
||||||
.choose(&mut self.rng(PERM_SPECIES))
|
.choose(&mut self.rng(PERM_SPECIES))
|
||||||
@ -986,6 +988,7 @@ fn humanoid_config(kind: RtSimEntityKind, rank: TravelerRank) -> &'static str {
|
|||||||
RtSimEntityKind::Blacksmith => "common.entity.village.blacksmith",
|
RtSimEntityKind::Blacksmith => "common.entity.village.blacksmith",
|
||||||
RtSimEntityKind::Chef => "common.entity.village.chef",
|
RtSimEntityKind::Chef => "common.entity.village.chef",
|
||||||
RtSimEntityKind::Alchemist => "common.entity.village.alchemist",
|
RtSimEntityKind::Alchemist => "common.entity.village.alchemist",
|
||||||
|
RtSimEntityKind::Prisoner => "common.entity.dungeon.sea_chapel.prisoner",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,6 +368,33 @@ pub fn init(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
SiteKind::ChapelSite(site2) => {
|
||||||
|
// prisoners
|
||||||
|
for _ in 0..10 {
|
||||||
|
rtsim.entities.insert(Entity {
|
||||||
|
is_loaded: false,
|
||||||
|
pos: site2
|
||||||
|
.plots()
|
||||||
|
.filter(|plot| {
|
||||||
|
matches!(plot.kind(), world::site2::PlotKind::SeaChapel(_))
|
||||||
|
})
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
.map_or(site.get_origin(), |plot| {
|
||||||
|
site2.tile_center_wpos(Vec2::new(
|
||||||
|
plot.root_tile().x,
|
||||||
|
plot.root_tile().y + 4,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.with_z(0)
|
||||||
|
.map(|e| e as f32),
|
||||||
|
seed: thread_rng().gen(),
|
||||||
|
controller: RtSimController::default(),
|
||||||
|
last_time_ticked: 0.0,
|
||||||
|
kind: RtSimEntityKind::Prisoner,
|
||||||
|
brain: Brain::villager(site_id, &mut thread_rng()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,10 @@ use common::{
|
|||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use specs::saveload::Marker;
|
use specs::saveload::Marker;
|
||||||
|
|
||||||
use crate::{rtsim::entity::PersonalityTrait, sys::agent::util::get_entity_by_id};
|
use crate::{
|
||||||
|
rtsim::entity::{PersonalityTrait, RtSimEntityKind},
|
||||||
|
sys::agent::util::get_entity_by_id,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{BehaviorData, BehaviorTree};
|
use super::{BehaviorData, BehaviorTree};
|
||||||
|
|
||||||
@ -89,145 +92,160 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
|
|
||||||
match subject {
|
match subject {
|
||||||
Subject::Regular => {
|
Subject::Regular => {
|
||||||
if let (Some((_travel_to, destination_name)), Some(rtsim_entity)) =
|
if let Some(rtsim_entity) = &bdata.rtsim_entity {
|
||||||
(&agent.rtsim_controller.travel_to, &bdata.rtsim_entity)
|
if matches!(rtsim_entity.kind, RtSimEntityKind::Prisoner) {
|
||||||
{
|
agent_data.chat_npc("npc-speech-prisoner", event_emitter);
|
||||||
let personality = &rtsim_entity.brain.personality;
|
} else if let (
|
||||||
let standard_response_msg = || -> String {
|
Some((_travel_to, destination_name)),
|
||||||
if personality.will_ambush {
|
Some(rtsim_entity),
|
||||||
format!(
|
) =
|
||||||
"I'm heading to {}! Want to come along? We'll make \
|
(&agent.rtsim_controller.travel_to, &&bdata.rtsim_entity)
|
||||||
great travel buddies, hehe.",
|
{
|
||||||
destination_name
|
let personality = &rtsim_entity.brain.personality;
|
||||||
)
|
let standard_response_msg = || -> String {
|
||||||
} else if personality
|
|
||||||
.personality_traits
|
|
||||||
.contains(PersonalityTrait::Extroverted)
|
|
||||||
{
|
|
||||||
format!(
|
|
||||||
"I'm heading to {}! Want to come along?",
|
|
||||||
destination_name
|
|
||||||
)
|
|
||||||
} else if personality
|
|
||||||
.personality_traits
|
|
||||||
.contains(PersonalityTrait::Disagreeable)
|
|
||||||
{
|
|
||||||
"Hrm.".to_string()
|
|
||||||
} else {
|
|
||||||
"Hello!".to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let msg = if let Some(tgt_stats) = read_data.stats.get(target) {
|
|
||||||
agent.rtsim_controller.events.push(RtSimEvent::AddMemory(
|
|
||||||
Memory {
|
|
||||||
item: MemoryItem::CharacterInteraction {
|
|
||||||
name: tgt_stats.name.clone(),
|
|
||||||
},
|
|
||||||
time_to_forget: read_data.time.0 + 600.0,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
if rtsim_entity.brain.remembers_character(&tgt_stats.name) {
|
|
||||||
if personality.will_ambush {
|
if personality.will_ambush {
|
||||||
"Just follow me a bit more, hehe.".to_string()
|
format!(
|
||||||
|
"I'm heading to {}! Want to come along? We'll \
|
||||||
|
make great travel buddies, hehe.",
|
||||||
|
destination_name
|
||||||
|
)
|
||||||
} else if personality
|
} else if personality
|
||||||
.personality_traits
|
.personality_traits
|
||||||
.contains(PersonalityTrait::Extroverted)
|
.contains(PersonalityTrait::Extroverted)
|
||||||
{
|
{
|
||||||
format!(
|
format!(
|
||||||
"Greetings fair {}! It has been far too long \
|
"I'm heading to {}! Want to come along?",
|
||||||
since last I saw you. I'm going to {} right now.",
|
destination_name
|
||||||
&tgt_stats.name, destination_name
|
|
||||||
)
|
)
|
||||||
} else if personality
|
} else if personality
|
||||||
.personality_traits
|
.personality_traits
|
||||||
.contains(PersonalityTrait::Disagreeable)
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
{
|
{
|
||||||
"Oh. It's you again.".to_string()
|
"Hrm.".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
"Hello!".to_string()
|
||||||
"Hi again {}! Unfortunately I'm in a hurry right \
|
}
|
||||||
now. See you!",
|
};
|
||||||
&tgt_stats.name
|
let msg = if let Some(tgt_stats) = read_data.stats.get(target) {
|
||||||
)
|
agent.rtsim_controller.events.push(RtSimEvent::AddMemory(
|
||||||
|
Memory {
|
||||||
|
item: MemoryItem::CharacterInteraction {
|
||||||
|
name: tgt_stats.name.clone(),
|
||||||
|
},
|
||||||
|
time_to_forget: read_data.time.0 + 600.0,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
if rtsim_entity.brain.remembers_character(&tgt_stats.name) {
|
||||||
|
if personality.will_ambush {
|
||||||
|
"Just follow me a bit more, hehe.".to_string()
|
||||||
|
} else if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Extroverted)
|
||||||
|
{
|
||||||
|
format!(
|
||||||
|
"Greetings fair {}! It has been far too long \
|
||||||
|
since last I saw you. I'm going to {} right \
|
||||||
|
now.",
|
||||||
|
&tgt_stats.name, destination_name
|
||||||
|
)
|
||||||
|
} else if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
|
{
|
||||||
|
"Oh. It's you again.".to_string()
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"Hi again {}! Unfortunately I'm in a hurry \
|
||||||
|
right now. See you!",
|
||||||
|
&tgt_stats.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
standard_response_msg()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
standard_response_msg()
|
standard_response_msg()
|
||||||
}
|
|
||||||
} else {
|
|
||||||
standard_response_msg()
|
|
||||||
};
|
|
||||||
agent_data.chat_npc(msg, event_emitter);
|
|
||||||
} else if agent.behavior.can_trade() {
|
|
||||||
if !agent.behavior.is(BehaviorState::TRADING) {
|
|
||||||
controller.push_initiate_invite(by, InviteKind::Trade);
|
|
||||||
agent_data.chat_npc(
|
|
||||||
"npc-speech-merchant_advertisement",
|
|
||||||
event_emitter,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let default_msg = "npc-speech-merchant_busy";
|
|
||||||
let msg = bdata.rtsim_entity.map_or(default_msg, |e| {
|
|
||||||
if e.brain
|
|
||||||
.personality
|
|
||||||
.personality_traits
|
|
||||||
.contains(PersonalityTrait::Disagreeable)
|
|
||||||
{
|
|
||||||
"npc-speech-merchant_busy_rude"
|
|
||||||
} else {
|
|
||||||
default_msg
|
|
||||||
}
|
|
||||||
});
|
|
||||||
agent_data.chat_npc(msg, event_emitter);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
if let Some(extreme_trait) = bdata
|
|
||||||
.rtsim_entity
|
|
||||||
.and_then(|e| e.brain.personality.random_chat_trait(&mut rng))
|
|
||||||
{
|
|
||||||
let msg = match extreme_trait {
|
|
||||||
PersonalityTrait::Open => "npc-speech-villager_open",
|
|
||||||
PersonalityTrait::Adventurous => {
|
|
||||||
"npc-speech-villager_adventurous"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Closed => "npc-speech-villager_closed",
|
|
||||||
PersonalityTrait::Conscientious => {
|
|
||||||
"npc-speech-villager_conscientious"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Busybody => {
|
|
||||||
"npc-speech-villager_busybody"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Unconscientious => {
|
|
||||||
"npc-speech-villager_unconscientious"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Extroverted => {
|
|
||||||
"npc-speech-villager_extroverted"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Introverted => {
|
|
||||||
"npc-speech-villager_introverted"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Agreeable => {
|
|
||||||
"npc-speech-villager_agreeable"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Sociable => {
|
|
||||||
"npc-speech-villager_sociable"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Disagreeable => {
|
|
||||||
"npc-speech-villager_disagreeable"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Neurotic => {
|
|
||||||
"npc-speech-villager_neurotic"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Seeker => "npc-speech-villager_seeker",
|
|
||||||
PersonalityTrait::SadLoner => {
|
|
||||||
"npc-speech-villager_sad_loner"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Worried => "npc-speech-villager_worried",
|
|
||||||
PersonalityTrait::Stable => "npc-speech-villager_stable",
|
|
||||||
};
|
};
|
||||||
agent_data.chat_npc(msg, event_emitter);
|
agent_data.chat_npc(msg, event_emitter);
|
||||||
|
} else if agent.behavior.can_trade() {
|
||||||
|
if !agent.behavior.is(BehaviorState::TRADING) {
|
||||||
|
controller.push_initiate_invite(by, InviteKind::Trade);
|
||||||
|
agent_data.chat_npc(
|
||||||
|
"npc-speech-merchant_advertisement",
|
||||||
|
event_emitter,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let default_msg = "npc-speech-merchant_busy";
|
||||||
|
let msg = &bdata.rtsim_entity.map_or(default_msg, |e| {
|
||||||
|
if e.brain
|
||||||
|
.personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
|
{
|
||||||
|
"npc-speech-merchant_busy_rude"
|
||||||
|
} else {
|
||||||
|
default_msg
|
||||||
|
}
|
||||||
|
});
|
||||||
|
agent_data.chat_npc(msg, event_emitter);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
agent_data.chat_npc("npc-speech-villager", event_emitter);
|
let mut rng = thread_rng();
|
||||||
|
if let Some(extreme_trait) = &bdata.rtsim_entity.and_then(|e| {
|
||||||
|
e.brain.personality.random_chat_trait(&mut rng)
|
||||||
|
}) {
|
||||||
|
let msg = match extreme_trait {
|
||||||
|
PersonalityTrait::Open => "npc-speech-villager_open",
|
||||||
|
PersonalityTrait::Adventurous => {
|
||||||
|
"npc-speech-villager_adventurous"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Closed => {
|
||||||
|
"npc-speech-villager_closed"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Conscientious => {
|
||||||
|
"npc-speech-villager_conscientious"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Busybody => {
|
||||||
|
"npc-speech-villager_busybody"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Unconscientious => {
|
||||||
|
"npc-speech-villager_unconscientious"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Extroverted => {
|
||||||
|
"npc-speech-villager_extroverted"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Introverted => {
|
||||||
|
"npc-speech-villager_introverted"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Agreeable => {
|
||||||
|
"npc-speech-villager_agreeable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Sociable => {
|
||||||
|
"npc-speech-villager_sociable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Disagreeable => {
|
||||||
|
"npc-speech-villager_disagreeable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Neurotic => {
|
||||||
|
"npc-speech-villager_neurotic"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Seeker => {
|
||||||
|
"npc-speech-villager_seeker"
|
||||||
|
},
|
||||||
|
PersonalityTrait::SadLoner => {
|
||||||
|
"npc-speech-villager_sad_loner"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Worried => {
|
||||||
|
"npc-speech-villager_worried"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Stable => {
|
||||||
|
"npc-speech-villager_stable"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
agent_data.chat_npc(msg, event_emitter);
|
||||||
|
} else {
|
||||||
|
agent_data.chat_npc("npc-speech-villager", event_emitter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -250,7 +268,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Subject::Mood => {
|
Subject::Mood => {
|
||||||
if let Some(rtsim_entity) = bdata.rtsim_entity {
|
if let Some(rtsim_entity) = &bdata.rtsim_entity {
|
||||||
if !rtsim_entity.brain.remembers_mood() {
|
if !rtsim_entity.brain.remembers_mood() {
|
||||||
// TODO: the following code will need a rework to
|
// TODO: the following code will need a rework to
|
||||||
// implement more mood contexts
|
// implement more mood contexts
|
||||||
|
@ -208,6 +208,7 @@ pub enum VoiceKind {
|
|||||||
Pig,
|
Pig,
|
||||||
Cow,
|
Cow,
|
||||||
Canine,
|
Canine,
|
||||||
|
Dagon,
|
||||||
Lion,
|
Lion,
|
||||||
Mindflayer,
|
Mindflayer,
|
||||||
Marlin,
|
Marlin,
|
||||||
@ -236,6 +237,7 @@ fn body_to_voice(body: &Body) -> Option<VoiceKind> {
|
|||||||
quadruped_low::Species::Maneater => VoiceKind::Maneater,
|
quadruped_low::Species::Maneater => VoiceKind::Maneater,
|
||||||
quadruped_low::Species::Alligator => VoiceKind::Alligator,
|
quadruped_low::Species::Alligator => VoiceKind::Alligator,
|
||||||
quadruped_low::Species::SeaCrocodile => VoiceKind::SeaCrocodile,
|
quadruped_low::Species::SeaCrocodile => VoiceKind::SeaCrocodile,
|
||||||
|
quadruped_low::Species::Dagon => VoiceKind::Dagon,
|
||||||
quadruped_low::Species::Asp => VoiceKind::Asp,
|
quadruped_low::Species::Asp => VoiceKind::Asp,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
@ -527,6 +529,7 @@ impl SfxMgr {
|
|||||||
},
|
},
|
||||||
beam::FrontendSpecifier::ClayGolem
|
beam::FrontendSpecifier::ClayGolem
|
||||||
| beam::FrontendSpecifier::Bubbles
|
| beam::FrontendSpecifier::Bubbles
|
||||||
|
| beam::FrontendSpecifier::Steam
|
||||||
| beam::FrontendSpecifier::Frost
|
| beam::FrontendSpecifier::Frost
|
||||||
| beam::FrontendSpecifier::WebStrand => {},
|
| beam::FrontendSpecifier::WebStrand => {},
|
||||||
},
|
},
|
||||||
|
@ -89,7 +89,8 @@ pub enum ParticleMode {
|
|||||||
WebStrand = 36,
|
WebStrand = 36,
|
||||||
BlackSmoke = 37,
|
BlackSmoke = 37,
|
||||||
Lightning = 38,
|
Lightning = 38,
|
||||||
BarrelOrgan = 39,
|
Steam = 39,
|
||||||
|
BarrelOrgan = 40,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParticleMode {
|
impl ParticleMode {
|
||||||
|
@ -947,6 +947,31 @@ impl ParticleMgr {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
beam::FrontendSpecifier::Steam => {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let (from, to) = (Vec3::<f32>::unit_z(), *ori.look_dir());
|
||||||
|
let m = Mat3::<f32>::rotation_from_to_3d(from, to);
|
||||||
|
self.particles.resize_with(
|
||||||
|
self.particles.len() + usize::from(beam_tick_count) / 15,
|
||||||
|
|| {
|
||||||
|
let phi: f32 = rng.gen_range(0.0..beam.properties.angle);
|
||||||
|
let theta: f32 = rng.gen_range(0.0..2.0 * PI);
|
||||||
|
let offset_z = Vec3::new(
|
||||||
|
phi.sin() * theta.cos(),
|
||||||
|
phi.sin() * theta.sin(),
|
||||||
|
phi.cos(),
|
||||||
|
);
|
||||||
|
let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0);
|
||||||
|
Particle::new_directed(
|
||||||
|
beam.properties.duration,
|
||||||
|
time,
|
||||||
|
ParticleMode::Steam,
|
||||||
|
pos,
|
||||||
|
pos + random_ori * range,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
beam::FrontendSpecifier::Frost => {
|
beam::FrontendSpecifier::Frost => {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let (from, to) = (Vec3::<f32>::unit_z(), *ori.look_dir());
|
let (from, to) = (Vec3::<f32>::unit_z(), *ori.look_dir());
|
||||||
@ -1561,6 +1586,41 @@ impl ParticleMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
FrontendSpecifier::Steam => {
|
||||||
|
// 1 particle per unit length of arc
|
||||||
|
let particles_per_length = arc_length as usize;
|
||||||
|
let dtheta = radians / particles_per_length as f32;
|
||||||
|
// Scales number of desired heartbeats from speed - thicker arc = higher speed =
|
||||||
|
// lower duration = more particles
|
||||||
|
let heartbeats = self
|
||||||
|
.scheduler
|
||||||
|
.heartbeats(Duration::from_secs_f32(1.0 / speed));
|
||||||
|
|
||||||
|
// Reserves capacity for new particles
|
||||||
|
let new_particle_count = particles_per_length * heartbeats as usize;
|
||||||
|
self.particles.reserve(new_particle_count);
|
||||||
|
|
||||||
|
for i in 0..particles_per_length {
|
||||||
|
let angle = dtheta * i as f32;
|
||||||
|
let direction = Vec3::new(angle.cos(), angle.sin(), 0.0);
|
||||||
|
for j in 0..heartbeats {
|
||||||
|
// Sub tick dt
|
||||||
|
let dt = (j as f32 / heartbeats as f32) * dt;
|
||||||
|
let distance = distance + speed * dt;
|
||||||
|
let pos1 = pos + distance * direction - Vec3::unit_z();
|
||||||
|
let pos2 = pos1 + (Vec3::unit_z() + direction) * 3.0;
|
||||||
|
let time = time + dt as f64;
|
||||||
|
|
||||||
|
self.particles.push(Particle::new_directed(
|
||||||
|
Duration::from_secs_f32(0.5),
|
||||||
|
time,
|
||||||
|
ParticleMode::Steam,
|
||||||
|
pos1,
|
||||||
|
pos2,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
FrontendSpecifier::IceSpikes => {
|
FrontendSpecifier::IceSpikes => {
|
||||||
// 1 / 3 the size of terrain voxel
|
// 1 / 3 the size of terrain voxel
|
||||||
let scale = 1.0 / 3.0;
|
let scale = 1.0 / 3.0;
|
||||||
|
@ -77,6 +77,7 @@ impl Structure for SeaChapel {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let glass_barrier = Fill::Block(Block::air(SpriteKind::GlassBarrier));
|
let glass_barrier = Fill::Block(Block::air(SpriteKind::GlassBarrier));
|
||||||
|
let sea_urchins = Fill::Block(Block::air(SpriteKind::SeaUrchin));
|
||||||
// random exit from water basin to side building
|
// random exit from water basin to side building
|
||||||
let mut connect_gate_types = vec![
|
let mut connect_gate_types = vec![
|
||||||
SpriteKind::GlassBarrier,
|
SpriteKind::GlassBarrier,
|
||||||
@ -2242,6 +2243,18 @@ impl Structure for SeaChapel {
|
|||||||
min: (center - 8).with_z(base - (2 * (diameter / 3)) + 14),
|
min: (center - 8).with_z(base - (2 * (diameter / 3)) + 14),
|
||||||
max: (center + 8).with_z(base - (2 * (diameter / 3)) + 16),
|
max: (center + 8).with_z(base - (2 * (diameter / 3)) + 16),
|
||||||
})
|
})
|
||||||
|
.fill(white_coral.clone());
|
||||||
|
painter
|
||||||
|
.cylinder(Aabb {
|
||||||
|
min: (center - 9).with_z(base - (2 * (diameter / 3)) + 16),
|
||||||
|
max: (center + 9).with_z(base - (2 * (diameter / 3)) + 18),
|
||||||
|
})
|
||||||
|
.fill(sea_urchins);
|
||||||
|
painter
|
||||||
|
.cylinder(Aabb {
|
||||||
|
min: (center - 8).with_z(base - (2 * (diameter / 3)) + 16),
|
||||||
|
max: (center + 8).with_z(base - (2 * (diameter / 3)) + 18),
|
||||||
|
})
|
||||||
.clear();
|
.clear();
|
||||||
painter
|
painter
|
||||||
.cylinder(Aabb {
|
.cylinder(Aabb {
|
||||||
@ -2829,20 +2842,6 @@ impl Structure for SeaChapel {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Holding Cell2
|
// Holding Cell2
|
||||||
painter
|
|
||||||
.sphere(Aabb {
|
|
||||||
min: Vec3::new(
|
|
||||||
center.x - (diameter / 2) - (diameter / 8) - 1,
|
|
||||||
center.y + (diameter / 16) - 1,
|
|
||||||
base - (diameter / 4) - 2,
|
|
||||||
),
|
|
||||||
max: Vec3::new(
|
|
||||||
center.x - (diameter / 2) + (diameter / 8) + 1,
|
|
||||||
center.y + (diameter / 16) + (diameter / 4) + 1,
|
|
||||||
base,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.fill(white.clone());
|
|
||||||
painter
|
painter
|
||||||
.sphere(Aabb {
|
.sphere(Aabb {
|
||||||
min: Vec3::new(
|
min: Vec3::new(
|
||||||
@ -2909,20 +2908,6 @@ impl Structure for SeaChapel {
|
|||||||
})
|
})
|
||||||
.fill(glass_barrier.clone());
|
.fill(glass_barrier.clone());
|
||||||
// Holding Cell3
|
// Holding Cell3
|
||||||
painter
|
|
||||||
.sphere(Aabb {
|
|
||||||
min: Vec3::new(
|
|
||||||
center.x + (diameter / 2) - (diameter / 8) - 1,
|
|
||||||
center.y - (diameter / 4) - (diameter / 16) - 1,
|
|
||||||
base - (diameter / 4) - 2,
|
|
||||||
),
|
|
||||||
max: Vec3::new(
|
|
||||||
center.x + (diameter / 2) + (diameter / 8) + 1,
|
|
||||||
center.y - (diameter / 16) + 1,
|
|
||||||
base,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.fill(white.clone());
|
|
||||||
painter
|
painter
|
||||||
.sphere(Aabb {
|
.sphere(Aabb {
|
||||||
min: Vec3::new(
|
min: Vec3::new(
|
||||||
@ -3375,14 +3360,6 @@ impl Structure for SeaChapel {
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
.fill(glass_barrier.clone());
|
.fill(glass_barrier.clone());
|
||||||
// Holding Cell Prisoners
|
|
||||||
let prisoners_pos = Vec3::new(center.x, center.y + (diameter / 3), base + 3);
|
|
||||||
for _ in 0..(5 + ((RandomField::new(0).get((prisoners_pos).with_z(base))) % 5)) {
|
|
||||||
painter.spawn(
|
|
||||||
EntityInfo::at(prisoners_pos.as_())
|
|
||||||
.with_asset_expect("common.entity.village.villager", &mut rng),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// stairway3 tube
|
// stairway3 tube
|
||||||
painter
|
painter
|
||||||
.cylinder(Aabb {
|
.cylinder(Aabb {
|
||||||
|
Loading…
Reference in New Issue
Block a user