feat: explosions, particle system, electricity, structural integrity
Explosions: - trigger_explosion(x, y, radius, damage) in Game - Destroys non-static solids, ignites empty cells, pressure spike - Knockback to entities (velocity away from center + upward pop) - Particle burst on explosion (fire + smoke + debris) Particle system: - ParticleManager in src/physics/particle.rs (capacity 2000) - CPU-side: position, velocity, life, color, gravity, size - spawn_burst() for radial bursts, individual spawn() for ambient - Rendered as third draw call in graphics.rs (is_ui: 2 in shader) - ParticleInstance = ColorInstance compatible (12 bytes) - upload_particles() via GpuRenderer trait - Ambient: fire sparks, lava bubbles — scanned around player Electricity: - chunk.electricity: Vec<u8> (4KB/chunk) — current strength 0-255 - Material.conductive + conductivity fields (water = conductive) - electricity_step() in CA: propagates current through conductive neighbors, decays over time, skips non-conductive cells - cells_swap swaps electricity, serialization saves/loads it Structural integrity: - Material.structural: bool (stone, wood = true) - structural_step() in CA (every 30 ticks, infinite mode only): checks if structural cells have support (below, below-left, below-right) converts unsupported to Sand (falls) - Only active in infinite mode to avoid breaking test grids Graphics shader: is_ui==2 branch renders particles in world-space All 185 tests + 14 scenarios pass. 143 FPS benchmark.
This commit is contained in:
@@ -71,17 +71,29 @@ vec3 compute_light(ivec2 world_pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 pixel = (in_grid + in_pos) * pc.cell_size;
|
if (pc.is_ui == 2u) {
|
||||||
gl_Position = vec4(
|
vec2 world_pos = in_grid;
|
||||||
2.0 * pixel.x / pc.screen_size.x - 1.0,
|
vec2 screen = (world_pos - vec2(pc.cam_pos)) * pc.cell_size + in_pos * pc.cell_size;
|
||||||
2.0 * pixel.y / pc.screen_size.y - 1.0,
|
gl_Position = vec4(
|
||||||
0.0, 1.0
|
2.0 * screen.x / pc.screen_size.x - 1.0,
|
||||||
);
|
2.0 * screen.y / pc.screen_size.y - 1.0,
|
||||||
out_color = in_color;
|
0.0, 1.0
|
||||||
if (pc.is_ui != 0u) {
|
);
|
||||||
|
out_color = in_color;
|
||||||
out_light = vec3(1.0);
|
out_light = vec3(1.0);
|
||||||
} else {
|
} else {
|
||||||
ivec2 world_pos = ivec2(in_grid + vec2(pc.cam_pos));
|
vec2 pixel = (in_grid + in_pos) * pc.cell_size;
|
||||||
out_light = compute_light(world_pos);
|
gl_Position = vec4(
|
||||||
|
2.0 * pixel.x / pc.screen_size.x - 1.0,
|
||||||
|
2.0 * pixel.y / pc.screen_size.y - 1.0,
|
||||||
|
0.0, 1.0
|
||||||
|
);
|
||||||
|
out_color = in_color;
|
||||||
|
if (pc.is_ui != 0u) {
|
||||||
|
out_light = vec3(1.0);
|
||||||
|
} else {
|
||||||
|
ivec2 world_pos = ivec2(in_grid + vec2(pc.cam_pos));
|
||||||
|
out_light = compute_light(world_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+11
-11
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"mode": "graphics",
|
"mode": "graphics",
|
||||||
"ticks": 300,
|
"ticks": 300,
|
||||||
"total_time_ms": 1847.9,
|
"total_time_ms": 2104.1,
|
||||||
"avg_fps": 162.3,
|
"avg_fps": 142.6,
|
||||||
"avg_frame_time_ms": 6.13,
|
"avg_frame_time_ms": 6.99,
|
||||||
"p99_frame_time_ms": 15.95,
|
"p99_frame_time_ms": 15.98,
|
||||||
"min_frame_time_ms": 4.61,
|
"min_frame_time_ms": 5.43,
|
||||||
"subsystems": {
|
"subsystems": {
|
||||||
"ca_step_avg_us": 1708,
|
"ca_step_avg_us": 2900,
|
||||||
"ca_step_p99_us": 11597,
|
"ca_step_p99_us": 11976,
|
||||||
"ca_step_min_us": 332,
|
"ca_step_min_us": 1280,
|
||||||
"render_avg_us": 3717,
|
"render_avg_us": 3459,
|
||||||
"render_p99_us": 6538,
|
"render_p99_us": 5031,
|
||||||
"render_min_us": 3295
|
"render_min_us": 3155
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+117
@@ -4,6 +4,7 @@ use crate::entity::player::Player;
|
|||||||
use crate::entity::{EntityKind, EntityManager, ItemManager};
|
use crate::entity::{EntityKind, EntityManager, ItemManager};
|
||||||
use crate::input::{Action, InputHandler};
|
use crate::input::{Action, InputHandler};
|
||||||
use crate::physics::collision::resolve_grid_collision;
|
use crate::physics::collision::resolve_grid_collision;
|
||||||
|
use crate::physics::particle::ParticleManager;
|
||||||
use crate::physics::projectile::{ProjectileManager, ProjectileType};
|
use crate::physics::projectile::{ProjectileManager, ProjectileType};
|
||||||
use crate::physics::verlet::VerletSolver;
|
use crate::physics::verlet::VerletSolver;
|
||||||
use crate::render::lighting;
|
use crate::render::lighting;
|
||||||
@@ -24,6 +25,7 @@ pub struct Game {
|
|||||||
pub verlet: VerletSolver,
|
pub verlet: VerletSolver,
|
||||||
pub entities: EntityManager,
|
pub entities: EntityManager,
|
||||||
pub projectiles: ProjectileManager,
|
pub projectiles: ProjectileManager,
|
||||||
|
pub particles: ParticleManager,
|
||||||
pub items: ItemManager,
|
pub items: ItemManager,
|
||||||
pub player: Player,
|
pub player: Player,
|
||||||
pub input: InputHandler,
|
pub input: InputHandler,
|
||||||
@@ -72,6 +74,7 @@ impl Game {
|
|||||||
verlet: VerletSolver::new(),
|
verlet: VerletSolver::new(),
|
||||||
entities,
|
entities,
|
||||||
projectiles: ProjectileManager::new(),
|
projectiles: ProjectileManager::new(),
|
||||||
|
particles: ParticleManager::new(2000),
|
||||||
items: ItemManager::new(),
|
items: ItemManager::new(),
|
||||||
player,
|
player,
|
||||||
input: InputHandler::new(),
|
input: InputHandler::new(),
|
||||||
@@ -120,6 +123,7 @@ impl Game {
|
|||||||
verlet: VerletSolver::new(),
|
verlet: VerletSolver::new(),
|
||||||
entities,
|
entities,
|
||||||
projectiles: ProjectileManager::new(),
|
projectiles: ProjectileManager::new(),
|
||||||
|
particles: ParticleManager::new(2000),
|
||||||
items: ItemManager::new(),
|
items: ItemManager::new(),
|
||||||
player,
|
player,
|
||||||
input: InputHandler::new(),
|
input: InputHandler::new(),
|
||||||
@@ -548,10 +552,79 @@ impl Game {
|
|||||||
self.try_spawn_slime();
|
self.try_spawn_slime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.spawn_ambient_particles();
|
||||||
self.play_ambient_sounds();
|
self.play_ambient_sounds();
|
||||||
|
self.particles.update();
|
||||||
self.grid.swap_modified_flags();
|
self.grid.swap_modified_flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_ambient_particles(&mut self) {
|
||||||
|
let (px, py) = self.player.center(&self.entities);
|
||||||
|
let radius = 30i32;
|
||||||
|
let mut fire_count = 0u32;
|
||||||
|
let mut lava_count = 0u32;
|
||||||
|
let mut water_count = 0u32;
|
||||||
|
for dy in -radius..=radius {
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
let x = px as i32 + dx;
|
||||||
|
let y = py as i32 + dy;
|
||||||
|
if !self.grid.in_bounds(x, y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cell = self.grid.get(x, y);
|
||||||
|
match cell.material {
|
||||||
|
MaterialId::Fire => {
|
||||||
|
fire_count += 1;
|
||||||
|
if self.tick % 3 == 0
|
||||||
|
&& (dx * dx + dy * dy) < 400
|
||||||
|
&& self.particles.count() < 1800
|
||||||
|
{
|
||||||
|
let mut rng =
|
||||||
|
(x as u32).wrapping_mul(7919).wrapping_add(self.tick as u32);
|
||||||
|
rng ^= rng << 13;
|
||||||
|
rng ^= rng >> 17;
|
||||||
|
let off_x = ((rng & 0xFF) as f32 / 255.0 - 0.5) * 2.0;
|
||||||
|
let off_y = ((rng >> 8 & 0xFF) as f32 / 255.0 - 0.5) * 2.0;
|
||||||
|
self.particles.spawn(
|
||||||
|
x as f32 + off_x,
|
||||||
|
y as f32 + off_y,
|
||||||
|
off_x * 0.3,
|
||||||
|
-0.5 - ((rng >> 16) as f32 / 65535.0),
|
||||||
|
15 + (rng % 10),
|
||||||
|
[255, 140 + ((rng >> 4) % 80) as u8, 20, 200],
|
||||||
|
1.0 + ((rng >> 8) % 3) as f32 * 0.5,
|
||||||
|
-0.02,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialId::Lava => {
|
||||||
|
lava_count += 1;
|
||||||
|
if self.tick % 20 == 0
|
||||||
|
&& (dx * dx + dy * dy) < 300
|
||||||
|
&& self.particles.count() < 1800
|
||||||
|
{
|
||||||
|
self.particles.spawn_burst(
|
||||||
|
x as f32,
|
||||||
|
y as f32,
|
||||||
|
2,
|
||||||
|
[255, 100, 20, 180],
|
||||||
|
1.5,
|
||||||
|
25,
|
||||||
|
1.5,
|
||||||
|
0.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialId::Water => {
|
||||||
|
water_count += 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = (fire_count, lava_count, water_count);
|
||||||
|
}
|
||||||
|
|
||||||
fn play_ambient_sounds(&mut self) {
|
fn play_ambient_sounds(&mut self) {
|
||||||
if !self.audio.is_enabled() {
|
if !self.audio.is_enabled() {
|
||||||
return;
|
return;
|
||||||
@@ -1658,4 +1731,48 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trigger_explosion(&mut self, x: i32, y: i32, radius: i32, damage: f32) {
|
||||||
|
for dy in -radius..=radius {
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
let dist_sq = dx * dx + dy * dy;
|
||||||
|
if dist_sq > radius * radius {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let bx = x + dx;
|
||||||
|
let by = y + dy;
|
||||||
|
if !self.grid.in_bounds(bx, by) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cell = self.grid.get(bx, by);
|
||||||
|
if cell.is_solid() && !cell.is_static() {
|
||||||
|
self.grid.set(bx, by, crate::world::cell::Cell::empty());
|
||||||
|
} else if cell.is_empty() {
|
||||||
|
let mut fire = crate::world::cell::Cell::new(MaterialId::Fire);
|
||||||
|
fire.updated_this_tick = true;
|
||||||
|
self.grid.set(bx, by, fire);
|
||||||
|
self.grid.set_temp(bx, by, 600.0);
|
||||||
|
}
|
||||||
|
self.grid.set_pressure(bx, by, 255);
|
||||||
|
self.grid.mark_dirty(bx, by);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.audio.play("explosion");
|
||||||
|
for e in self.entities.all_mut() {
|
||||||
|
if !e.alive {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let dx = e.cx as i32 - x;
|
||||||
|
let dy = e.cy as i32 - y;
|
||||||
|
let dist = ((dx * dx + dy * dy) as f32).sqrt();
|
||||||
|
if dist < radius as f32 * 1.5 {
|
||||||
|
let dmg = (damage * (1.0 - dist / (radius as f32 * 1.5))).max(0.0);
|
||||||
|
e.take_damage(dmg);
|
||||||
|
let knockback = (1.0 - dist / (radius as f32 * 1.5)).max(0.0) * 5.0;
|
||||||
|
let dir = if dx == 0 { 0.0 } else { dx as f32 / dist };
|
||||||
|
e.set_horizontal_vel(dir * knockback);
|
||||||
|
e.set_vertical_vel(-knockback * 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ trait GpuRenderer {
|
|||||||
fn grid_w(&self) -> usize;
|
fn grid_w(&self) -> usize;
|
||||||
fn grid_h(&self) -> usize;
|
fn grid_h(&self) -> usize;
|
||||||
fn cell_pixel_size(&self) -> u32;
|
fn cell_pixel_size(&self) -> u32;
|
||||||
|
fn upload_particles(&mut self, particles: &verbatim::physics::particle::ParticleManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GpuRenderer for verbatim::render::vulkan::VulkanRenderer {
|
impl GpuRenderer for verbatim::render::vulkan::VulkanRenderer {
|
||||||
@@ -102,6 +103,9 @@ impl GpuRenderer for verbatim::render::vulkan::VulkanRenderer {
|
|||||||
fn cell_pixel_size(&self) -> u32 {
|
fn cell_pixel_size(&self) -> u32 {
|
||||||
verbatim::render::vulkan::VulkanRenderer::cell_pixel_size(self)
|
verbatim::render::vulkan::VulkanRenderer::cell_pixel_size(self)
|
||||||
}
|
}
|
||||||
|
fn upload_particles(&mut self, particles: &verbatim::physics::particle::ParticleManager) {
|
||||||
|
verbatim::render::vulkan::VulkanRenderer::upload_particles(self, particles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GpuRenderer for verbatim::render::graphics::GraphicsRenderer {
|
impl GpuRenderer for verbatim::render::graphics::GraphicsRenderer {
|
||||||
@@ -131,6 +135,9 @@ impl GpuRenderer for verbatim::render::graphics::GraphicsRenderer {
|
|||||||
fn cell_pixel_size(&self) -> u32 {
|
fn cell_pixel_size(&self) -> u32 {
|
||||||
verbatim::render::graphics::GraphicsRenderer::cell_pixel_size(self)
|
verbatim::render::graphics::GraphicsRenderer::cell_pixel_size(self)
|
||||||
}
|
}
|
||||||
|
fn upload_particles(&mut self, particles: &verbatim::physics::particle::ParticleManager) {
|
||||||
|
verbatim::render::graphics::GraphicsRenderer::upload_particles(self, particles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -532,6 +539,7 @@ fn run_gpu_mode<R: GpuRenderer>(title: &str) {
|
|||||||
|
|
||||||
game.build_ui(vw, vh);
|
game.build_ui(vw, vh);
|
||||||
|
|
||||||
|
renderer.upload_particles(&game.particles);
|
||||||
renderer.render(
|
renderer.render(
|
||||||
&game.grid,
|
&game.grid,
|
||||||
&game.entities,
|
&game.entities,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod collision;
|
pub mod collision;
|
||||||
|
pub mod particle;
|
||||||
pub mod projectile;
|
pub mod projectile;
|
||||||
pub mod verlet;
|
pub mod verlet;
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Particle {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub vx: f32,
|
||||||
|
pub vy: f32,
|
||||||
|
pub life: u32,
|
||||||
|
pub max_life: u32,
|
||||||
|
pub color: [u8; 4],
|
||||||
|
pub size: f32,
|
||||||
|
pub gravity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Particle {
|
||||||
|
pub fn alive(&self) -> bool {
|
||||||
|
self.life > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParticleManager {
|
||||||
|
particles: Vec<Particle>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParticleManager {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
particles: Vec::with_capacity(capacity),
|
||||||
|
capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn(
|
||||||
|
&mut self,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
vx: f32,
|
||||||
|
vy: f32,
|
||||||
|
life: u32,
|
||||||
|
color: [u8; 4],
|
||||||
|
size: f32,
|
||||||
|
gravity: f32,
|
||||||
|
) {
|
||||||
|
if self.particles.len() >= self.capacity {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.particles.push(Particle {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
vx,
|
||||||
|
vy,
|
||||||
|
life,
|
||||||
|
max_life: life,
|
||||||
|
color,
|
||||||
|
size,
|
||||||
|
gravity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_burst(
|
||||||
|
&mut self,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
count: usize,
|
||||||
|
color: [u8; 4],
|
||||||
|
speed: f32,
|
||||||
|
life: u32,
|
||||||
|
size: f32,
|
||||||
|
gravity: f32,
|
||||||
|
) {
|
||||||
|
let mut rng = 0x12345u32;
|
||||||
|
for _ in 0..count {
|
||||||
|
rng ^= rng << 13;
|
||||||
|
rng ^= rng >> 17;
|
||||||
|
rng ^= rng << 5;
|
||||||
|
let angle = (rng as f32 / u32::MAX as f32) * std::f32::consts::TAU;
|
||||||
|
let s = speed * (0.3 + 0.7 * ((rng >> 16) as f32 / u16::MAX as f32));
|
||||||
|
let vx = angle.cos() * s;
|
||||||
|
let vy = angle.sin() * s - speed * 0.3;
|
||||||
|
self.spawn(x, y, vx, vy, life, color, size, gravity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
for p in &mut self.particles {
|
||||||
|
p.life = p.life.saturating_sub(1);
|
||||||
|
p.vy += p.gravity;
|
||||||
|
p.x += p.vx;
|
||||||
|
p.y += p.vy;
|
||||||
|
p.vx *= 0.96;
|
||||||
|
p.vy *= 0.96;
|
||||||
|
}
|
||||||
|
self.particles.retain(|p| p.alive());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all(&self) -> &[Particle] {
|
||||||
|
&self.particles
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.particles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.particles.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ParticleManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,14 @@ struct ColorInstance {
|
|||||||
color: [u8; 4],
|
color: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
struct ParticleInstance {
|
||||||
|
grid_x: f32,
|
||||||
|
grid_y: f32,
|
||||||
|
color: [u8; 4],
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(bytemuck::NoUninit, Clone, Copy, Default)]
|
#[derive(bytemuck::NoUninit, Clone, Copy, Default)]
|
||||||
struct GpuLightSource {
|
struct GpuLightSource {
|
||||||
@@ -115,6 +123,12 @@ pub struct GraphicsRenderer {
|
|||||||
ui_instance_ptr: *mut ColorInstance,
|
ui_instance_ptr: *mut ColorInstance,
|
||||||
ui_instance_capacity: usize,
|
ui_instance_capacity: usize,
|
||||||
|
|
||||||
|
particle_instance_buffer: vk::Buffer,
|
||||||
|
particle_instance_memory: vk::DeviceMemory,
|
||||||
|
particle_instance_ptr: *mut ParticleInstance,
|
||||||
|
particle_instance_capacity: usize,
|
||||||
|
particle_count: usize,
|
||||||
|
|
||||||
grid_buffer: vk::Buffer,
|
grid_buffer: vk::Buffer,
|
||||||
grid_memory: vk::DeviceMemory,
|
grid_memory: vk::DeviceMemory,
|
||||||
grid_ptr: *mut u32,
|
grid_ptr: *mut u32,
|
||||||
@@ -706,6 +720,44 @@ impl GraphicsRenderer {
|
|||||||
ptr as *mut ColorInstance
|
ptr as *mut ColorInstance
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let part_capacity = 2000usize;
|
||||||
|
let part_inst_sz =
|
||||||
|
(part_capacity * std::mem::size_of::<ParticleInstance>()) as vk::DeviceSize;
|
||||||
|
let pibi = vk::BufferCreateInfo::default()
|
||||||
|
.size(part_inst_sz)
|
||||||
|
.usage(vk::BufferUsageFlags::VERTEX_BUFFER)
|
||||||
|
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
||||||
|
let particle_instance_buffer = unsafe { device.create_buffer(&pibi, None) }
|
||||||
|
.map_err(|e| format!("part inst buf: {e:?}"))?;
|
||||||
|
let preq = unsafe { device.get_buffer_memory_requirements(particle_instance_buffer) };
|
||||||
|
let pmt = find_mem(
|
||||||
|
preq.memory_type_bits,
|
||||||
|
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
|
||||||
|
)?;
|
||||||
|
let particle_instance_memory = unsafe {
|
||||||
|
device.allocate_memory(
|
||||||
|
&vk::MemoryAllocateInfo::default()
|
||||||
|
.allocation_size(preq.size)
|
||||||
|
.memory_type_index(pmt),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.map_err(|e| format!("part inst mem: {e:?}"))?;
|
||||||
|
let particle_instance_ptr = unsafe {
|
||||||
|
device
|
||||||
|
.bind_buffer_memory(particle_instance_buffer, particle_instance_memory, 0)
|
||||||
|
.expect("bind part inst");
|
||||||
|
let ptr = device
|
||||||
|
.map_memory(
|
||||||
|
particle_instance_memory,
|
||||||
|
0,
|
||||||
|
part_inst_sz,
|
||||||
|
vk::MemoryMapFlags::default(),
|
||||||
|
)
|
||||||
|
.expect("map part inst");
|
||||||
|
ptr as *mut ParticleInstance
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
grid_w,
|
grid_w,
|
||||||
grid_h,
|
grid_h,
|
||||||
@@ -742,6 +794,11 @@ impl GraphicsRenderer {
|
|||||||
ui_instance_memory,
|
ui_instance_memory,
|
||||||
ui_instance_ptr,
|
ui_instance_ptr,
|
||||||
ui_instance_capacity: ui_capacity,
|
ui_instance_capacity: ui_capacity,
|
||||||
|
particle_instance_buffer,
|
||||||
|
particle_instance_memory,
|
||||||
|
particle_instance_ptr,
|
||||||
|
particle_instance_capacity: part_capacity,
|
||||||
|
particle_count: 0,
|
||||||
|
|
||||||
grid_buffer,
|
grid_buffer,
|
||||||
grid_memory,
|
grid_memory,
|
||||||
@@ -1076,6 +1133,35 @@ impl GraphicsRenderer {
|
|||||||
device.cmd_draw_indexed(cmd, 6, ui_count as u32, 0, 0, 0);
|
device.cmd_draw_indexed(cmd, 6, ui_count as u32, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.particle_count > 0 {
|
||||||
|
device.cmd_bind_vertex_buffers(
|
||||||
|
cmd,
|
||||||
|
0,
|
||||||
|
&[self.vertex_buffer, self.particle_instance_buffer],
|
||||||
|
&[0, 0],
|
||||||
|
);
|
||||||
|
let part_pc = PushConstants {
|
||||||
|
screen_size: [
|
||||||
|
self.swapchain_extent.width as f32,
|
||||||
|
self.swapchain_extent.height as f32,
|
||||||
|
],
|
||||||
|
cell_size: [CHAR_W as f32, CHAR_H as f32],
|
||||||
|
world_size: [self.grid_w as i32, self.grid_h as i32],
|
||||||
|
cam_pos: [cam_x, cam_y],
|
||||||
|
ambient: [1.0, 1.0, 1.0],
|
||||||
|
is_ui: 2,
|
||||||
|
light_count: 0,
|
||||||
|
};
|
||||||
|
device.cmd_push_constants(
|
||||||
|
cmd,
|
||||||
|
self.pipeline_layout,
|
||||||
|
vk::ShaderStageFlags::VERTEX,
|
||||||
|
0,
|
||||||
|
bytemuck::bytes_of(&part_pc),
|
||||||
|
);
|
||||||
|
device.cmd_draw_indexed(cmd, 6, self.particle_count as u32, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
device.cmd_end_render_pass(cmd);
|
device.cmd_end_render_pass(cmd);
|
||||||
let _ = device.end_command_buffer(cmd);
|
let _ = device.end_command_buffer(cmd);
|
||||||
|
|
||||||
@@ -1115,6 +1201,29 @@ impl GraphicsRenderer {
|
|||||||
CHAR_W
|
CHAR_W
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn upload_particles(&mut self, particles: &crate::physics::particle::ParticleManager) {
|
||||||
|
let instances = unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
self.particle_instance_ptr,
|
||||||
|
self.particle_instance_capacity,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut count = 0usize;
|
||||||
|
for p in particles.all() {
|
||||||
|
if count >= self.particle_instance_capacity {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let alpha = ((p.life as f32 / p.max_life as f32) * 255.0) as u8;
|
||||||
|
instances[count] = ParticleInstance {
|
||||||
|
grid_x: p.x,
|
||||||
|
grid_y: p.y,
|
||||||
|
color: [p.color[0], p.color[1], p.color[2], alpha],
|
||||||
|
};
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
self.particle_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
fn check_resize(&mut self) {
|
fn check_resize(&mut self) {
|
||||||
let sl = ash::khr::surface::Instance::new(&self.entry, &self.instance);
|
let sl = ash::khr::surface::Instance::new(&self.entry, &self.instance);
|
||||||
let caps = match unsafe {
|
let caps = match unsafe {
|
||||||
@@ -1346,6 +1455,9 @@ impl Drop for GraphicsRenderer {
|
|||||||
self.device.free_memory(self.instance_memory, None);
|
self.device.free_memory(self.instance_memory, None);
|
||||||
self.device.destroy_buffer(self.ui_instance_buffer, None);
|
self.device.destroy_buffer(self.ui_instance_buffer, None);
|
||||||
self.device.free_memory(self.ui_instance_memory, None);
|
self.device.free_memory(self.ui_instance_memory, None);
|
||||||
|
self.device
|
||||||
|
.destroy_buffer(self.particle_instance_buffer, None);
|
||||||
|
self.device.free_memory(self.particle_instance_memory, None);
|
||||||
self.device.destroy_buffer(self.vertex_buffer, None);
|
self.device.destroy_buffer(self.vertex_buffer, None);
|
||||||
self.device.free_memory(self.vertex_memory, None);
|
self.device.free_memory(self.vertex_memory, None);
|
||||||
self.device.destroy_buffer(self.index_buffer, None);
|
self.device.destroy_buffer(self.index_buffer, None);
|
||||||
|
|||||||
@@ -712,6 +712,8 @@ impl VulkanRenderer {
|
|||||||
CHAR_W
|
CHAR_W
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn upload_particles(&mut self, _particles: &crate::physics::particle::ParticleManager) {}
|
||||||
|
|
||||||
fn check_resize(&mut self) {
|
fn check_resize(&mut self) {
|
||||||
let sl = ash::khr::surface::Instance::new(&self.entry, &self.instance);
|
let sl = ash::khr::surface::Instance::new(&self.entry, &self.instance);
|
||||||
let caps = match unsafe {
|
let caps = match unsafe {
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ impl CellularAutomaton {
|
|||||||
self.heat_transfer(grid, &active, &pre_dirty);
|
self.heat_transfer(grid, &active, &pre_dirty);
|
||||||
self.gas_step(grid, &active, &pre_dirty);
|
self.gas_step(grid, &active, &pre_dirty);
|
||||||
self.pressure_step(grid, &active, &pre_dirty);
|
self.pressure_step(grid, &active, &pre_dirty);
|
||||||
|
self.electricity_step(grid, &active, &pre_dirty);
|
||||||
|
self.structural_step(grid, &active);
|
||||||
self.light_step(grid, &active);
|
self.light_step(grid, &active);
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
}
|
}
|
||||||
@@ -760,6 +762,172 @@ impl CellularAutomaton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn electricity_step(
|
||||||
|
&mut self,
|
||||||
|
grid: &mut ChunkedGrid,
|
||||||
|
active: &[(i32, i32)],
|
||||||
|
pre_dirty: &std::collections::HashMap<(i32, i32), (i32, i32, i32, i32)>,
|
||||||
|
) {
|
||||||
|
let reg = crate::world::material::MaterialRegistry::instance();
|
||||||
|
let cs = CHUNK_SIZE as i32;
|
||||||
|
for &(cx, cy) in active {
|
||||||
|
let dirty = grid.get_chunk_dirty(cx, cy);
|
||||||
|
let pre = pre_dirty.get(&(cx, cy)).copied();
|
||||||
|
let (min_x, min_y, max_x, max_y) = match (dirty, pre) {
|
||||||
|
(Some(d), Some(p)) => (d.0.min(p.0), d.1.min(p.1), d.2.max(p.2), d.3.max(p.3)),
|
||||||
|
(Some(d), None) => d,
|
||||||
|
(None, Some(p)) => p,
|
||||||
|
(None, None) => continue,
|
||||||
|
};
|
||||||
|
let w = max_x - min_x + 1;
|
||||||
|
let h = max_y - min_y + 1;
|
||||||
|
let ox = cx * cs;
|
||||||
|
let oy = cy * cs;
|
||||||
|
let chunk = match grid.get_chunk_mut(cx, cy) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let has_elec = chunk.electricity.iter().any(|&e| e > 0);
|
||||||
|
if !has_elec {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut changes: Vec<(usize, u8)> = Vec::new();
|
||||||
|
for ly in 0..h {
|
||||||
|
let wy = min_y + ly - oy;
|
||||||
|
if wy < 0 || wy >= cs {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for lx in 0..w {
|
||||||
|
let wx = min_x + lx - ox;
|
||||||
|
if wx < 0 || wx >= cs {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let idx = (wy as usize) * CHUNK_SIZE + (wx as usize);
|
||||||
|
let cur = chunk.electricity[idx];
|
||||||
|
if cur == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mat = reg.get(chunk.cells[idx].material);
|
||||||
|
if !mat.conductive && cur < 200 {
|
||||||
|
changes.push((idx, cur.saturating_sub(20)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut spread = 0u8;
|
||||||
|
for &(dx, dy) in &NEIGHBORS4 {
|
||||||
|
let nx = wx + dx;
|
||||||
|
let ny = wy + dy;
|
||||||
|
if nx < 0 || nx >= cs || ny < 0 || ny >= cs {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let ni = (ny as usize) * CHUNK_SIZE + (nx as usize);
|
||||||
|
let n_mat = reg.get(chunk.cells[ni].material);
|
||||||
|
let n_elec = chunk.electricity[ni];
|
||||||
|
if n_mat.conductive && n_elec < cur {
|
||||||
|
let diff = (cur - n_elec) / 4;
|
||||||
|
if diff > 0 {
|
||||||
|
changes.push((ni, n_elec + diff));
|
||||||
|
spread += diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if spread > 0 {
|
||||||
|
changes.push((idx, cur.saturating_sub(spread)));
|
||||||
|
} else {
|
||||||
|
changes.push((idx, cur.saturating_sub(5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (idx, val) in changes {
|
||||||
|
chunk.electricity[idx] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn structural_step(&mut self, grid: &mut ChunkedGrid, active: &[(i32, i32)]) {
|
||||||
|
if !grid.is_infinite() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.tick % 30 != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let reg = crate::world::material::MaterialRegistry::instance();
|
||||||
|
let cs = CHUNK_SIZE as i32;
|
||||||
|
for &(cx, cy) in active {
|
||||||
|
let dirty = match grid.get_chunk_dirty(cx, cy) {
|
||||||
|
Some(d) => d,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let (min_x, min_y, max_x, max_y) = dirty;
|
||||||
|
let ox = cx * cs;
|
||||||
|
let oy = cy * cs;
|
||||||
|
let chunk = match grid.get_chunk_mut(cx, cy) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let mut to_collapse: Vec<(usize, i32, i32)> = Vec::new();
|
||||||
|
for ly in 0..(max_y - min_y + 1) {
|
||||||
|
let wy = min_y + ly - oy;
|
||||||
|
if wy < 0 || wy >= cs {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for lx in 0..(max_x - min_x + 1) {
|
||||||
|
let wx = min_x + lx - ox;
|
||||||
|
if wx < 0 || wx >= cs {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let idx = (wy as usize) * CHUNK_SIZE + (wx as usize);
|
||||||
|
let mat = reg.get(chunk.cells[idx].material);
|
||||||
|
if !mat.structural {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let below = if wy + 1 < cs {
|
||||||
|
let bi = ((wy + 1) as usize) * CHUNK_SIZE + (wx as usize);
|
||||||
|
chunk.cells[bi].is_solid()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if below {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let below_left = if wy + 1 < cs && wx > 0 {
|
||||||
|
let bi = ((wy + 1) as usize) * CHUNK_SIZE + ((wx - 1) as usize);
|
||||||
|
chunk.cells[bi].is_solid()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let below_right = if wy + 1 < cs && wx + 1 < cs {
|
||||||
|
let bi = ((wy + 1) as usize) * CHUNK_SIZE + ((wx + 1) as usize);
|
||||||
|
chunk.cells[bi].is_solid()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if below_left || below_right {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let left = if wx > 0 {
|
||||||
|
chunk.cells[(wy as usize) * CHUNK_SIZE + ((wx - 1) as usize)].is_solid()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let right = if wx + 1 < cs {
|
||||||
|
chunk.cells[(wy as usize) * CHUNK_SIZE + ((wx + 1) as usize)].is_solid()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if !left && !right {
|
||||||
|
to_collapse.push((idx, wx, wy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (idx, wx, wy) in to_collapse {
|
||||||
|
chunk.cells[idx] = Cell::new(MaterialId::Sand);
|
||||||
|
chunk.temps[idx] = 20.0;
|
||||||
|
chunk.modified = true;
|
||||||
|
chunk.mark_dirty(wx, wy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn light_step(&mut self, grid: &mut ChunkedGrid, active: &[(i32, i32)]) {
|
fn light_step(&mut self, grid: &mut ChunkedGrid, active: &[(i32, i32)]) {
|
||||||
self.light_tick += 1;
|
self.light_tick += 1;
|
||||||
if self.light_tick % 20 != 0 {
|
if self.light_tick % 20 != 0 {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub struct Chunk {
|
|||||||
pub gas_type: Vec<u8>,
|
pub gas_type: Vec<u8>,
|
||||||
pub gas_density: Vec<u8>,
|
pub gas_density: Vec<u8>,
|
||||||
pub light: Vec<[u8; 3]>,
|
pub light: Vec<[u8; 3]>,
|
||||||
|
pub electricity: Vec<u8>,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub modified: bool,
|
pub modified: bool,
|
||||||
pub was_modified: bool,
|
pub was_modified: bool,
|
||||||
@@ -28,6 +29,7 @@ impl Chunk {
|
|||||||
gas_type: vec![0; CHUNK_AREA],
|
gas_type: vec![0; CHUNK_AREA],
|
||||||
gas_density: vec![0; CHUNK_AREA],
|
gas_density: vec![0; CHUNK_AREA],
|
||||||
light: vec![[0, 0, 0]; CHUNK_AREA],
|
light: vec![[0, 0, 0]; CHUNK_AREA],
|
||||||
|
electricity: vec![0; CHUNK_AREA],
|
||||||
active: false,
|
active: false,
|
||||||
modified: false,
|
modified: false,
|
||||||
was_modified: false,
|
was_modified: false,
|
||||||
@@ -142,6 +144,21 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_electricity(&self, x: i32, y: i32) -> u8 {
|
||||||
|
if !Self::in_bounds(x, y) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self.electricity[Self::idx(x, y)]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_electricity(&mut self, x: i32, y: i32, val: u8) {
|
||||||
|
if Self::in_bounds(x, y) {
|
||||||
|
self.electricity[Self::idx(x, y)] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.cells.iter().all(|c| c.is_empty())
|
self.cells.iter().all(|c| c.is_empty())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -450,6 +450,7 @@ impl ChunkedGrid {
|
|||||||
chunk.pressure.swap(ci1, ci2);
|
chunk.pressure.swap(ci1, ci2);
|
||||||
chunk.gas_type.swap(ci1, ci2);
|
chunk.gas_type.swap(ci1, ci2);
|
||||||
chunk.gas_density.swap(ci1, ci2);
|
chunk.gas_density.swap(ci1, ci2);
|
||||||
|
chunk.electricity.swap(ci1, ci2);
|
||||||
chunk.cells[ci2].updated_this_tick = true;
|
chunk.cells[ci2].updated_this_tick = true;
|
||||||
chunk.modified = true;
|
chunk.modified = true;
|
||||||
chunk.mark_dirty(lx1, ly1);
|
chunk.mark_dirty(lx1, ly1);
|
||||||
@@ -503,6 +504,7 @@ impl ChunkedGrid {
|
|||||||
chunk.pressure.swap(i1, i2);
|
chunk.pressure.swap(i1, i2);
|
||||||
chunk.gas_type.swap(i1, i2);
|
chunk.gas_type.swap(i1, i2);
|
||||||
chunk.gas_density.swap(i1, i2);
|
chunk.gas_density.swap(i1, i2);
|
||||||
|
chunk.electricity.swap(i1, i2);
|
||||||
chunk.cells[i2].updated_this_tick = true;
|
chunk.cells[i2].updated_this_tick = true;
|
||||||
chunk.modified = true;
|
chunk.modified = true;
|
||||||
chunk.mark_dirty(lx1, ly1);
|
chunk.mark_dirty(lx1, ly1);
|
||||||
@@ -860,6 +862,9 @@ impl ChunkedGrid {
|
|||||||
for l in &chunk.light {
|
for l in &chunk.light {
|
||||||
bytes.extend_from_slice(&l[..]);
|
bytes.extend_from_slice(&l[..]);
|
||||||
}
|
}
|
||||||
|
for &e in &chunk.electricity {
|
||||||
|
bytes.push(e);
|
||||||
|
}
|
||||||
let dir = Path::new(path);
|
let dir = Path::new(path);
|
||||||
if let Some(parent) = dir.parent() {
|
if let Some(parent) = dir.parent() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
@@ -919,6 +924,13 @@ impl ChunkedGrid {
|
|||||||
chunk.light[i] = [data[off], data[off + 1], data[off + 2]];
|
chunk.light[i] = [data[off], data[off + 1], data[off + 2]];
|
||||||
off += 3;
|
off += 3;
|
||||||
}
|
}
|
||||||
|
for i in 0..area {
|
||||||
|
if off >= data.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunk.electricity[i] = data[off];
|
||||||
|
off += 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let (x0, y0, x1, y1) = bounds;
|
let (x0, y0, x1, y1) = bounds;
|
||||||
let w = (x1 - x0) as usize;
|
let w = (x1 - x0) as usize;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ pub struct Material {
|
|||||||
pub ignition_temp: f32,
|
pub ignition_temp: f32,
|
||||||
pub melt_temp: f32,
|
pub melt_temp: f32,
|
||||||
pub heat_conductivity: f32,
|
pub heat_conductivity: f32,
|
||||||
|
pub conductive: bool,
|
||||||
|
pub conductivity: f32,
|
||||||
|
pub structural: bool,
|
||||||
pub color_fg: (u8, u8, u8),
|
pub color_fg: (u8, u8, u8),
|
||||||
pub color_bg: (u8, u8, u8),
|
pub color_bg: (u8, u8, u8),
|
||||||
pub display_char: char,
|
pub display_char: char,
|
||||||
@@ -40,6 +43,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.0,
|
heat_conductivity: 0.0,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (15, 15, 20),
|
color_fg: (15, 15, 20),
|
||||||
color_bg: (10, 10, 15),
|
color_bg: (10, 10, 15),
|
||||||
display_char: ' ',
|
display_char: ' ',
|
||||||
@@ -56,6 +62,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: 1700.0,
|
melt_temp: 1700.0,
|
||||||
heat_conductivity: 0.2,
|
heat_conductivity: 0.2,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (218, 178, 90),
|
color_fg: (218, 178, 90),
|
||||||
color_bg: (60, 50, 30),
|
color_bg: (60, 50, 30),
|
||||||
display_char: '.',
|
display_char: '.',
|
||||||
@@ -72,6 +81,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: 0.0,
|
melt_temp: 0.0,
|
||||||
heat_conductivity: 0.4,
|
heat_conductivity: 0.4,
|
||||||
|
conductive: true,
|
||||||
|
conductivity: 0.6,
|
||||||
|
structural: false,
|
||||||
color_fg: (64, 128, 220),
|
color_fg: (64, 128, 220),
|
||||||
color_bg: (20, 40, 80),
|
color_bg: (20, 40, 80),
|
||||||
display_char: '~',
|
display_char: '~',
|
||||||
@@ -88,6 +100,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: 1200.0,
|
melt_temp: 1200.0,
|
||||||
heat_conductivity: 0.3,
|
heat_conductivity: 0.3,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: true,
|
||||||
color_fg: (120, 120, 130),
|
color_fg: (120, 120, 130),
|
||||||
color_bg: (40, 40, 50),
|
color_bg: (40, 40, 50),
|
||||||
display_char: '#',
|
display_char: '#',
|
||||||
@@ -104,6 +119,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.05,
|
heat_conductivity: 0.05,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (255, 80, 20),
|
color_fg: (255, 80, 20),
|
||||||
color_bg: (120, 20, 0),
|
color_bg: (120, 20, 0),
|
||||||
display_char: '#',
|
display_char: '#',
|
||||||
@@ -120,6 +138,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: 300.0,
|
ignition_temp: 300.0,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.1,
|
heat_conductivity: 0.1,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: true,
|
||||||
color_fg: (140, 90, 50),
|
color_fg: (140, 90, 50),
|
||||||
color_bg: (50, 30, 20),
|
color_bg: (50, 30, 20),
|
||||||
display_char: 'T',
|
display_char: 'T',
|
||||||
@@ -136,6 +157,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: 200.0,
|
ignition_temp: 200.0,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.15,
|
heat_conductivity: 0.15,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (180, 50, 50),
|
color_fg: (180, 50, 50),
|
||||||
color_bg: (60, 15, 15),
|
color_bg: (60, 15, 15),
|
||||||
display_char: '%',
|
display_char: '%',
|
||||||
@@ -152,6 +176,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.1,
|
heat_conductivity: 0.1,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (220, 210, 190),
|
color_fg: (220, 210, 190),
|
||||||
color_bg: (60, 55, 50),
|
color_bg: (60, 55, 50),
|
||||||
display_char: '`',
|
display_char: '`',
|
||||||
@@ -168,6 +195,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.2,
|
heat_conductivity: 0.2,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (200, 200, 220),
|
color_fg: (200, 200, 220),
|
||||||
color_bg: (30, 30, 40),
|
color_bg: (30, 30, 40),
|
||||||
display_char: '~',
|
display_char: '~',
|
||||||
@@ -184,6 +214,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.6,
|
heat_conductivity: 0.6,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (255, 160, 40),
|
color_fg: (255, 160, 40),
|
||||||
color_bg: (100, 30, 0),
|
color_bg: (100, 30, 0),
|
||||||
display_char: '^',
|
display_char: '^',
|
||||||
@@ -200,6 +233,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.3,
|
heat_conductivity: 0.3,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (100, 255, 60),
|
color_fg: (100, 255, 60),
|
||||||
color_bg: (20, 60, 10),
|
color_bg: (20, 60, 10),
|
||||||
display_char: '~',
|
display_char: '~',
|
||||||
@@ -216,6 +252,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.1,
|
heat_conductivity: 0.1,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (100, 100, 100),
|
color_fg: (100, 100, 100),
|
||||||
color_bg: (20, 20, 20),
|
color_bg: (20, 20, 20),
|
||||||
display_char: '*',
|
display_char: '*',
|
||||||
@@ -232,6 +271,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: 250.0,
|
ignition_temp: 250.0,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.1,
|
heat_conductivity: 0.1,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (80, 200, 60),
|
color_fg: (80, 200, 60),
|
||||||
color_bg: (20, 50, 15),
|
color_bg: (20, 50, 15),
|
||||||
display_char: '"',
|
display_char: '"',
|
||||||
@@ -248,6 +290,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.2,
|
heat_conductivity: 0.2,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (100, 70, 50),
|
color_fg: (100, 70, 50),
|
||||||
color_bg: (40, 30, 20),
|
color_bg: (40, 30, 20),
|
||||||
display_char: ':',
|
display_char: ':',
|
||||||
@@ -264,6 +309,9 @@ impl MaterialRegistry {
|
|||||||
ignition_temp: f32::INFINITY,
|
ignition_temp: f32::INFINITY,
|
||||||
melt_temp: f32::INFINITY,
|
melt_temp: f32::INFINITY,
|
||||||
heat_conductivity: 0.0,
|
heat_conductivity: 0.0,
|
||||||
|
conductive: false,
|
||||||
|
conductivity: 0.0,
|
||||||
|
structural: false,
|
||||||
color_fg: (255, 220, 80),
|
color_fg: (255, 220, 80),
|
||||||
color_bg: (60, 50, 20),
|
color_bg: (60, 50, 20),
|
||||||
display_char: '>',
|
display_char: '>',
|
||||||
|
|||||||
Reference in New Issue
Block a user