mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'new_cultist' into 'master'
new_cultist See merge request veloren/veloren!4447
This commit is contained in:
commit
5e9cbdf754
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Added reworked Cultist Dungeon
|
||||
- Petting animals tamed by you or someone else!
|
||||
- Updated furniture sprites.
|
||||
- Added Abyssal rings
|
||||
|
@ -477,6 +477,13 @@
|
||||
secondary: Simple(None, "common.abilities.custom.quadmedbasic.triplestrike"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Darkhound"): (
|
||||
primary: Simple(None, "common.abilities.custom.darkhound.leap"),
|
||||
secondary: Simple(None, "common.abilities.custom.darkhound.doublestrike"),
|
||||
abilities: [
|
||||
Simple(None, "common.abilities.custom.darkhound.quickleap"),
|
||||
],
|
||||
),
|
||||
Custom("Frostfang"): (
|
||||
primary: Simple(None, "common.abilities.custom.frostfang.singlestrike"),
|
||||
secondary: Simple(None, "common.abilities.custom.frostfang.triplestrike"),
|
||||
@ -1124,4 +1131,24 @@
|
||||
secondary: Simple(None, "common.abilities.custom.jiangshi.teleport"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("BipedLargeCultistStaff"): (
|
||||
primary: Simple(None, "common.abilities.custom.biped_large_cultist.staff.firebomb"),
|
||||
secondary: Simple(None, "common.abilities.custom.biped_large_cultist.staff.flamethrower"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("BipedLargeCultistSword"): (
|
||||
primary: Simple(None, "common.abilities.custom.biped_large_cultist.sword.doublestrike"),
|
||||
secondary: Simple(None, "common.abilities.custom.biped_large_cultist.sword.dash"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("BipedLargeCultistHammer"): (
|
||||
primary: Simple(None, "common.abilities.custom.biped_large_cultist.hammer.doublestrike"),
|
||||
secondary: Simple(None, "common.abilities.custom.biped_large_cultist.hammer.doublestrike"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("BipedLargeCultistBow"): (
|
||||
primary: Simple(None, "common.abilities.custom.biped_large_cultist.bow.basic"),
|
||||
secondary: Simple(None, "common.abilities.custom.biped_large_cultist.bow.basic"),
|
||||
abilities: [],
|
||||
),
|
||||
})
|
||||
|
@ -0,0 +1,16 @@
|
||||
BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.9,
|
||||
recover_duration: 0.3,
|
||||
projectile: Arrow(
|
||||
damage: 12.0,
|
||||
knockback: 5.0,
|
||||
energy_regen: 4.0,
|
||||
),
|
||||
projectile_body: Object(Arrow),
|
||||
projectile_light: None,
|
||||
projectile_speed: 100.0,
|
||||
num_projectiles: 1,
|
||||
projectile_spread: 0.0,
|
||||
move_efficiency: 0.3,
|
||||
)
|
@ -0,0 +1,45 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 16,
|
||||
poise: 40,
|
||||
knockback: 4,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 4.5,
|
||||
angle: 50.0,
|
||||
),
|
||||
buildup_duration: 1.0,
|
||||
swing_duration: 0.28,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 1.0,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.65,
|
||||
),
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 32,
|
||||
poise: 40,
|
||||
knockback: 16,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 2.5,
|
||||
angle: 30.0,
|
||||
),
|
||||
buildup_duration: 0.6,
|
||||
swing_duration: 0.25,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 1.2,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.65,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
)
|
@ -0,0 +1,20 @@
|
||||
BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 1.0,
|
||||
recover_duration: 1.35,
|
||||
projectile: Fireball(
|
||||
damage: 15.0,
|
||||
radius: 5.0,
|
||||
energy_regen: 5.0,
|
||||
min_falloff: 0.5,
|
||||
),
|
||||
projectile_body: Object(BoltFire),
|
||||
/*projectile_light: Some(LightEmitter {
|
||||
col: (1.0, 0.75, 0.11).into(),
|
||||
..Default::default()
|
||||
}),*/
|
||||
projectile_speed: 60.0,
|
||||
num_projectiles: 1,
|
||||
projectile_spread: 0.0,
|
||||
move_efficiency: 0.3,
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.5,
|
||||
beam_duration: 1.0,
|
||||
damage: 3.5,
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 35.0,
|
||||
ori_rate: 0.3,
|
||||
specifier: Flamethrower,
|
||||
)
|
@ -0,0 +1,28 @@
|
||||
DashMelee(
|
||||
energy_cost: 10.0,
|
||||
melee_constructor: (
|
||||
kind: Stab(
|
||||
damage: 9.0,
|
||||
poise: 40.0,
|
||||
knockback: 8.0,
|
||||
energy_regen: 0.0,
|
||||
),
|
||||
scaled: Some((
|
||||
kind: Stab(
|
||||
damage: 16.0,
|
||||
poise: 0.0,
|
||||
knockback: 7.0,
|
||||
energy_regen: 0.0,
|
||||
))),
|
||||
range: 5.0,
|
||||
angle: 45.0,
|
||||
),
|
||||
energy_drain: 0,
|
||||
forward_speed: 4.0,
|
||||
buildup_duration: 0.6,
|
||||
charge_duration: 1.2,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.9,
|
||||
ori_modifier: 0.3,
|
||||
auto_charge: false,
|
||||
)
|
@ -0,0 +1,57 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 16,
|
||||
poise: 15,
|
||||
knockback: 8,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 3.5,
|
||||
angle: 50.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Bleeding,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.1),
|
||||
chance: 0.1,
|
||||
))),
|
||||
),
|
||||
buildup_duration: 0.8,
|
||||
swing_duration: 0.18,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.9,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.7,
|
||||
),
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 32,
|
||||
poise: 20,
|
||||
knockback: 12,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 3.5,
|
||||
angle: 30.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Bleeding,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.1),
|
||||
chance: 0.1,
|
||||
))),
|
||||
),
|
||||
buildup_duration: 0.7,
|
||||
swing_duration: 0.1,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.7,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.7,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
)
|
45
assets/common/abilities/custom/darkhound/doublestrike.ron
Normal file
45
assets/common/abilities/custom/darkhound/doublestrike.ron
Normal file
@ -0,0 +1,45 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 16,
|
||||
poise: 15,
|
||||
knockback: 4,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 2.2,
|
||||
angle: 30.0,
|
||||
),
|
||||
buildup_duration: 0.7,
|
||||
swing_duration: 0.5,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.6,
|
||||
movement: (
|
||||
swing: Some(Forward(0.2)),
|
||||
),
|
||||
ori_modifier: 0.8,
|
||||
),
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 16,
|
||||
poise: 15,
|
||||
knockback: 4,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 2.2,
|
||||
angle: 30.0,
|
||||
),
|
||||
buildup_duration: 0.85,
|
||||
swing_duration: 0.45,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.6,
|
||||
movement: (
|
||||
swing: Some(Forward(0.4)),
|
||||
),
|
||||
ori_modifier: 0.8,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
)
|
21
assets/common/abilities/custom/darkhound/leap.ron
Normal file
21
assets/common/abilities/custom/darkhound/leap.ron
Normal file
@ -0,0 +1,21 @@
|
||||
LeapMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.95,
|
||||
movement_duration: 0.8,
|
||||
swing_duration: 0.55,
|
||||
recover_duration: 0.4,
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 36.0,
|
||||
poise: 30.0,
|
||||
knockback: 4.0,
|
||||
energy_regen: 0.0,
|
||||
),
|
||||
range: 6.75,
|
||||
angle: 180.0,
|
||||
multi_target: Some(Normal),
|
||||
),
|
||||
forward_leap_strength: 45.0,
|
||||
vertical_leap_strength: 10.0,
|
||||
specifier: None,
|
||||
)
|
21
assets/common/abilities/custom/darkhound/quickleap.ron
Normal file
21
assets/common/abilities/custom/darkhound/quickleap.ron
Normal file
@ -0,0 +1,21 @@
|
||||
LeapMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.5,
|
||||
movement_duration: 0.6,
|
||||
swing_duration: 0.3,
|
||||
recover_duration: 0.25,
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 16.0,
|
||||
poise: 15.0,
|
||||
knockback: 2.0,
|
||||
energy_regen: 0.0,
|
||||
),
|
||||
range: 4.5,
|
||||
angle: 180.0,
|
||||
multi_target: Some(Normal),
|
||||
),
|
||||
forward_leap_strength: 20.0,
|
||||
vertical_leap_strength: 5.0,
|
||||
specifier: None,
|
||||
)
|
@ -11,8 +11,8 @@ ComboMelee2(
|
||||
range: 2.5,
|
||||
angle: 60.0,
|
||||
),
|
||||
buildup_duration: 0.5,
|
||||
swing_duration: 0.07,
|
||||
buildup_duration: 1.2,
|
||||
swing_duration: 0.27,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.5,
|
||||
movement: (
|
||||
|
@ -1,8 +1,8 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.3,
|
||||
buildup_duration: 0.9,
|
||||
recover_duration: 1.0,
|
||||
beam_duration: 1.0,
|
||||
damage: 20.0,
|
||||
damage: 12.0,
|
||||
tick_rate: 5.0,
|
||||
range: 22.0,
|
||||
max_angle: 15.0,
|
||||
|
@ -1,5 +1,5 @@
|
||||
RapidMelee(
|
||||
buildup_duration: 1.2,
|
||||
buildup_duration: 1.8,
|
||||
swing_duration: 0.5,
|
||||
recover_duration: 1.2,
|
||||
melee_constructor: (
|
||||
|
@ -2673,6 +2673,9 @@
|
||||
Simple(
|
||||
"common.items.npc_weapons.unique.quadsmallbasic",
|
||||
): "common-items-npc_weapons-unique-quadsmallbasic",
|
||||
Simple(
|
||||
"common.items.npc_weapons.unique.darkhound",
|
||||
): "common-items-npc_weapons-unique-darkhound",
|
||||
Simple(
|
||||
"common.items.npc_weapons.unique.roshwalr",
|
||||
): "common-items-npc_weapons-unique-roshwalr",
|
||||
|
@ -16,5 +16,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Bow Simple")),
|
||||
ability_spec: Some(Custom("BipedLargeCultistBow")),
|
||||
)
|
@ -16,5 +16,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Hammer Simple")),
|
||||
ability_spec: Some(Custom("BipedLargeCultistHammer")),
|
||||
)
|
@ -16,5 +16,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Staff Simple")),
|
||||
ability_spec: Some(Custom("BipedLargeCultistStaff")),
|
||||
)
|
@ -16,5 +16,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Sword Simple")),
|
||||
ability_spec: Some(Custom("BipedLargeCultistSword")),
|
||||
)
|
20
assets/common/items/npc_weapons/unique/darkhound.ron
Normal file
20
assets/common/items/npc_weapons/unique/darkhound.ron
Normal file
@ -0,0 +1,20 @@
|
||||
ItemDef(
|
||||
legacy_name: "Quad Med Jump",
|
||||
legacy_description: "testing123",
|
||||
kind: Tool((
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
stats: (
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
effect_power: 1.0,
|
||||
speed: 1.0,
|
||||
range: 1.0,
|
||||
energy_efficiency: 1.0,
|
||||
buff_strength: 1.0,
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Darkhound")),
|
||||
)
|
@ -36,5 +36,6 @@ hud-map-gnarling = Gnarling Fortification
|
||||
hud-map-chapel_site = Sea Chapel
|
||||
hud-map-adlet = Adlet Stronghold
|
||||
hud-map-haniwa = Haniwa Catacomb
|
||||
hud-map-cultist = Cultist Dungeon
|
||||
hud-map-terracotta = Terracotta Ruins
|
||||
hud-map-placed_by = Placed by { $name }
|
||||
|
@ -280,6 +280,9 @@ common-items-npc_weapons-unique-quadmedquick = Quad Med Quick
|
||||
common-items-npc_weapons-unique-quadsmallbasic = Quad Small Basic
|
||||
.desc = testing123
|
||||
|
||||
common-items-npc_weapons-unique-darkhound = Darkhound
|
||||
.desc = testing123
|
||||
|
||||
common-items-npc_weapons-unique-roshwalr = Roshwalr
|
||||
.desc = testing123
|
||||
|
||||
|
@ -18,5 +18,4 @@
|
||||
([
|
||||
(2, 0.20),
|
||||
(4, 0.10),
|
||||
(5, 0.10),
|
||||
])
|
||||
|
@ -154,6 +154,7 @@ pub enum SiteKind {
|
||||
Adlet,
|
||||
Haniwa,
|
||||
DwarvenMine,
|
||||
Cultist,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -798,6 +798,7 @@ impl Body {
|
||||
match self {
|
||||
Body::BipedLarge(biped_large) => match biped_large.species {
|
||||
biped_large::Species::Dullahan => 400,
|
||||
biped_large::Species::Cultistwarlord | biped_large::Species::Cultistwarlock => 240,
|
||||
_ => 300,
|
||||
},
|
||||
Body::BirdLarge(body) => match body.species {
|
||||
@ -853,6 +854,7 @@ impl Body {
|
||||
// T1
|
||||
quadruped_medium::Species::Alpaca => 55,
|
||||
quadruped_medium::Species::Antelope => 70,
|
||||
quadruped_medium::Species::Darkhound => 80,
|
||||
quadruped_medium::Species::Camel => 100,
|
||||
quadruped_medium::Species::Cattle => 90,
|
||||
quadruped_medium::Species::Deer => 55,
|
||||
@ -889,7 +891,6 @@ impl Body {
|
||||
quadruped_medium::Species::Ngoubou => 590,
|
||||
quadruped_medium::Species::Roshwalr => 640,
|
||||
quadruped_medium::Species::Tarasque => 370,
|
||||
_ => 100,
|
||||
},
|
||||
Body::FishMedium(fish_medium) => match fish_medium.species {
|
||||
// T2
|
||||
@ -943,7 +944,7 @@ impl Body {
|
||||
biped_large::Species::Mountaintroll => 240,
|
||||
biped_large::Species::Swamptroll => 240,
|
||||
biped_large::Species::Dullahan => 600,
|
||||
biped_large::Species::Mindflayer => 1250,
|
||||
biped_large::Species::Mindflayer => 2000,
|
||||
biped_large::Species::Tidalwarrior => 1600,
|
||||
biped_large::Species::Yeti => 1800,
|
||||
biped_large::Species::Minotaur => 3000,
|
||||
@ -951,8 +952,8 @@ impl Body {
|
||||
biped_large::Species::Blueoni => 240,
|
||||
biped_large::Species::Redoni => 240,
|
||||
biped_large::Species::Huskbrute => 800,
|
||||
biped_large::Species::Cultistwarlord => 250,
|
||||
biped_large::Species::Cultistwarlock => 250,
|
||||
biped_large::Species::Cultistwarlord => 200,
|
||||
biped_large::Species::Cultistwarlock => 200,
|
||||
biped_large::Species::Gigasfrost => 30000,
|
||||
biped_large::Species::AdletElder => 1500,
|
||||
biped_large::Species::Tursus => 300,
|
||||
|
@ -599,11 +599,13 @@ fn default_main_tool(body: &Body) -> Item {
|
||||
)),
|
||||
quadruped_medium::Species::Saber
|
||||
| quadruped_medium::Species::Bonerattler
|
||||
| quadruped_medium::Species::Darkhound
|
||||
| quadruped_medium::Species::Lion
|
||||
| quadruped_medium::Species::Snowleopard => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.quadmedjump",
|
||||
)),
|
||||
quadruped_medium::Species::Darkhound => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.darkhound",
|
||||
)),
|
||||
// Below uniques still follow quadmedcharge just with stat alterations
|
||||
quadruped_medium::Species::Moose | quadruped_medium::Species::Tuskram => {
|
||||
Some(Item::new_from_asset_expect(
|
||||
|
@ -18,6 +18,7 @@ pub enum DungeonKindMeta {
|
||||
Haniwa,
|
||||
SeaChapel,
|
||||
Terracotta,
|
||||
Cultist,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
@ -32,6 +32,7 @@ impl Site {
|
||||
| SiteKind::ChapelSite(_)
|
||||
| SiteKind::Terracotta(_)
|
||||
| SiteKind::Gnarling(_)
|
||||
| SiteKind::Cultist(_)
|
||||
| SiteKind::PirateHideout(_)
|
||||
| SiteKind::JungleRuin(_)
|
||||
| SiteKind::RockCircle(_)
|
||||
|
@ -1076,17 +1076,20 @@ impl<'a> AgentData<'a> {
|
||||
if let Some(ability_spec) = item.ability_spec() {
|
||||
match &*ability_spec {
|
||||
AbilitySpec::Custom(spec) => match spec.as_str() {
|
||||
"Oni" | "Sword Simple" => Tactic::SwordSimple,
|
||||
"Staff Simple" => Tactic::Staff,
|
||||
"Oni" | "Sword Simple" | "BipedLargeCultistSword" => {
|
||||
Tactic::SwordSimple
|
||||
},
|
||||
"Staff Simple" | "BipedLargeCultistStaff" => Tactic::Staff,
|
||||
"BipedLargeCultistHammer" => Tactic::Hammer,
|
||||
"Simple Flying Melee" => Tactic::SimpleFlyingMelee,
|
||||
"Bow Simple" | "Boreal Bow" => Tactic::Bow,
|
||||
"Bow Simple" | "Boreal Bow" | "BipedLargeCultistBow" => Tactic::Bow,
|
||||
"Stone Golem" | "Coral Golem" => Tactic::StoneGolem,
|
||||
"Iron Golem" => Tactic::IronGolem,
|
||||
"Quad Med Quick" => Tactic::CircleCharge {
|
||||
radius: 3,
|
||||
circle_time: 2,
|
||||
},
|
||||
"Quad Med Jump" => Tactic::QuadMedJump,
|
||||
"Quad Med Jump" | "Darkhound" => Tactic::QuadMedJump,
|
||||
"Quad Med Charge" => Tactic::CircleCharge {
|
||||
radius: 6,
|
||||
circle_time: 1,
|
||||
|
@ -114,7 +114,8 @@ impl Animation for ComboAnimation {
|
||||
},
|
||||
Some(
|
||||
"common.abilities.custom.cyclops.doublestrike"
|
||||
| "common.abilities.hammersimple.doublestrike",
|
||||
| "common.abilities.hammersimple.doublestrike"
|
||||
| "common.abilities.custom.biped_large_cultist.hammer.doublestrike",
|
||||
) => {
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
match strike {
|
||||
@ -152,6 +153,7 @@ impl Animation for ComboAnimation {
|
||||
Some(
|
||||
"common.abilities.custom.dullahan.melee"
|
||||
| "common.abilities.swordsimple.doublestrike"
|
||||
| "common.abilities.custom.biped_large_cultist.sword.doublestrike"
|
||||
| "common.abilities.custom.terracotta_pursuer.doublestrike"
|
||||
| "common.abilities.custom.terracotta_demolisher.triplestrike",
|
||||
) => {
|
||||
|
@ -585,6 +585,9 @@ image_ids! {
|
||||
mmap_site_haniwa: "voxygen.element.ui.map.buttons.haniwa",
|
||||
mmap_site_haniwa_hover: "voxygen.element.ui.map.buttons.haniwa_hover",
|
||||
mmap_site_haniwa_bg: "voxygen.element.ui.map.buttons.haniwa_bg",
|
||||
mmap_site_cultist: "voxygen.element.ui.map.buttons.mindflayer",
|
||||
mmap_site_cultist_hover: "voxygen.element.ui.map.buttons.mindflayer_hover",
|
||||
mmap_site_cultist_bg: "voxygen.element.ui.map.buttons.mindflayer_bg",
|
||||
mmap_site_minotaur: "voxygen.element.ui.map.buttons.minotaur",
|
||||
mmap_site_minotaur_hover: "voxygen.element.ui.map.buttons.minotaur_hover",
|
||||
mmap_site_minotaur_bg: "voxygen.element.ui.map.buttons.minotaur_bg",
|
||||
|
@ -927,6 +927,7 @@ impl<'a> Widget for Map<'a> {
|
||||
SiteKind::Bridge => i18n.get_msg("hud-map-bridge"),
|
||||
SiteKind::Adlet => i18n.get_msg("hud-map-adlet"),
|
||||
SiteKind::Haniwa => i18n.get_msg("hud-map-haniwa"),
|
||||
SiteKind::Cultist => i18n.get_msg("hud-map-cultist"),
|
||||
SiteKind::DwarvenMine => i18n.get_msg("hud-map-df_mine"),
|
||||
});
|
||||
let (difficulty, desc) = match &site.kind {
|
||||
@ -957,6 +958,7 @@ impl<'a> Widget for Map<'a> {
|
||||
SiteKind::Bridge => (None, i18n.get_msg("hud-map-bridge")),
|
||||
SiteKind::Adlet => (Some(1), i18n.get_msg("hud-map-adlet")),
|
||||
SiteKind::Haniwa => (Some(3), i18n.get_msg("hud-map-haniwa")),
|
||||
SiteKind::Cultist => (Some(5), i18n.get_msg("hud-map-cultist")),
|
||||
SiteKind::DwarvenMine => (Some(5), i18n.get_msg("hud-map-df_mine")),
|
||||
};
|
||||
let desc = desc.into_owned() + &get_site_economy(site_rich);
|
||||
@ -970,6 +972,7 @@ impl<'a> Widget for Map<'a> {
|
||||
SiteKind::Gnarling => self.imgs.mmap_site_gnarling,
|
||||
SiteKind::Adlet => self.imgs.mmap_site_adlet,
|
||||
SiteKind::Haniwa => self.imgs.mmap_site_haniwa,
|
||||
SiteKind::Cultist => self.imgs.mmap_site_cultist,
|
||||
SiteKind::DwarvenMine => self.imgs.mmap_site_mine,
|
||||
SiteKind::Dungeon { difficulty } => match difficulty {
|
||||
4 => self.imgs.mmap_site_minotaur,
|
||||
@ -994,6 +997,7 @@ impl<'a> Widget for Map<'a> {
|
||||
SiteKind::Gnarling => self.imgs.mmap_site_gnarling_hover,
|
||||
SiteKind::Adlet => self.imgs.mmap_site_adlet_hover,
|
||||
SiteKind::Haniwa => self.imgs.mmap_site_haniwa_hover,
|
||||
SiteKind::Cultist => self.imgs.mmap_site_cultist_hover,
|
||||
SiteKind::DwarvenMine => self.imgs.mmap_site_mine_hover,
|
||||
SiteKind::Dungeon { difficulty } => match difficulty {
|
||||
4 => self.imgs.mmap_site_minotaur_hover,
|
||||
@ -1015,6 +1019,7 @@ impl<'a> Widget for Map<'a> {
|
||||
| SiteKind::Terracotta
|
||||
| SiteKind::Adlet
|
||||
| SiteKind::Haniwa
|
||||
| SiteKind::Cultist
|
||||
| SiteKind::DwarvenMine => match difficulty {
|
||||
Some(0) => QUALITY_LOW,
|
||||
Some(1) => QUALITY_COMMON,
|
||||
@ -1043,6 +1048,7 @@ impl<'a> Widget for Map<'a> {
|
||||
| SiteKind::ChapelSite
|
||||
| SiteKind::DwarvenMine
|
||||
| SiteKind::Haniwa
|
||||
| SiteKind::Cultist
|
||||
| SiteKind::Terracotta
|
||||
| SiteKind::Adlet => show_dungeons,
|
||||
SiteKind::Castle => show_castles,
|
||||
@ -1105,6 +1111,7 @@ impl<'a> Widget for Map<'a> {
|
||||
| SiteKind::Gnarling
|
||||
| SiteKind::ChapelSite
|
||||
| SiteKind::Haniwa
|
||||
| SiteKind::Cultist
|
||||
| SiteKind::Terracotta
|
||||
| SiteKind::Adlet => {
|
||||
if show_dungeons {
|
||||
|
@ -708,6 +708,7 @@ impl<'a> Widget for MiniMap<'a> {
|
||||
SiteKind::Bridge => None,
|
||||
SiteKind::Adlet => Some(1),
|
||||
SiteKind::Haniwa => Some(3),
|
||||
SiteKind::Cultist => Some(5),
|
||||
SiteKind::DwarvenMine => Some(5),
|
||||
};
|
||||
|
||||
@ -723,6 +724,7 @@ impl<'a> Widget for MiniMap<'a> {
|
||||
SiteKind::Bridge => self.imgs.mmap_site_bridge_bg,
|
||||
SiteKind::Adlet => self.imgs.mmap_site_adlet_bg,
|
||||
SiteKind::Haniwa => self.imgs.mmap_site_haniwa_bg,
|
||||
SiteKind::Cultist => self.imgs.mmap_site_cultist_bg,
|
||||
SiteKind::DwarvenMine => self.imgs.mmap_site_mine_bg,
|
||||
})
|
||||
.x_y_position_relative_to(
|
||||
@ -753,6 +755,7 @@ impl<'a> Widget for MiniMap<'a> {
|
||||
SiteKind::Bridge => self.imgs.mmap_site_bridge,
|
||||
SiteKind::Adlet => self.imgs.mmap_site_adlet,
|
||||
SiteKind::Haniwa => self.imgs.mmap_site_haniwa,
|
||||
SiteKind::Cultist => self.imgs.mmap_site_cultist,
|
||||
SiteKind::DwarvenMine => self.imgs.mmap_site_mine,
|
||||
})
|
||||
.middle_of(state.ids.mmap_site_icons_bgs[i])
|
||||
|
@ -414,7 +414,17 @@ impl Civs {
|
||||
)?,
|
||||
SiteKind::DwarvenMine,
|
||||
),
|
||||
/*86..=97 => (
|
||||
87..=92 => (
|
||||
find_site_loc(
|
||||
&mut ctx,
|
||||
&ProximityRequirementsBuilder::new()
|
||||
.avoid_all_of(this.cultist_enemies(), 40)
|
||||
.finalize(&world_dims),
|
||||
&SiteKind::Cultist,
|
||||
)?,
|
||||
SiteKind::Cultist,
|
||||
),
|
||||
/*97..=102 => (
|
||||
find_site_loc(
|
||||
&mut ctx,
|
||||
&ProximityRequirementsBuilder::new()
|
||||
@ -425,7 +435,7 @@ impl Civs {
|
||||
)?,
|
||||
SiteKind::Castle,
|
||||
),
|
||||
98..=103 => (SiteKind::Citadel, (&castle_enemies, 20)),
|
||||
103..=108 => (SiteKind::Citadel, (&castle_enemies, 20)),
|
||||
*/
|
||||
_ => (
|
||||
find_site_loc(
|
||||
@ -480,6 +490,7 @@ impl Civs {
|
||||
SiteKind::TrollCave => (4i32, 1.5),
|
||||
SiteKind::Camp => (4i32, 1.5),
|
||||
SiteKind::DwarvenMine => (8i32, 3.0),
|
||||
SiteKind::Cultist => (24i32, 10.0),
|
||||
};
|
||||
|
||||
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
|
||||
@ -663,6 +674,11 @@ impl Civs {
|
||||
&mut rng,
|
||||
wpos,
|
||||
)),
|
||||
SiteKind::Cultist => WorldSite::cultist(site2::Site::generate_cultist(
|
||||
&Land::from_sim(ctx.sim),
|
||||
&mut rng,
|
||||
wpos,
|
||||
)),
|
||||
}
|
||||
});
|
||||
sim_site.site_tmp = Some(site);
|
||||
@ -1550,6 +1566,13 @@ impl Civs {
|
||||
})
|
||||
}
|
||||
|
||||
fn cultist_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {
|
||||
self.sites().filter_map(|s| match s.kind {
|
||||
SiteKind::Tree | SiteKind::GiantTree => None,
|
||||
_ => Some(s.center),
|
||||
})
|
||||
}
|
||||
|
||||
fn dungeon_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {
|
||||
self.sites().filter_map(|s| match s.kind {
|
||||
SiteKind::Tree | SiteKind::GiantTree => None,
|
||||
@ -1964,6 +1987,7 @@ pub enum SiteKind {
|
||||
Camp,
|
||||
DwarvenMine,
|
||||
JungleRuin,
|
||||
Cultist,
|
||||
}
|
||||
|
||||
impl SiteKind {
|
||||
@ -2073,6 +2097,7 @@ impl SiteKind {
|
||||
&& !chunk.river.near_water()
|
||||
&& !chunk.near_cliffs()
|
||||
},
|
||||
SiteKind::Cultist => on_land() && chunk.temp < 0.5 && chunk.near_cliffs(),
|
||||
SiteKind::Castle => {
|
||||
if chunk.tree_density > 0.4 || chunk.river.near_water() || chunk.near_cliffs() {
|
||||
return false;
|
||||
|
@ -216,6 +216,7 @@ impl World {
|
||||
civ::SiteKind::Terracotta => world_msg::SiteKind::Terracotta,
|
||||
civ::SiteKind::Citadel => world_msg::SiteKind::Castle,
|
||||
civ::SiteKind::Bridge(_, _) => world_msg::SiteKind::Bridge,
|
||||
civ::SiteKind::Cultist => world_msg::SiteKind::Cultist,
|
||||
civ::SiteKind::Adlet => world_msg::SiteKind::Adlet,
|
||||
civ::SiteKind::Haniwa => world_msg::SiteKind::Haniwa,
|
||||
},
|
||||
|
@ -131,6 +131,7 @@ impl Environment {
|
||||
SiteKind::GiantTree(_) => (),
|
||||
SiteKind::Gnarling(_) => {},
|
||||
SiteKind::Adlet(_) => {},
|
||||
SiteKind::Cultist(_) => {},
|
||||
SiteKind::Haniwa(_) => {},
|
||||
SiteKind::JungleRuin(_) => {},
|
||||
SiteKind::ChapelSite(_) => {},
|
||||
|
@ -84,6 +84,7 @@ pub enum SiteKind {
|
||||
RockCircle(site2::Site),
|
||||
TrollCave(site2::Site),
|
||||
Camp(site2::Site),
|
||||
Cultist(site2::Site),
|
||||
}
|
||||
|
||||
impl Site {
|
||||
@ -206,6 +207,13 @@ impl Site {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cultist(cl: site2::Site) -> Self {
|
||||
Self {
|
||||
kind: SiteKind::Cultist(cl),
|
||||
economy: Economy::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dwarven_mine(dm: site2::Site) -> Self {
|
||||
Self {
|
||||
kind: SiteKind::DwarvenMine(dm),
|
||||
@ -265,6 +273,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.radius(),
|
||||
SiteKind::Adlet(ad) => ad.radius(),
|
||||
SiteKind::Haniwa(ha) => ha.radius(),
|
||||
SiteKind::Cultist(cl) => cl.radius(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +301,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.origin,
|
||||
SiteKind::Adlet(ad) => ad.origin,
|
||||
SiteKind::Haniwa(ha) => ha.origin,
|
||||
SiteKind::Cultist(cl) => cl.origin,
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,6 +329,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.spawn_rules(wpos),
|
||||
SiteKind::Adlet(ad) => ad.spawn_rules(wpos),
|
||||
SiteKind::Haniwa(ha) => ha.spawn_rules(wpos),
|
||||
SiteKind::Cultist(cl) => cl.spawn_rules(wpos),
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,6 +357,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.name(),
|
||||
SiteKind::Adlet(ad) => ad.name(),
|
||||
SiteKind::Haniwa(ha) => ha.name(),
|
||||
SiteKind::Cultist(cl) => cl.name(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,6 +405,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.render(canvas, dynamic_rng),
|
||||
SiteKind::Adlet(ad) => ad.render(canvas, dynamic_rng),
|
||||
SiteKind::Haniwa(ha) => ha.render(canvas, dynamic_rng),
|
||||
SiteKind::Cultist(cl) => cl.render(canvas, dynamic_rng),
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,6 +447,7 @@ impl Site {
|
||||
SiteKind::Bridge(b) => b.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||
SiteKind::Adlet(ad) => ad.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||
SiteKind::Haniwa(ha) => ha.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||
SiteKind::Cultist(cl) => cl.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,6 +489,7 @@ impl Site {
|
||||
SiteKind::Bridge(site2) => Some(site2),
|
||||
SiteKind::Adlet(site2) => Some(site2),
|
||||
SiteKind::Haniwa(site2) => Some(site2),
|
||||
SiteKind::Cultist(site2) => Some(site2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,8 @@ impl Site {
|
||||
PlotKind::TerracottaPalace(tp) => Some(tp.spawn_rules(wpos)),
|
||||
PlotKind::TerracottaHouse(th) => Some(th.spawn_rules(wpos)),
|
||||
PlotKind::TerracottaYard(ty) => Some(ty.spawn_rules(wpos)),
|
||||
//PlotKind::DwarvenMine(m) => Some(m.spawn_rules(wpos)),
|
||||
PlotKind::Cultist(cl) => Some(cl.spawn_rules(wpos)),
|
||||
PlotKind::DwarvenMine(dm) => Some(dm.spawn_rules(wpos)),
|
||||
_ => None,
|
||||
})
|
||||
.fold(base_spawn_rules, |a, b| a.combine(b))
|
||||
@ -1726,6 +1727,49 @@ impl Site {
|
||||
site
|
||||
}
|
||||
|
||||
pub fn generate_cultist(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
|
||||
let mut rng = reseed(rng);
|
||||
let mut site = Site {
|
||||
origin,
|
||||
|
||||
name: {
|
||||
let name = NameGen::location(&mut rng).generate();
|
||||
match rng.gen_range(0..5) {
|
||||
0 => format!("{} Dungeon", name),
|
||||
1 => format!("{} Lair", name),
|
||||
2 => format!("{} Crib", name),
|
||||
3 => format!("{} Catacombs", name),
|
||||
_ => format!("{} Pit", name),
|
||||
}
|
||||
},
|
||||
|
||||
//name: NameGen::location(&mut rng).generate_adlet(),
|
||||
..Site::default()
|
||||
};
|
||||
let size = 22.0 as i32;
|
||||
let aabr = Aabr {
|
||||
min: Vec2::broadcast(-size),
|
||||
max: Vec2::broadcast(size),
|
||||
};
|
||||
{
|
||||
let cultist = plot::Cultist::generate(land, &mut reseed(&mut rng), &site, aabr);
|
||||
let cultist_alt = cultist.alt;
|
||||
let plot = site.create_plot(Plot {
|
||||
kind: PlotKind::Cultist(cultist),
|
||||
root_tile: aabr.center(),
|
||||
tiles: aabr_tiles(aabr).collect(),
|
||||
seed: rng.gen(),
|
||||
});
|
||||
|
||||
site.blit_aabr(aabr, Tile {
|
||||
kind: TileKind::Building,
|
||||
plot: Some(plot),
|
||||
hard_alt: Some(cultist_alt),
|
||||
});
|
||||
}
|
||||
site
|
||||
}
|
||||
|
||||
pub fn generate_bridge(
|
||||
land: &Land,
|
||||
index: IndexRef,
|
||||
@ -2083,6 +2127,7 @@ impl Site {
|
||||
PlotKind::TerracottaYard(terracotta_yard) => {
|
||||
terracotta_yard.render_collect(self, canvas)
|
||||
},
|
||||
PlotKind::Cultist(cultist) => cultist.render_collect(self, canvas),
|
||||
PlotKind::DesertCityMultiPlot(desert_city_multi_plot) => {
|
||||
desert_city_multi_plot.render_collect(self, canvas)
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ mod citadel;
|
||||
mod cliff_tower;
|
||||
mod coastal_house;
|
||||
mod coastal_workshop;
|
||||
mod cultist;
|
||||
mod desert_city_arena;
|
||||
mod desert_city_multiplot;
|
||||
mod desert_city_temple;
|
||||
@ -33,7 +34,7 @@ mod workshop;
|
||||
pub use self::{
|
||||
adlet::AdletStronghold, airship_dock::AirshipDock, bridge::Bridge, camp::Camp, castle::Castle,
|
||||
citadel::Citadel, cliff_tower::CliffTower, coastal_house::CoastalHouse,
|
||||
coastal_workshop::CoastalWorkshop, desert_city_arena::DesertCityArena,
|
||||
coastal_workshop::CoastalWorkshop, cultist::Cultist, desert_city_arena::DesertCityArena,
|
||||
desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple,
|
||||
dungeon::Dungeon, dwarven_mine::DwarvenMine, giant_tree::GiantTree,
|
||||
gnarling::GnarlingFortification, haniwa::Haniwa, house::House, jungle_ruin::JungleRuin,
|
||||
@ -94,6 +95,7 @@ pub enum PlotKind {
|
||||
JungleRuin(JungleRuin),
|
||||
Plaza,
|
||||
Castle(Castle),
|
||||
Cultist(Cultist),
|
||||
Road(Path<Vec2<i32>>),
|
||||
Dungeon(Dungeon),
|
||||
Gnarling(GnarlingFortification),
|
||||
|
855
world/src/site2/plot/cultist.rs
Normal file
855
world/src/site2/plot/cultist.rs
Normal file
@ -0,0 +1,855 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
site2::plot::dungeon::inscribed_polystar,
|
||||
util::{sampler::Sampler, RandomField, DIAGONALS},
|
||||
Land,
|
||||
};
|
||||
use common::{
|
||||
comp::misc::PortalData,
|
||||
generation::{EntityInfo, SpecialEntity},
|
||||
resources::Secs,
|
||||
terrain::SpriteKind,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use std::{f32::consts::TAU, sync::Arc};
|
||||
use vek::*;
|
||||
|
||||
pub struct Room {
|
||||
room_base: i32,
|
||||
room_center: Vec2<i32>,
|
||||
clear_center: Vec2<i32>,
|
||||
mob_room: bool,
|
||||
boss_room: bool,
|
||||
}
|
||||
|
||||
pub struct Cultist {
|
||||
base: i32,
|
||||
bounds: Aabr<i32>,
|
||||
pub(crate) alt: i32,
|
||||
pub(crate) center: Vec2<i32>,
|
||||
pub(crate) room_data: Vec<Room>,
|
||||
room_size: i32,
|
||||
floors: i32,
|
||||
}
|
||||
impl Cultist {
|
||||
pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
|
||||
let bounds = Aabr {
|
||||
min: site.tile_wpos(tile_aabr.min),
|
||||
max: site.tile_wpos(tile_aabr.max),
|
||||
};
|
||||
let center = bounds.center();
|
||||
let base = land.get_alt_approx(center) as i32;
|
||||
let room_size = 30;
|
||||
let mut room_data = vec![];
|
||||
|
||||
let floors = 3;
|
||||
for f in 0..=floors {
|
||||
for s in 0..=1 {
|
||||
// rooms
|
||||
let rooms = [1, 2];
|
||||
if rooms.contains(&f) {
|
||||
for dir in DIAGONALS {
|
||||
let room_base = base - (f * (2 * (room_size))) - (s * room_size);
|
||||
let room_center = center + (dir * ((room_size * 2) - 5 + (10 * s)));
|
||||
let clear_center = center + (dir * ((room_size * 2) - 6 + (10 * s)));
|
||||
let mob_room = s < 1;
|
||||
room_data.push(Room {
|
||||
room_base,
|
||||
room_center,
|
||||
clear_center,
|
||||
mob_room,
|
||||
boss_room: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let boss_room_base = base - (6 * room_size);
|
||||
room_data.push(Room {
|
||||
room_base: boss_room_base,
|
||||
room_center: center,
|
||||
clear_center: center,
|
||||
mob_room: false,
|
||||
boss_room: true,
|
||||
});
|
||||
|
||||
Self {
|
||||
bounds,
|
||||
alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
|
||||
as i32
|
||||
+ 2,
|
||||
base,
|
||||
center,
|
||||
room_size,
|
||||
room_data,
|
||||
floors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
||||
SpawnRules {
|
||||
waypoints: false,
|
||||
trees: wpos.distance_squared(self.bounds.center()) > (75_i32).pow(2),
|
||||
..SpawnRules::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Structure for Cultist {
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"render_cultist\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_cultist")]
|
||||
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
|
||||
let center = self.center;
|
||||
let base = self.base;
|
||||
let room_size = self.room_size;
|
||||
let floors = self.floors;
|
||||
let mut thread_rng = thread_rng();
|
||||
let candles_lite = Fill::Sampling(Arc::new(|wpos| {
|
||||
Some(match (RandomField::new(0).get(wpos)) % 30 {
|
||||
0 => Block::air(SpriteKind::Candle),
|
||||
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
|
||||
})
|
||||
}));
|
||||
|
||||
let mut tower_positions = vec![];
|
||||
let mut clear_positions = vec![];
|
||||
let room_data = &self.room_data;
|
||||
let mut star_positions = vec![];
|
||||
let mut sprite_positions = vec![];
|
||||
let mut random_npcs = vec![];
|
||||
|
||||
let rock_broken = Fill::Sampling(Arc::new(|center| {
|
||||
Some(match (RandomField::new(0).get(center)) % 52 {
|
||||
0..=8 => Block::new(BlockKind::Rock, Rgb::new(60, 55, 65)),
|
||||
9..=17 => Block::new(BlockKind::Rock, Rgb::new(65, 60, 70)),
|
||||
18..=26 => Block::new(BlockKind::Rock, Rgb::new(70, 65, 75)),
|
||||
27..=35 => Block::new(BlockKind::Rock, Rgb::new(75, 70, 80)),
|
||||
36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
|
||||
_ => Block::new(BlockKind::Rock, Rgb::new(55, 50, 60)),
|
||||
})
|
||||
}));
|
||||
let rock = Fill::Brick(BlockKind::Rock, Rgb::new(55, 50, 60), 24);
|
||||
let water = Fill::Block(Block::new(BlockKind::Water, Rgb::zero()));
|
||||
let key_door = Fill::Block(Block::air(SpriteKind::KeyDoor));
|
||||
let key_hole = Fill::Block(Block::air(SpriteKind::Keyhole));
|
||||
let gold_chain = Fill::Block(Block::air(SpriteKind::SeaDecorChain));
|
||||
|
||||
for room in room_data {
|
||||
let (room_base, room_center) = (room.room_base, room.room_center);
|
||||
// rooms
|
||||
// encapsulation
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (room_center - room_size - 23).with_z(room_base - room_size - 1),
|
||||
max: (room_center + room_size + 23).with_z(room_base - 2),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (room_center - room_size - 2).with_z(room_base - room_size - 1),
|
||||
max: (room_center + room_size + 2).with_z(room_base - 2),
|
||||
})
|
||||
.fill(rock_broken.clone());
|
||||
|
||||
// solid floor
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (room_center - room_size - 10).with_z(room_base - room_size - 2),
|
||||
max: (room_center + room_size + 10).with_z(room_base - room_size - 1),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
// floor candles
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (room_center - room_size + 1).with_z(room_base - room_size - 1),
|
||||
max: (room_center + room_size - 1).with_z(room_base - room_size),
|
||||
})
|
||||
.fill(candles_lite.clone());
|
||||
}
|
||||
|
||||
for s in 0..=floors {
|
||||
let room_base = base - (s * (2 * room_size));
|
||||
|
||||
// center pit
|
||||
for p in 3..=5 {
|
||||
let pos = 3 * p;
|
||||
let radius = pos * 2;
|
||||
let amount = pos;
|
||||
let clear_radius = radius - 8;
|
||||
let tower_pos = place_circular(center, radius as f32, amount);
|
||||
let clear_pos = place_circular(center, clear_radius as f32, amount);
|
||||
tower_positions.extend(tower_pos);
|
||||
clear_positions.extend(clear_pos);
|
||||
}
|
||||
for tower_center in &tower_positions {
|
||||
let height_var =
|
||||
(RandomField::new(0).get(tower_center.with_z(room_base)) % 15) as i32;
|
||||
let height = height_var * 3;
|
||||
let size = height_var / 3;
|
||||
|
||||
// towers
|
||||
|
||||
// encapsulation if under ground
|
||||
if room_base < base {
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (tower_center - 9 - size).with_z(room_base - 2),
|
||||
max: (tower_center + 9 + size).with_z(room_base + 10 + height),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (tower_center - 9 - (size / 2)).with_z(room_base + 8 + height),
|
||||
max: (tower_center + 9 + (size / 2))
|
||||
.with_z(room_base + 10 + height + 5 + (height / 2)),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
}
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (tower_center - 8 - size).with_z(room_base),
|
||||
max: (tower_center + 8 + size).with_z(room_base + 10 + height),
|
||||
})
|
||||
.fill(rock_broken.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (tower_center - 8 - (size / 2)).with_z(room_base + 10 + height),
|
||||
max: (tower_center + 8 + (size / 2))
|
||||
.with_z(room_base + 10 + height + 5 + (height / 2)),
|
||||
})
|
||||
.fill(rock_broken.clone());
|
||||
|
||||
// vault carves floor 0
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(tower_center.x - 8 - size, tower_center.y - 4 - size)
|
||||
.with_z(room_base + size),
|
||||
max: Vec2::new(tower_center.x + 8 + size, tower_center.y + 4 + size)
|
||||
.with_z(room_base + height),
|
||||
},
|
||||
Dir::X,
|
||||
)
|
||||
.clear();
|
||||
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(tower_center.x - 4 - size, tower_center.y - 8 - size)
|
||||
.with_z(room_base + size),
|
||||
max: Vec2::new(tower_center.x + 4 + size, tower_center.y + 8 + size)
|
||||
.with_z(room_base + height),
|
||||
},
|
||||
Dir::Y,
|
||||
)
|
||||
.clear();
|
||||
// vault carves floor 1
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(
|
||||
tower_center.x - 8 - (size / 2),
|
||||
tower_center.y - 4 - (size / 2),
|
||||
)
|
||||
.with_z(room_base + 10 + height),
|
||||
max: Vec2::new(
|
||||
tower_center.x + 8 + (size / 2),
|
||||
tower_center.y + 4 + (size / 2),
|
||||
)
|
||||
.with_z(room_base + 10 + height + 5 + (height / 4) + (size / 2)),
|
||||
},
|
||||
Dir::X,
|
||||
)
|
||||
.clear();
|
||||
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(
|
||||
tower_center.x - 4 - (size / 2),
|
||||
tower_center.y - 8 - (size / 2),
|
||||
)
|
||||
.with_z(room_base + 10 + height),
|
||||
max: Vec2::new(
|
||||
tower_center.x + 4 + (size / 2),
|
||||
tower_center.y + 8 + (size / 2),
|
||||
)
|
||||
.with_z(room_base + 10 + height + 5 + (height / 4) + (size / 2)),
|
||||
},
|
||||
Dir::Y,
|
||||
)
|
||||
.clear();
|
||||
}
|
||||
// tower clears
|
||||
for (tower_center, clear_center) in tower_positions.iter().zip(&clear_positions) {
|
||||
let height_var =
|
||||
(RandomField::new(0).get(tower_center.with_z(room_base)) % 15) as i32;
|
||||
let height = height_var * 3;
|
||||
let size = height_var / 3;
|
||||
// tower clears
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (clear_center - 9 - size).with_z(room_base + size),
|
||||
max: (clear_center + 9 + size).with_z(room_base + 8 + height),
|
||||
})
|
||||
.clear();
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (clear_center - 8 - (size / 2)).with_z(room_base + 10 + height),
|
||||
max: (clear_center + 8 + (size / 2))
|
||||
.with_z(room_base + 8 + height + 5 + (height / 2)),
|
||||
})
|
||||
.clear();
|
||||
|
||||
// decay
|
||||
let decay_size = 8 + size;
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (clear_center - decay_size).with_z(room_base + 8 + height),
|
||||
max: (clear_center + decay_size).with_z(room_base + 10 + height),
|
||||
})
|
||||
.clear();
|
||||
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (clear_center - decay_size + 5)
|
||||
.with_z(room_base + 8 + height + 5 + (height / 2)),
|
||||
max: (clear_center + decay_size - 5)
|
||||
.with_z(room_base + 10 + height + 5 + (height / 2)),
|
||||
})
|
||||
.clear();
|
||||
}
|
||||
|
||||
// center clear
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (center - room_size + 10).with_z(room_base),
|
||||
max: (center + room_size - 10).with_z(room_base + (4 * (room_size))),
|
||||
})
|
||||
.clear();
|
||||
}
|
||||
// room clears
|
||||
for room in room_data {
|
||||
let (room_base, room_center, clear_center, mob_room, boss_room) = (
|
||||
room.room_base,
|
||||
room.room_center,
|
||||
room.clear_center,
|
||||
room.mob_room,
|
||||
room.boss_room,
|
||||
);
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (clear_center - room_size - 1).with_z(room_base - room_size),
|
||||
max: (clear_center + room_size + 1).with_z(room_base - 4),
|
||||
})
|
||||
.clear();
|
||||
|
||||
// room decor
|
||||
let decor_var = RandomField::new(0).get(room_center.with_z(room_base)) % 4;
|
||||
if mob_room {
|
||||
// room_center platform or water basin
|
||||
if decor_var < 3 {
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (room_center - room_size + 10).with_z(room_base - room_size - 1),
|
||||
max: (room_center + room_size - 10).with_z(room_base - 2),
|
||||
})
|
||||
.fill(rock_broken.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// carves
|
||||
let spacing = 12;
|
||||
let carve_length = room_size + 8;
|
||||
let carve_width = 3;
|
||||
for f in 0..3 {
|
||||
for c in 0..5 {
|
||||
// candles & chest & npcs
|
||||
let sprite_pos_1 = Vec2::new(
|
||||
room_center.x - room_size + (spacing / 2) + (spacing * c) - carve_width + 2,
|
||||
room_center.y - carve_length + 2,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f));
|
||||
sprite_positions.push(sprite_pos_1);
|
||||
|
||||
let sprite_pos_2 = Vec2::new(
|
||||
room_center.x - room_size + (spacing / 2) + (spacing * c) + carve_width - 2,
|
||||
room_center.y + carve_length - 2,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f));
|
||||
sprite_positions.push(sprite_pos_2);
|
||||
|
||||
let sprite_pos_3 = Vec2::new(
|
||||
room_center.x - carve_length + 2,
|
||||
room_center.y - room_size + (spacing / 2) + (spacing * c) - carve_width + 2,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f));
|
||||
sprite_positions.push(sprite_pos_3);
|
||||
|
||||
let sprite_pos_4 = Vec2::new(
|
||||
room_center.x + carve_length - 2,
|
||||
room_center.y - room_size + (spacing / 2) + (spacing * c) + carve_width - 2,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f));
|
||||
sprite_positions.push(sprite_pos_4);
|
||||
|
||||
let candle_limiter = painter.aabb(Aabb {
|
||||
min: (room_center - room_size + 10)
|
||||
.with_z(room_base - room_size - 2 + ((room_size / 3) * f)),
|
||||
max: (room_center + room_size - 10)
|
||||
.with_z(room_base - room_size + ((room_size / 3) * f)),
|
||||
});
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
room_center.y - carve_length,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
room_center.y + carve_length,
|
||||
)
|
||||
.with_z(
|
||||
room_base - room_size - 3
|
||||
+ (room_size / 3)
|
||||
+ ((room_size / 3) * f),
|
||||
),
|
||||
},
|
||||
Dir::Y,
|
||||
)
|
||||
.clear();
|
||||
|
||||
if mob_room {
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
room_center.y - carve_length,
|
||||
)
|
||||
.with_z(room_base - room_size - 2 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
room_center.y + carve_length,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
})
|
||||
.intersect(candle_limiter)
|
||||
.fill(rock.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
room_center.y - carve_length,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
room_center.y + carve_length,
|
||||
)
|
||||
.with_z(room_base - room_size + ((room_size / 3) * f)),
|
||||
})
|
||||
.intersect(candle_limiter)
|
||||
.fill(candles_lite.clone());
|
||||
}
|
||||
|
||||
painter
|
||||
.vault(
|
||||
Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - carve_length,
|
||||
room_center.y - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x + carve_length,
|
||||
room_center.y - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
)
|
||||
.with_z(
|
||||
room_base - room_size - 3
|
||||
+ (room_size / 3)
|
||||
+ ((room_size / 3) * f),
|
||||
),
|
||||
},
|
||||
Dir::X,
|
||||
)
|
||||
.clear();
|
||||
if mob_room {
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - carve_length,
|
||||
room_center.y - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
)
|
||||
.with_z(room_base - room_size - 2 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x + carve_length,
|
||||
room_center.y - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
})
|
||||
.intersect(candle_limiter)
|
||||
.fill(rock.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: Vec2::new(
|
||||
room_center.x - carve_length,
|
||||
room_center.y - room_size + (spacing / 2) + (spacing * c)
|
||||
- carve_width,
|
||||
)
|
||||
.with_z(room_base - room_size - 1 + ((room_size / 3) * f)),
|
||||
max: Vec2::new(
|
||||
room_center.x + carve_length,
|
||||
room_center.y - room_size
|
||||
+ (spacing / 2)
|
||||
+ (spacing * c)
|
||||
+ carve_width,
|
||||
)
|
||||
.with_z(room_base - room_size + ((room_size / 3) * f)),
|
||||
})
|
||||
.intersect(candle_limiter)
|
||||
.fill(candles_lite.clone());
|
||||
}
|
||||
}
|
||||
if mob_room {
|
||||
// mob room npcs
|
||||
for dir in CARDINALS {
|
||||
for d in 1..=4 {
|
||||
let npc_pos = (room_center + dir * ((spacing / 2) * d))
|
||||
.with_z(room_base - room_size + ((room_size / 3) * f));
|
||||
let pos_var = RandomField::new(0).get(npc_pos) % 10;
|
||||
if pos_var < 1 {
|
||||
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.cultist",
|
||||
&mut thread_rng,
|
||||
None,
|
||||
))
|
||||
} else if pos_var > 2 && f > 0 {
|
||||
painter.sphere_with_radius(npc_pos, 5_f32).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
let decor_var = RandomField::new(0).get(room_center.with_z(room_base)) % 4;
|
||||
if decor_var < 3 {
|
||||
// portal platform
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (room_center - 3).with_z(room_base - (room_size / 4) - 5),
|
||||
max: (room_center + 3).with_z(room_base - (room_size / 4) - 4),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
} else {
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (room_center - room_size + 10)
|
||||
.with_z(room_base - room_size - 3),
|
||||
max: (room_center + room_size - 10)
|
||||
.with_z(room_base - room_size - 2),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (room_center - room_size + 10)
|
||||
.with_z(room_base - room_size - 2),
|
||||
max: (room_center + room_size - 10)
|
||||
.with_z(room_base - room_size - 1),
|
||||
})
|
||||
.fill(water.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (room_center - room_size + 10)
|
||||
.with_z(room_base - room_size - 1),
|
||||
max: (room_center + room_size - 10)
|
||||
.with_z(room_base - (room_size / 4) - 3),
|
||||
})
|
||||
.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// room portals
|
||||
let mob_portal = room_center.with_z(room_base - (room_size / 4));
|
||||
let mob_portal_target = (room_center + 10).with_z(room_base - (room_size * 2));
|
||||
let mini_boss_portal = room_center.with_z(room_base - (room_size * 2));
|
||||
let exit_position = (center - 10).with_z(base - (6 * room_size));
|
||||
let boss_position = (center - 10).with_z(base - (7 * room_size));
|
||||
let boss_portal = center.with_z(base - (7 * room_size));
|
||||
|
||||
let mob_portal_pos = Vec3::new(
|
||||
mob_portal.x as f32,
|
||||
mob_portal.y as f32,
|
||||
mob_portal.z as f32,
|
||||
);
|
||||
let mob_portal_target_pos = Vec3::new(
|
||||
mob_portal_target.x as f32,
|
||||
mob_portal_target.y as f32,
|
||||
mob_portal_target.z as f32,
|
||||
);
|
||||
let mini_boss_portal_pos = Vec3::new(
|
||||
mini_boss_portal.x as f32,
|
||||
mini_boss_portal.y as f32,
|
||||
mini_boss_portal.z as f32,
|
||||
);
|
||||
let exit_pos = Vec3::new(
|
||||
exit_position.x as f32,
|
||||
exit_position.y as f32,
|
||||
exit_position.z as f32,
|
||||
);
|
||||
let boss_pos = Vec3::new(
|
||||
boss_position.x as f32,
|
||||
boss_position.y as f32,
|
||||
boss_position.z as f32,
|
||||
);
|
||||
let boss_portal_pos = Vec3::new(
|
||||
boss_portal.x as f32,
|
||||
boss_portal.y as f32,
|
||||
boss_portal.z as f32,
|
||||
);
|
||||
|
||||
let mini_boss_portal_target = [
|
||||
exit_pos, exit_pos, exit_pos, exit_pos, exit_pos, boss_pos, boss_pos, boss_pos,
|
||||
];
|
||||
|
||||
if mob_room {
|
||||
painter.spawn(EntityInfo::at(mob_portal_pos).into_special(
|
||||
SpecialEntity::Teleporter(PortalData {
|
||||
target: mob_portal_target_pos,
|
||||
requires_no_aggro: true,
|
||||
buildup_time: Secs(5.),
|
||||
}),
|
||||
));
|
||||
let mini_boss_portal_target_index = RandomField::new(0).get(mini_boss_portal)
|
||||
as usize
|
||||
% mini_boss_portal_target.len();
|
||||
let mini_boss_portal_target_pos =
|
||||
mini_boss_portal_target[mini_boss_portal_target_index];
|
||||
painter.spawn(EntityInfo::at(mini_boss_portal_pos).into_special(
|
||||
SpecialEntity::Teleporter(PortalData {
|
||||
target: mini_boss_portal_target_pos,
|
||||
requires_no_aggro: true,
|
||||
buildup_time: Secs(5.),
|
||||
}),
|
||||
));
|
||||
} else if boss_room {
|
||||
painter.spawn(EntityInfo::at(boss_portal_pos).into_special(
|
||||
SpecialEntity::Teleporter(PortalData {
|
||||
target: exit_pos,
|
||||
requires_no_aggro: true,
|
||||
buildup_time: Secs(5.),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
if !mob_room {
|
||||
if boss_room {
|
||||
let npc_pos = room_center.with_z(room_base - room_size);
|
||||
|
||||
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.mindflayer",
|
||||
&mut thread_rng,
|
||||
None,
|
||||
));
|
||||
} else {
|
||||
let npc_pos = (room_center - 2).with_z(room_base - room_size);
|
||||
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.warlock",
|
||||
&mut thread_rng,
|
||||
None,
|
||||
));
|
||||
|
||||
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.warlord",
|
||||
&mut thread_rng,
|
||||
None,
|
||||
));
|
||||
painter.spawn(
|
||||
EntityInfo::at(((room_center + 5).with_z(room_base - room_size)).as_())
|
||||
.with_asset_expect(
|
||||
"common.entity.dungeon.cultist.beastmaster",
|
||||
&mut thread_rng,
|
||||
None,
|
||||
),
|
||||
);
|
||||
}
|
||||
// gold chains
|
||||
let chain_positions = place_circular(room_center, 15.0, 10);
|
||||
for pos in chain_positions {
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: pos.with_z(room_base - 12),
|
||||
max: (pos + 1).with_z(room_base - 4),
|
||||
})
|
||||
.fill(gold_chain.clone());
|
||||
}
|
||||
}
|
||||
let down = if mob_room && decor_var < 3 {
|
||||
0
|
||||
} else if mob_room && decor_var > 2 {
|
||||
room_size
|
||||
} else {
|
||||
10
|
||||
};
|
||||
let magic_circle_bb = painter.cylinder(Aabb {
|
||||
min: (room_center - 15).with_z(room_base - 3 - down),
|
||||
max: (room_center + 16).with_z(room_base - 2 - down),
|
||||
});
|
||||
star_positions.push((magic_circle_bb, room_center));
|
||||
}
|
||||
// candles & chests & npcs
|
||||
for sprite_pos in sprite_positions {
|
||||
// keep center pit clear
|
||||
if sprite_pos.xy().distance_squared(center) > 40_i32.pow(2)
|
||||
|| sprite_pos.z < (base - (6 * room_size))
|
||||
{
|
||||
match (RandomField::new(0).get(sprite_pos + 1)) % 16 {
|
||||
0 => {
|
||||
if sprite_pos.z > (base - (6 * room_size)) {
|
||||
random_npcs.push(sprite_pos)
|
||||
}
|
||||
},
|
||||
1 => {
|
||||
// prisoners
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (sprite_pos - 1).with_z(sprite_pos.z),
|
||||
max: (sprite_pos + 2).with_z(sprite_pos.z + 3),
|
||||
})
|
||||
.fill(key_door.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: sprite_pos.with_z(sprite_pos.z + 3),
|
||||
max: (sprite_pos + 1).with_z(sprite_pos.z + 4),
|
||||
})
|
||||
.fill(key_hole.clone());
|
||||
painter
|
||||
.aabb(Aabb {
|
||||
min: (sprite_pos).with_z(sprite_pos.z),
|
||||
max: (sprite_pos + 1).with_z(sprite_pos.z + 2),
|
||||
})
|
||||
.clear();
|
||||
painter.spawn(EntityInfo::at(sprite_pos.as_()).with_asset_expect(
|
||||
match (RandomField::new(0).get(sprite_pos)) % 10 {
|
||||
0 => "common.entity.village.farmer",
|
||||
1 => "common.entity.village.guard",
|
||||
2 => "common.entity.village.hunter",
|
||||
3 => "common.entity.village.skinner",
|
||||
_ => "common.entity.village.villager",
|
||||
},
|
||||
&mut thread_rng,
|
||||
None,
|
||||
));
|
||||
},
|
||||
_ => {
|
||||
painter.sprite(
|
||||
sprite_pos,
|
||||
match (RandomField::new(0).get(sprite_pos)) % 20 {
|
||||
0 => SpriteKind::DungeonChest5,
|
||||
_ => SpriteKind::Candle,
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
// random_npcs around upper entrance and bottom portal
|
||||
for s in 0..=1 {
|
||||
let radius = 62.0 - (s * 50) as f32;
|
||||
let npcs = place_circular(center, radius, 8 - (s * 4));
|
||||
for npc_pos in npcs {
|
||||
random_npcs.push(npc_pos.with_z(base + 8 - ((6 * room_size) * s) - (s * 8)));
|
||||
}
|
||||
}
|
||||
for pos in random_npcs {
|
||||
let entities = [
|
||||
"common.entity.dungeon.cultist.cultist",
|
||||
"common.entity.dungeon.cultist.turret",
|
||||
"common.entity.dungeon.cultist.husk",
|
||||
"common.entity.dungeon.cultist.husk_brute",
|
||||
"common.entity.dungeon.cultist.hound",
|
||||
];
|
||||
let npc = entities[(RandomField::new(0).get(pos) % entities.len() as u32) as usize];
|
||||
painter.spawn(EntityInfo::at(pos.as_()).with_asset_expect(npc, &mut thread_rng, None));
|
||||
}
|
||||
|
||||
// outside portal
|
||||
let top_position = (center - 20).with_z(base + 125);
|
||||
let bottom_position = center.with_z(base - (6 * room_size));
|
||||
let top_pos = Vec3::new(
|
||||
top_position.x as f32,
|
||||
top_position.y as f32,
|
||||
top_position.z as f32,
|
||||
);
|
||||
let bottom_pos = Vec3::new(
|
||||
bottom_position.x as f32,
|
||||
bottom_position.y as f32,
|
||||
bottom_position.z as f32,
|
||||
);
|
||||
painter.spawn(
|
||||
EntityInfo::at(bottom_pos).into_special(SpecialEntity::Teleporter(PortalData {
|
||||
target: top_pos,
|
||||
requires_no_aggro: true,
|
||||
buildup_time: Secs(5.),
|
||||
})),
|
||||
);
|
||||
let stone_purple = Block::new(BlockKind::GlowingRock, Rgb::new(96, 0, 128));
|
||||
let magic_circle_bb = painter.cylinder(Aabb {
|
||||
min: (center - 15).with_z(base - (floors * (2 * room_size)) - 1),
|
||||
max: (center + 16).with_z(base - (floors * (2 * room_size))),
|
||||
});
|
||||
let magic_circle_bb_boss = painter.cylinder(Aabb {
|
||||
min: (center - 15).with_z(base - (7 * room_size) - 2),
|
||||
max: (center + 16).with_z(base - (7 * room_size) - 1),
|
||||
});
|
||||
star_positions.push((magic_circle_bb, center));
|
||||
star_positions.push((magic_circle_bb_boss, center));
|
||||
for (magic_circle_bb, position) in star_positions {
|
||||
let magic_circle = painter.prim(Primitive::sampling(
|
||||
magic_circle_bb,
|
||||
inscribed_polystar(position, 15.0, 7),
|
||||
));
|
||||
painter.fill(magic_circle, Fill::Block(stone_purple));
|
||||
}
|
||||
// base floor
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (center - room_size - 15).with_z(base - (floors * (2 * room_size)) - 3),
|
||||
max: (center + room_size + 15).with_z(base - (floors * (2 * room_size)) - 2),
|
||||
})
|
||||
.fill(rock.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn place_circular(center: Vec2<i32>, radius: f32, amount: i32) -> Vec<Vec2<i32>> {
|
||||
let phi = TAU / amount as f32;
|
||||
let mut positions = vec![];
|
||||
for n in 1..=amount {
|
||||
let pos = Vec2::new(
|
||||
center.x + (radius * ((n as f32 * phi).cos())) as i32,
|
||||
center.y + (radius * ((n as f32 * phi).sin())) as i32,
|
||||
);
|
||||
positions.push(pos);
|
||||
}
|
||||
positions
|
||||
}
|
@ -231,31 +231,12 @@ impl Room {
|
||||
let entities = match self.difficulty {
|
||||
2 => enemy_2(dynamic_rng, tile_wcenter),
|
||||
4 => enemy_4(dynamic_rng, tile_wcenter),
|
||||
5 => enemy_5(dynamic_rng, tile_wcenter),
|
||||
_ => enemy_fallback(dynamic_rng, tile_wcenter),
|
||||
};
|
||||
|
||||
for entity in entities {
|
||||
supplement.add_entity(entity);
|
||||
}
|
||||
} else {
|
||||
// Turrets
|
||||
// Turret has 1/5000 chance to spawn per voxel in fight room
|
||||
if dynamic_rng.gen_range(0..5000) == 0 {
|
||||
let pos = tile_wcenter.map(|e| e as f32)
|
||||
+ Vec3::<u32>::iota()
|
||||
.map(|e| {
|
||||
(RandomField::new(self.seed.wrapping_add(10 + e))
|
||||
.get(Vec3::from(tile_pos))
|
||||
% 32) as i32
|
||||
- 16
|
||||
})
|
||||
.map(|e| e as f32 / 16.0);
|
||||
if self.difficulty == 5 {
|
||||
let turret = turret_5(dynamic_rng, pos);
|
||||
supplement.add_entity(turret);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +261,6 @@ impl Room {
|
||||
let entities = match self.difficulty {
|
||||
2 => mini_boss_2(dynamic_rng, tile_wcenter),
|
||||
4 => mini_boss_4(dynamic_rng, tile_wcenter),
|
||||
5 => mini_boss_5(dynamic_rng, tile_wcenter),
|
||||
_ => mini_boss_fallback(dynamic_rng, tile_wcenter),
|
||||
};
|
||||
|
||||
@ -311,7 +291,6 @@ impl Room {
|
||||
let entities = match self.difficulty {
|
||||
2 => boss_2(dynamic_rng, tile_wcenter),
|
||||
4 => boss_4(dynamic_rng, tile_wcenter),
|
||||
5 => boss_5(dynamic_rng, tile_wcenter),
|
||||
_ => boss_fallback(dynamic_rng, tile_wcenter),
|
||||
};
|
||||
|
||||
@ -744,28 +723,6 @@ fn enemy_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInf
|
||||
entities
|
||||
}
|
||||
|
||||
fn enemy_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
let number = dynamic_rng.gen_range(1..=3);
|
||||
let mut entities = Vec::new();
|
||||
entities.resize_with(number, || {
|
||||
// TODO: give enemies health skills?
|
||||
let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32));
|
||||
match dynamic_rng.gen_range(0..=4) {
|
||||
0 => {
|
||||
entity.with_asset_expect("common.entity.dungeon.cultist.warlock", dynamic_rng, None)
|
||||
},
|
||||
1 => {
|
||||
entity.with_asset_expect("common.entity.dungeon.cultist.warlord", dynamic_rng, None)
|
||||
},
|
||||
_ => {
|
||||
entity.with_asset_expect("common.entity.dungeon.cultist.cultist", dynamic_rng, None)
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
entities
|
||||
}
|
||||
|
||||
fn enemy_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
let number = dynamic_rng.gen_range(2..=4);
|
||||
let mut entities = Vec::new();
|
||||
@ -777,10 +734,6 @@ fn enemy_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<En
|
||||
entities
|
||||
}
|
||||
|
||||
fn turret_5(dynamic_rng: &mut impl Rng, pos: Vec3<f32>) -> EntityInfo {
|
||||
EntityInfo::at(pos).with_asset_expect("common.entity.dungeon.cultist.turret", dynamic_rng, None)
|
||||
}
|
||||
|
||||
fn boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
vec![
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
@ -801,16 +754,6 @@ fn boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo
|
||||
]
|
||||
}
|
||||
|
||||
fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
vec![
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.mindflayer",
|
||||
dynamic_rng,
|
||||
None,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
fn boss_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
vec![
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
@ -843,40 +786,6 @@ fn mini_boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<Entit
|
||||
]
|
||||
}
|
||||
|
||||
fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
let mut entities = Vec::new();
|
||||
match dynamic_rng.gen_range(0..=2) {
|
||||
0 => {
|
||||
entities.push(
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.beastmaster",
|
||||
dynamic_rng,
|
||||
None,
|
||||
),
|
||||
);
|
||||
},
|
||||
1 => {
|
||||
entities.resize_with(2, || {
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.husk_brute",
|
||||
dynamic_rng,
|
||||
None,
|
||||
)
|
||||
});
|
||||
},
|
||||
_ => {
|
||||
entities.resize_with(10, || {
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
"common.entity.dungeon.cultist.husk",
|
||||
dynamic_rng,
|
||||
None,
|
||||
)
|
||||
});
|
||||
},
|
||||
}
|
||||
entities
|
||||
}
|
||||
|
||||
fn mini_boss_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
|
||||
vec![
|
||||
EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_asset_expect(
|
||||
@ -1469,7 +1378,6 @@ mod tests {
|
||||
let tile_wcenter = Vec3::new(0, 0, 0);
|
||||
boss_2(&mut dynamic_rng, tile_wcenter);
|
||||
boss_4(&mut dynamic_rng, tile_wcenter);
|
||||
boss_5(&mut dynamic_rng, tile_wcenter);
|
||||
boss_fallback(&mut dynamic_rng, tile_wcenter);
|
||||
}
|
||||
|
||||
@ -1480,7 +1388,6 @@ mod tests {
|
||||
let random_position = Vec3::new(0, 0, 0);
|
||||
enemy_2(&mut dynamic_rng, random_position);
|
||||
enemy_4(&mut dynamic_rng, random_position);
|
||||
enemy_5(&mut dynamic_rng, random_position);
|
||||
enemy_fallback(&mut dynamic_rng, random_position);
|
||||
}
|
||||
|
||||
@ -1491,14 +1398,6 @@ mod tests {
|
||||
let tile_wcenter = Vec3::new(0, 0, 0);
|
||||
mini_boss_2(&mut dynamic_rng, tile_wcenter);
|
||||
mini_boss_4(&mut dynamic_rng, tile_wcenter);
|
||||
mini_boss_5(&mut dynamic_rng, tile_wcenter);
|
||||
mini_boss_fallback(&mut dynamic_rng, tile_wcenter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_creating_turrets() {
|
||||
let mut dynamic_rng = thread_rng();
|
||||
let pos = Vec3::new(0.0, 0.0, 0.0);
|
||||
turret_5(&mut dynamic_rng, pos);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user