use super::*;
use crate::{
site2::gen::place_circular,
util::{RandomField, Sampler, NEIGHBORS},
Land,
};
use common::generation::EntityInfo;
use rand::prelude::*;
use std::sync::Arc;
use vek::*;
pub struct ArenaData {
base: i32,
center: Vec2<i32>,
entry_pos: Vec2<i32>,
boss_pos: Vec2<i32>,
radius: i32,
}
pub struct MyrmidonArena {
bounds: Aabr<i32>,
pub(crate) alt: i32,
pub(crate) arena_data: ArenaData,
}
impl MyrmidonArena {
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 + 2;
let diameter = (bounds.max.x - bounds.min.x).min(bounds.max.y - bounds.min.y) - 20;
let radius = diameter / 2;
let entry_pos = Vec2::new(center.x, center.y + radius - 12);
let boss_pos = Vec2::new(center.x, center.y - radius + 12);
let arena_data = ArenaData {
base,
center,
entry_pos,
boss_pos,
radius,
};
Self {
bounds,
alt: base,
arena_data,
}
}
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
waypoints: false,
trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
..SpawnRules::default()
}
}
}
impl Structure for MyrmidonArena {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_myrmidon_arena\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_myrmidon_arena")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.arena_data.base + 1;
let center = self.arena_data.center;
let mut thread_rng = thread_rng();
let sandstone_unbroken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 37 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
_ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
})
}));
let sandstone = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 42 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
_ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
})
}));
let weak_sandstone = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 42 {
0..=8 => Block::new(BlockKind::WeakRock, Rgb::new(245, 212, 129)),
9..=17 => Block::new(BlockKind::WeakRock, Rgb::new(246, 214, 133)),
18..=26 => Block::new(BlockKind::WeakRock, Rgb::new(247, 216, 136)),
27..=35 => Block::new(BlockKind::WeakRock, Rgb::new(248, 219, 142)),
36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
_ => Block::new(BlockKind::WeakRock, Rgb::new(235, 178, 99)),
})
}));
let roof_color = Fill::Brick(BlockKind::Sand, Rgb::new(115, 32, 2), 12);
let radius = self.arena_data.radius;
painter
.cylinder(Aabb {
min: (center - radius).with_z(base - 80),
max: (center + radius).with_z(base),
})
.fill(sandstone_unbroken.clone());
painter
.cylinder(Aabb {
min: (center - radius + 30).with_z(base - 20),
max: (center + radius - 30).with_z(base - 12),
})
.clear();
painter
.cylinder(Aabb {
min: (center - radius + 33).with_z(base - 12),
max: (center + radius - 33).with_z(base - 11),
})
.clear();
painter
.cylinder(Aabb {
min: (center - radius + 20).with_z(base - 11),
max: (center + radius - 20).with_z(base),
})
.clear();
painter
.cylinder(Aabb {
min: (center - radius + 15).with_z(base - 11),
max: (center + radius - 15).with_z(base - 1),
})
.clear();
painter
.cylinder(Aabb {
min: (center - radius + 5).with_z(base),
max: (center + radius - 5).with_z(base + 30),
})
.clear();
let circle_radius = radius / 5;
painter
.cylinder(Aabb {
min: (center - circle_radius).with_z(base - 20),
max: (center + circle_radius).with_z(base - 19),
})
.fill(sandstone_unbroken.clone());
painter
.cylinder(Aabb {
min: (center - circle_radius + 1).with_z(base - 19),
max: (center + circle_radius - 1).with_z(base - 18),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (center - circle_radius + 2).with_z(base - 19),
max: (center + circle_radius - 2).with_z(base - 18),
})
.clear();
for dir in CARDINALS {
let clear_pos = center + dir * circle_radius;
let clear_rand = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
if clear_rand < 1 {
painter
.cylinder(Aabb {
min: (clear_pos - 6).with_z(base - 19),
max: (clear_pos + 6).with_z(base - 18),
})
.clear();
}
}
let pillars = 3 + (RandomField::new(0).get(center.with_z(base + 2)) % 3) as i32;
let center_pillar_positions = place_circular(center, (circle_radius - 5) as f32, pillars);
for pillar in center_pillar_positions {
let pillar_rand = RandomField::new(0).get(pillar.with_z(base)) % 3;
if pillar_rand > 0 {
let pillar_heigth = pillar_rand as i32;
painter
.cylinder(Aabb {
min: (pillar - 3).with_z(base - 19),
max: (pillar + 3).with_z(base - 18),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar - 2).with_z(base - 18),
max: (pillar + 2).with_z(base - 18 + pillar_heigth),
})
.fill(sandstone.clone());
for dir in CARDINALS {
let clear_pos = pillar + dir * 3;
let clear_var = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
if clear_var > 0 {
painter
.sphere_with_radius(clear_pos.with_z(base - 18 + pillar_heigth), 3.0)
.clear();
}
}
}
}
let pillar_positions = place_circular(center, (radius - 5) as f32, 60);
let outer_pillar_positions = place_circular(center, (radius + 5) as f32, 60);
let top_decor_positions = place_circular(center, radius as f32, 100);
let top_platform_height = 45;
let platform_1_height = top_platform_height / 3;
let platform_2_height = platform_1_height * 2;
painter
.cylinder(Aabb {
min: (center - radius - 7).with_z(base + platform_1_height),
max: (center + radius + 7).with_z(base + platform_1_height + 1),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (center - radius + 5).with_z(base + platform_1_height),
max: (center + radius - 5).with_z(base + platform_1_height + 1),
})
.clear();
painter
.cylinder(Aabb {
min: (center - radius - 7).with_z(base + platform_2_height),
max: (center + radius + 7).with_z(base + platform_2_height + 1),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (center - radius + 5).with_z(base + platform_2_height),
max: (center + radius - 5).with_z(base + platform_2_height + 1),
})
.clear();
for pillar_pos in pillar_positions {
painter
.cylinder(Aabb {
min: (pillar_pos - 5).with_z(base - 1),
max: (pillar_pos + 5).with_z(base + (2 * (top_platform_height / 3))),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar_pos - 3).with_z(base + (2 * (top_platform_height / 3))),
max: (pillar_pos + 3).with_z(base + (2 * (top_platform_height / 3)) + 1),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar_pos - 2).with_z(base + (2 * (top_platform_height / 3)) + 1),
max: (pillar_pos + 2).with_z(base + top_platform_height),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar_pos - 5).with_z(base + top_platform_height),
max: (pillar_pos + 5).with_z(base + top_platform_height + 1),
})
.fill(sandstone.clone());
}
for outer_pillar_pos in outer_pillar_positions {
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 3).with_z(base - 10),
max: (outer_pillar_pos + 3).with_z(base - 5),
})
.fill(sandstone_unbroken.clone());
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 3).with_z(base - 5),
max: (outer_pillar_pos + 3).with_z(base - 1),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 3).with_z(base + platform_1_height + 1),
max: (outer_pillar_pos + 3).with_z(base + platform_1_height + 2),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 3).with_z(base + platform_2_height + 1),
max: (outer_pillar_pos + 3).with_z(base + platform_2_height + 2),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 2).with_z(base - 1),
max: (outer_pillar_pos + 2).with_z(base + top_platform_height),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (outer_pillar_pos - 5).with_z(base + top_platform_height),
max: (outer_pillar_pos + 5).with_z(base + top_platform_height + 1),
})
.fill(sandstone.clone());
}
for top_decor_pos in top_decor_positions {
for d in 0..4 {
painter
.cylinder(Aabb {
min: (top_decor_pos - 6 + d).with_z(base + top_platform_height + d),
max: (top_decor_pos + 6 - d).with_z(base + top_platform_height + 1 + d),
})
.fill(sandstone.clone());
}
let decay = (RandomField::new(0).get(top_decor_pos.with_z(base)) % 6) as i32;
if decay < 1 {
let decay_rand =
12.0 + (RandomField::new(0).get(top_decor_pos.with_z(base)) % 6) as f32;
painter
.sphere_with_radius(
top_decor_pos.with_z(base + top_platform_height + 10),
decay_rand,
)
.clear();
}
}
let entry_pos = self.arena_data.entry_pos;
let entry_pillar_pos_1 = Vec2::new(center.x, center.y + radius - 16);
let pillar_x_offset = [-12, 12];
let pillar_y_offset = [0, 29];
for pillar_x_dir in pillar_x_offset {
for pillar_y_dir in pillar_y_offset {
let pillar_pos = Vec2::new(
entry_pillar_pos_1.x + pillar_x_dir,
entry_pillar_pos_1.y + pillar_y_dir,
);
painter
.cylinder(Aabb {
min: (pillar_pos - 3).with_z(base - 2),
max: (pillar_pos + 3).with_z(base + 1),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar_pos - 2).with_z(base + 1),
max: (pillar_pos + 2).with_z(base + platform_2_height),
})
.fill(sandstone.clone());
painter
.cylinder(Aabb {
min: (pillar_pos - 3).with_z(base + platform_2_height),
max: (pillar_pos + 3).with_z(base + platform_2_height + 1),
})
.fill(sandstone.clone());
}
}
painter
.vault(
Aabb {
min: Vec2::new(entry_pos.x - 15, entry_pos.y).with_z(base - 2),
max: Vec2::new(entry_pos.x + 15, entry_pos.y + 12)
.with_z(base + platform_2_height),
},
Dir::Y,
)
.fill(sandstone.clone());
painter
.vault(
Aabb {
min: Vec2::new(entry_pos.x - 10, entry_pos.y).with_z(base),
max: Vec2::new(entry_pos.x + 10, entry_pos.y + 12)
.with_z(base + platform_2_height - 5),
},
Dir::Y,
)
.clear();
let height_handle = 5;
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 15, entry_pos.y - 7)
.with_z(base + platform_2_height - 1 + height_handle),
max: Vec2::new(entry_pos.x + 15, entry_pos.y + 28)
.with_z(base + platform_2_height + height_handle),
})
.fill(sandstone.clone());
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 16, entry_pos.y - 8)
.with_z(base + platform_2_height - 2 + height_handle),
max: Vec2::new(entry_pos.x + 16, entry_pos.y + 29)
.with_z(base + platform_2_height - 1 + height_handle),
})
.fill(sandstone.clone());
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 15, entry_pos.y - 7)
.with_z(base + platform_2_height - 3 + height_handle),
max: Vec2::new(entry_pos.x + 15, entry_pos.y + 28)
.with_z(base + platform_2_height - 2 + height_handle),
})
.fill(sandstone.clone());
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 16, entry_pos.y - 8)
.with_z(base + platform_2_height - 4 + height_handle),
max: Vec2::new(entry_pos.x + 16, entry_pos.y + 29)
.with_z(base + platform_2_height - 3 + height_handle),
})
.fill(sandstone.clone());
painter
.gable(
Aabb {
min: Vec2::new(entry_pos.x - 18, entry_pos.y - 8)
.with_z(base + platform_2_height + height_handle),
max: Vec2::new(entry_pos.x + 18, entry_pos.y + 29)
.with_z(base + platform_2_height + 4 + height_handle),
},
16,
Dir::Y,
)
.fill(sandstone.clone());
painter
.gable(
Aabb {
min: Vec2::new(entry_pos.x - 16, entry_pos.y - 6)
.with_z(base + platform_2_height + height_handle),
max: Vec2::new(entry_pos.x + 16, entry_pos.y + 27)
.with_z(base + platform_2_height + 5 + height_handle),
},
16,
Dir::Y,
)
.fill(roof_color.clone());
for g in 0..8 {
painter
.gable(
Aabb {
min: Vec2::new(entry_pos.x - 17, entry_pos.y - 4 + (4 * g))
.with_z(base + platform_2_height + height_handle),
max: Vec2::new(entry_pos.x + 17, entry_pos.y - 3 + (4 * g))
.with_z(base + platform_2_height + 6 + height_handle),
},
16,
Dir::Y,
)
.fill(roof_color.clone());
}
painter
.vault(
Aabb {
min: Vec2::new(entry_pos.x - 3, entry_pos.y - 19).with_z(base - 20),
max: Vec2::new(entry_pos.x + 3, entry_pos.y + 5).with_z(base - 12),
},
Dir::Y,
)
.clear();
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 3, entry_pos.y - 1).with_z(base - 20),
max: Vec2::new(entry_pos.x + 3, entry_pos.y + 1).with_z(base),
})
.fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 1, entry_pos.y - 1).with_z(base - 18),
max: Vec2::new(entry_pos.x, entry_pos.y).with_z(base - 17),
})
.fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyhole)));
painter
.aabb(Aabb {
min: Vec2::new(entry_pos.x - 1, entry_pos.y + 4).with_z(base - 20),
max: Vec2::new(entry_pos.x + 1, entry_pos.y + 5).with_z(base - 19),
})
.fill(Fill::Block(Block::air(SpriteKind::DungeonChest4)));
let mob_positions_low = place_circular(center, (radius - 12) as f32, 20);
for npc_position in mob_positions_low {
let entities = [
"common.entity.dungeon.myrmidon.hoplite",
"common.entity.dungeon.myrmidon.marksman",
"common.entity.dungeon.myrmidon.strategian",
];
let npc_pos = npc_position.with_z(base + 2);
let npc = entities[(RandomField::new(0).get(npc_pos) % entities.len() as u32) as usize];
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
npc,
&mut thread_rng,
None,
));
}
let mob_positions_pit = place_circular(center, (radius / 3) as f32, 10);
for npc_position in mob_positions_pit {
let entities = [
"common.entity.dungeon.myrmidon.hoplite",
"common.entity.dungeon.myrmidon.marksman",
"common.entity.dungeon.myrmidon.strategian",
];
let npc_pos = npc_position.with_z(base - 20);
let npc = entities[(RandomField::new(0).get(npc_pos) % entities.len() as u32) as usize];
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
npc,
&mut thread_rng,
None,
));
}
let mob_positions_high = place_circular(center, radius as f32, 20);
for npc_position in mob_positions_high {
let npc_pos = npc_position.with_z(base + top_platform_height + 5);
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
"common.entity.dungeon.myrmidon.marksman",
&mut thread_rng,
None,
));
}
let boss_pos = self.arena_data.boss_pos;
let npc_pos = Vec2::new(boss_pos.x - 50, boss_pos.y).with_z(base - 20);
painter
.aabb(Aabb {
min: (npc_pos - 12).with_z(base - 21),
max: (npc_pos + 12).with_z(base - 12),
})
.fill(sandstone_unbroken.clone());
painter
.vault(
Aabb {
min: Vec2::new(boss_pos.x - 4, boss_pos.y - 10).with_z(base - 22),
max: Vec2::new(boss_pos.x + 4, boss_pos.y + 18).with_z(base - 12),
},
Dir::Y,
)
.clear();
painter
.vault(
Aabb {
min: Vec2::new(boss_pos.x - 52, boss_pos.y - 4).with_z(base - 22),
max: Vec2::new(boss_pos.x, boss_pos.y + 4).with_z(base - 12),
},
Dir::X,
)
.clear();
painter
.vault(
Aabb {
min: Vec2::new(boss_pos.x - 4, boss_pos.y - 4).with_z(base - 22),
max: Vec2::new(boss_pos.x - 3, boss_pos.y + 4).with_z(base - 12),
},
Dir::X,
)
.fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
painter
.aabb(Aabb {
min: Vec2::new(boss_pos.x - 4, boss_pos.y + 4).with_z(base - 18),
max: Vec2::new(boss_pos.x - 3, boss_pos.y + 17).with_z(base - 17),
})
.fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
painter
.vault(
Aabb {
min: Vec2::new(boss_pos.x - 4, boss_pos.y + 17).with_z(base - 20),
max: Vec2::new(boss_pos.x + 4, boss_pos.y + 18).with_z(base - 12),
},
Dir::Y,
)
.fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
painter
.vault(
Aabb {
min: Vec2::new(boss_pos.x, boss_pos.y + 17).with_z(base - 18),
max: Vec2::new(boss_pos.x + 1, boss_pos.y + 18).with_z(base - 17),
},
Dir::Y,
)
.fill(Fill::Block(Block::air(SpriteKind::MinotaurKeyhole)));
let npc_pos = Vec2::new(boss_pos.x - 50, boss_pos.y).with_z(base - 20);
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
"common.entity.dungeon.myrmidon.minotaur",
&mut thread_rng,
None,
));
painter
.cylinder(Aabb {
min: (center - 3 * (radius / 4)).with_z(base - 79),
max: (center + 3 * (radius / 4)).with_z(base - 21),
})
.fill(weak_sandstone.clone());
painter
.cylinder(Aabb {
min: (center - 5).with_z(base - 79),
max: (center + 1).with_z(base - 18),
})
.fill(sandstone_unbroken.clone());
painter
.cylinder(Aabb {
min: (center - 4).with_z(base - 28),
max: center.with_z(base - 14),
})
.fill(sandstone_unbroken.clone());
painter
.cylinder(Aabb {
min: (center - 4).with_z(base - 14),
max: center.with_z(base - 13),
})
.fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
painter
.cylinder(Aabb {
min: (center - 3).with_z(base - 77),
max: (center - 1).with_z(base - 13),
})
.clear();
painter
.aabb(Aabb {
min: (center - 3).with_z(base - 43),
max: (center + 5).with_z(base - 33),
})
.clear();
painter
.cylinder(Aabb {
min: (center - 10).with_z(base - 79),
max: (center + 5).with_z(base - 77),
})
.clear();
for dir in NEIGHBORS {
for r in 1..=3 {
let room_pos = center + dir * ((radius / 6) * r);
for s in 0..3 {
let room_var = RandomField::new(0).get(room_pos.with_z(base + r + s)) % 6;
let room_dir = if room_var < 3 { Dir::Y } else { Dir::X };
let room = painter.vault(
Aabb {
min: (room_pos - (radius / 8)).with_z(base - 79 + (18 * s)),
max: (room_pos + (radius / 8)).with_z(base - 64 + (18 * s)),
},
room_dir,
);
match room_var {
0 => room.fill(weak_sandstone.clone()),
_ => room.clear(),
};
if room_var > 3 {
painter
.vault(
Aabb {
min: (room_pos - (radius / 8)).with_z(base - 79 + (18 * s)),
max: (room_pos + (radius / 8)).with_z(base - 59 + (18 * s)),
},
room_dir,
)
.clear();
}
}
}
}
let cyclops_pos_high = Vec2::new(center.x, center.y - 14).with_z(base - 40);
painter
.vault(
Aabb {
min: (cyclops_pos_high - (radius / 8)).with_z(base - 40),
max: (cyclops_pos_high + (radius / 8)).with_z(base - 25),
},
Dir::X,
)
.clear();
painter.spawn(EntityInfo::at(cyclops_pos_high.as_()).with_asset_expect(
"common.entity.dungeon.myrmidon.cyclops_key",
&mut thread_rng,
None,
));
let cyclops_pos_low = Vec2::new(center.x, center.y + 14).with_z(base - 79);
painter
.vault(
Aabb {
min: (cyclops_pos_low - (radius / 8)).with_z(base - 79),
max: (cyclops_pos_low + (radius / 8)).with_z(base - 64),
},
Dir::X,
)
.clear();
painter.spawn(EntityInfo::at(cyclops_pos_low.as_()).with_asset_expect(
"common.entity.dungeon.myrmidon.cyclops",
&mut thread_rng,
None,
));
let mob_positions_cellar = place_circular(center, 2.0, 3);
for npc_position in mob_positions_cellar {
let npc_pos = npc_position.with_z(base - 79);
painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
"common.entity.dungeon.myrmidon.marksman",
&mut thread_rng,
None,
));
}
}
}