2022-12-30 20:13:35 +00:00
|
|
|
mod world;
|
2022-12-30 20:30:12 +00:00
|
|
|
mod state;
|
2022-12-31 07:54:18 +00:00
|
|
|
mod events;
|
|
|
|
mod time;
|
|
|
|
mod generators;
|
2023-01-04 13:54:57 +00:00
|
|
|
mod creature;
|
2023-01-04 16:52:58 +00:00
|
|
|
mod site;
|
2023-01-04 18:41:31 +00:00
|
|
|
mod ui;
|
2022-12-31 14:22:44 +00:00
|
|
|
mod entity;
|
2023-01-01 20:11:27 +00:00
|
|
|
mod game;
|
2023-01-05 19:14:27 +00:00
|
|
|
mod item;
|
2022-12-30 20:13:35 +00:00
|
|
|
|
2023-01-04 18:41:31 +00:00
|
|
|
use ui::App;
|
2022-12-31 07:54:18 +00:00
|
|
|
use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias};
|
2022-12-30 20:07:36 +00:00
|
|
|
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
|
|
|
use image::{RgbImage, Rgb};
|
|
|
|
use rand::prelude::*;
|
2022-12-31 07:54:18 +00:00
|
|
|
use world::World;
|
|
|
|
|
2023-01-04 13:54:57 +00:00
|
|
|
use std::{io};
|
2022-12-31 10:44:42 +00:00
|
|
|
use tui::{
|
|
|
|
backend::CrosstermBackend,
|
|
|
|
Terminal
|
|
|
|
};
|
|
|
|
use crossterm::{
|
2023-01-04 13:54:57 +00:00
|
|
|
event::{DisableMouseCapture, EnableMouseCapture},
|
2022-12-31 10:44:42 +00:00
|
|
|
execute,
|
|
|
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
|
|
|
};
|
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
|
|
|
|
const N_TOWNS: usize = 10;
|
|
|
|
const WORLD_SIZE: usize = 256;
|
2022-12-30 20:07:36 +00:00
|
|
|
|
|
|
|
struct BorderNoise {
|
|
|
|
pub center: [f64; 2],
|
|
|
|
pub radius: f64,
|
|
|
|
pub falloff: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BorderNoise {
|
|
|
|
pub fn new(center: [f64; 2], radius: f64, falloff: f64) -> BorderNoise {
|
|
|
|
BorderNoise {
|
|
|
|
center: center,
|
|
|
|
radius: radius,
|
|
|
|
falloff: falloff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NoiseFn<f64, 2> for BorderNoise {
|
|
|
|
fn get(&self, _point: [f64; 2]) -> f64 {
|
|
|
|
let dist = (
|
|
|
|
(self.center[0] - _point[0]).powi(2) +
|
|
|
|
(self.center[1] - _point[1]).powi(2)
|
|
|
|
).sqrt();
|
|
|
|
if dist > self.radius {
|
|
|
|
1.0 - ((dist - self.radius) * self.falloff)
|
|
|
|
} else {
|
|
|
|
1.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
fn build_world() -> World {
|
2022-12-30 20:07:36 +00:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
let map_center = WORLD_SIZE / 2;
|
2022-12-30 20:07:36 +00:00
|
|
|
let height = Add::new(
|
|
|
|
ScaleBias::new(
|
|
|
|
Add::new(
|
|
|
|
ScalePoint::new(Perlin::new(rng.gen::<u32>()))
|
|
|
|
.set_x_scale(0.04)
|
|
|
|
.set_y_scale(0.04),
|
|
|
|
ScalePoint::new(Perlin::new(rng.gen::<u32>()))
|
|
|
|
.set_x_scale(0.011)
|
|
|
|
.set_y_scale(0.011),
|
|
|
|
),
|
|
|
|
).set_scale(2000.0).set_bias(1000.0),
|
|
|
|
BorderNoise::new(
|
|
|
|
[map_center as f64, map_center as f64],
|
2022-12-31 07:54:18 +00:00
|
|
|
WORLD_SIZE as f64 * 0.4,
|
2022-12-30 20:07:36 +00:00
|
|
|
100.0
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let plane = PlaneMapBuilder::<_, 2>::new(&height)
|
2022-12-31 07:54:18 +00:00
|
|
|
.set_size(WORLD_SIZE, WORLD_SIZE)
|
|
|
|
.set_x_bounds(0.0, WORLD_SIZE as f64)
|
|
|
|
.set_y_bounds(0.0, WORLD_SIZE as f64)
|
2022-12-30 20:07:36 +00:00
|
|
|
.build();
|
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
let mut img = RgbImage::new(WORLD_SIZE as u32, WORLD_SIZE as u32);
|
2022-12-30 20:07:36 +00:00
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
let mut world = world::World::new(WORLD_SIZE);
|
2022-12-30 20:07:36 +00:00
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
for x in 0..WORLD_SIZE {
|
|
|
|
for y in 0..WORLD_SIZE {
|
2022-12-30 20:07:36 +00:00
|
|
|
let h = plane.get_value(x, y);
|
2022-12-30 20:13:35 +00:00
|
|
|
let t = world::Terrain::from_height(h);
|
2022-12-30 20:30:12 +00:00
|
|
|
world.map[x][y] = world::WorldCell::new(t);
|
2022-12-30 20:13:35 +00:00
|
|
|
img.put_pixel(x as u32, y as u32, Rgb(t.to_color()));
|
2022-12-30 20:07:36 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-30 20:30:12 +00:00
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
world
|
|
|
|
}
|
|
|
|
|
2022-12-31 10:44:42 +00:00
|
|
|
fn main() -> Result<(), io::Error> {
|
2023-01-01 20:11:27 +00:00
|
|
|
// build game state
|
2022-12-31 10:44:42 +00:00
|
|
|
let mut world_state = state::GameState::new(build_world());
|
2023-01-01 20:11:27 +00:00
|
|
|
world_state.time = world_state.time.add_years(100);
|
2022-12-31 07:54:18 +00:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
for _ in 0..N_TOWNS {
|
2023-01-01 20:11:27 +00:00
|
|
|
world_state.time = world_state.time.add_days(rng.gen_range(0..360*3));
|
2022-12-31 10:44:42 +00:00
|
|
|
world_state.found_town();
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
2022-12-31 14:22:44 +00:00
|
|
|
world_state.build_tavern();
|
2023-01-01 20:11:27 +00:00
|
|
|
// insert state into game
|
|
|
|
let game = game::Game::new(world_state);
|
2022-12-30 20:07:36 +00:00
|
|
|
|
2022-12-31 10:44:42 +00:00
|
|
|
|
|
|
|
// setup terminal
|
|
|
|
enable_raw_mode()?;
|
|
|
|
let mut stdout = io::stdout();
|
|
|
|
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
|
|
|
let backend = CrosstermBackend::new(stdout);
|
|
|
|
let mut terminal = Terminal::new(backend)?;
|
|
|
|
|
2023-01-01 20:11:27 +00:00
|
|
|
let mut app = App::new(game);
|
2022-12-31 10:44:42 +00:00
|
|
|
loop {
|
|
|
|
terminal.draw(|f| app.draw(f))?;
|
|
|
|
if !app.step() {
|
|
|
|
break;
|
|
|
|
}
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
2022-12-31 10:44:42 +00:00
|
|
|
|
|
|
|
// restore terminal
|
|
|
|
disable_raw_mode()?;
|
|
|
|
execute!(
|
|
|
|
terminal.backend_mut(),
|
|
|
|
LeaveAlternateScreen,
|
|
|
|
DisableMouseCapture
|
|
|
|
)?;
|
|
|
|
terminal.show_cursor()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
2022-12-30 20:07:36 +00:00
|
|
|
}
|