mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
add weather_zone command
This commit is contained in:
parent
cb969e5b87
commit
27ec6d7469
@ -125,6 +125,13 @@ lazy_static! {
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
static ref WEATHERS: Vec<String> = vec![
|
||||
"clear", "cloudy", "rain", "wind", "storm"
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
pub static ref BUFF_PARSER: HashMap<String, BuffKind> = {
|
||||
let string_from_buff = |kind| match kind {
|
||||
BuffKind::Burning => "burning",
|
||||
@ -297,6 +304,7 @@ pub enum ServerChatCommand {
|
||||
Location,
|
||||
CreateLocation,
|
||||
DeleteLocation,
|
||||
WeatherZone,
|
||||
}
|
||||
|
||||
impl ServerChatCommand {
|
||||
@ -686,6 +694,15 @@ impl ServerChatCommand {
|
||||
"Delete a location",
|
||||
Some(Moderator),
|
||||
),
|
||||
ServerChatCommand::WeatherZone => cmd(
|
||||
vec![
|
||||
Enum("weather kind", WEATHERS.clone(), Required),
|
||||
Float("radius", 500.0, Optional),
|
||||
Float("time", 300.0, Optional),
|
||||
],
|
||||
"Create a weather zone",
|
||||
Some(Admin),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -763,6 +780,7 @@ impl ServerChatCommand {
|
||||
ServerChatCommand::Location => "location",
|
||||
ServerChatCommand::CreateLocation => "create_location",
|
||||
ServerChatCommand::DeleteLocation => "delete_location",
|
||||
ServerChatCommand::WeatherZone => "weather_zone",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use crate::{
|
||||
Ban, BanAction, BanInfo, EditableSetting, SettingError, WhitelistInfo, WhitelistRecord,
|
||||
},
|
||||
sys::terrain::NpcData,
|
||||
weather::WeatherSim,
|
||||
wiring,
|
||||
wiring::OutputFormula,
|
||||
Server, Settings, SpawnPoint, StateExt,
|
||||
@ -44,7 +45,7 @@ use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
uid::{Uid, UidAllocator},
|
||||
vol::{ReadVol, RectVolSize},
|
||||
Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
||||
weather, Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
||||
};
|
||||
use common_net::{
|
||||
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral},
|
||||
@ -190,6 +191,7 @@ fn do_command(
|
||||
ServerChatCommand::Location => handle_location,
|
||||
ServerChatCommand::CreateLocation => handle_create_location,
|
||||
ServerChatCommand::DeleteLocation => handle_delete_location,
|
||||
ServerChatCommand::WeatherZone => handle_weather_zone,
|
||||
};
|
||||
|
||||
handler(server, client, target, args, cmd)
|
||||
@ -3595,3 +3597,72 @@ fn handle_delete_location(
|
||||
Err(action.help_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_weather_zone(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
args: Vec<String>,
|
||||
action: &ServerChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
if let (Some(name), radius, time) = parse_cmd_args!(args, String, f32, f32) {
|
||||
let radius = radius.map(|r| r / weather::CELL_SIZE as f32).unwrap_or(1.0);
|
||||
let time = time.unwrap_or(100.0);
|
||||
|
||||
let mut add_zone = |weather: weather::Weather| {
|
||||
if let Ok(pos) = position(server, client, "player") {
|
||||
let pos = pos.0.xy() / weather::CELL_SIZE as f32;
|
||||
server
|
||||
.state
|
||||
.ecs_mut()
|
||||
.write_resource::<WeatherSim>()
|
||||
.add_zone(weather, pos, radius, time);
|
||||
}
|
||||
};
|
||||
match name.as_str() {
|
||||
"clear" => {
|
||||
add_zone(weather::Weather {
|
||||
cloud: 0.0,
|
||||
rain: 0.0,
|
||||
wind: Vec2::zero(),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
"cloudy" => {
|
||||
add_zone(weather::Weather {
|
||||
cloud: 0.4,
|
||||
rain: 0.0,
|
||||
wind: Vec2::zero(),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
"rain" => {
|
||||
add_zone(weather::Weather {
|
||||
cloud: 0.1,
|
||||
rain: 0.15,
|
||||
wind: Vec2::new(1.0, -1.0),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
"wind" => {
|
||||
add_zone(weather::Weather {
|
||||
cloud: 0.0,
|
||||
rain: 0.0,
|
||||
wind: Vec2::new(10.0, 10.0),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
"storm" => {
|
||||
add_zone(weather::Weather {
|
||||
cloud: 0.3,
|
||||
rain: 0.3,
|
||||
wind: Vec2::new(15.0,20.0),
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
_ => Err("Valid values are 'clear', 'rain', 'wind', 'storm'".to_string()),
|
||||
}
|
||||
} else {
|
||||
Err(action.help_string())
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ mod sim;
|
||||
mod sync;
|
||||
mod tick;
|
||||
|
||||
pub use sim::WeatherSim;
|
||||
|
||||
/// How often the weather is updated, in seconds
|
||||
const WEATHER_DT: f32 = 5.0;
|
||||
|
||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch::<tick::Sys>(dispatch_builder, &[]);
|
||||
dispatch::<sync::Sys>(dispatch_builder, &[&tick::Sys::sys_name()]);
|
||||
@ -17,12 +22,9 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
|
||||
pub fn init(state: &mut State, world: &world::World) {
|
||||
let weather_size = world.sim().get_size() / CHUNKS_PER_CELL;
|
||||
let sim = sim::WeatherSim::new(weather_size, world);
|
||||
let sim = WeatherSim::new(weather_size, world);
|
||||
state.ecs_mut().insert(sim);
|
||||
|
||||
/// How often the weather is updated, in seconds
|
||||
const WEATHER_DT: f32 = 5.0;
|
||||
|
||||
// NOTE: If weather computations get too heavy, this should not block the main
|
||||
// thread.
|
||||
state
|
||||
|
@ -1,19 +1,58 @@
|
||||
use common::{
|
||||
grid::Grid,
|
||||
resources::TimeOfDay,
|
||||
weather::{WeatherGrid, CELL_SIZE},
|
||||
weather::{Weather, WeatherGrid, CELL_SIZE},
|
||||
};
|
||||
use noise::{NoiseFn, SuperSimplex, Turbulence};
|
||||
use vek::*;
|
||||
use world::World;
|
||||
|
||||
use crate::weather::WEATHER_DT;
|
||||
|
||||
fn cell_to_wpos(p: Vec2<i32>) -> Vec2<i32> { p * CELL_SIZE as i32 }
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WeatherZone {
|
||||
weather: Weather,
|
||||
/// Time, in seconds this zone lives.
|
||||
time_to_live: f32,
|
||||
}
|
||||
|
||||
pub struct WeatherSim {
|
||||
size: Vec2<u32>,
|
||||
zones: Grid<Option<WeatherZone>>,
|
||||
}
|
||||
|
||||
impl WeatherSim {
|
||||
pub fn new(size: Vec2<u32>, _world: &World) -> Self { Self { size } }
|
||||
pub fn new(size: Vec2<u32>, _world: &World) -> Self {
|
||||
Self {
|
||||
size,
|
||||
zones: Grid::new(size.as_(), None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a weather zone as a circle at a position, with a given radius. Both
|
||||
/// of which should be in weather cell units
|
||||
pub fn add_zone(&mut self, weather: Weather, pos: Vec2<f32>, radius: f32, time: f32) {
|
||||
let min: Vec2<i32> = (pos - radius).as_::<i32>().map(|e| e.max(0));
|
||||
let max: Vec2<i32> = (pos + radius)
|
||||
.ceil()
|
||||
.as_::<i32>()
|
||||
.map2(self.size.as_::<i32>(), |a, b| a.min(b));
|
||||
for y in min.y..max.y {
|
||||
for x in min.x..max.x {
|
||||
let ipos = Vec2::new(x, y);
|
||||
let p = ipos.as_::<f32>();
|
||||
|
||||
if p.distance_squared(pos) < radius.powi(2) {
|
||||
self.zones[ipos] = Some(WeatherZone {
|
||||
weather,
|
||||
time_to_live: time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Time step is cell size / maximum wind speed
|
||||
pub fn tick(&mut self, time_of_day: &TimeOfDay, out: &mut WeatherGrid) {
|
||||
@ -30,24 +69,33 @@ impl WeatherSim {
|
||||
let rain_nz = SuperSimplex::new();
|
||||
|
||||
for (point, cell) in out.iter_mut() {
|
||||
let wpos = cell_to_wpos(point);
|
||||
if let Some(zone) = &mut self.zones[point] {
|
||||
*cell = zone.weather;
|
||||
zone.time_to_live -= WEATHER_DT;
|
||||
if zone.time_to_live <= 0.0 {
|
||||
self.zones[point] = None;
|
||||
}
|
||||
} else {
|
||||
let wpos = cell_to_wpos(point);
|
||||
|
||||
let pos = wpos.as_::<f64>() + time as f64 * 0.1;
|
||||
let pos = wpos.as_::<f64>() + time as f64 * 0.1;
|
||||
|
||||
let space_scale = 7_500.0;
|
||||
let time_scale = 100_000.0;
|
||||
let spos = (pos / space_scale).with_z(time as f64 / time_scale);
|
||||
let space_scale = 7_500.0;
|
||||
let time_scale = 100_000.0;
|
||||
let spos = (pos / space_scale).with_z(time as f64 / time_scale);
|
||||
|
||||
let pressure = (base_nz.get(spos.into_array()) * 0.5 + 1.0).clamped(0.0, 1.0) as f32;
|
||||
let pressure =
|
||||
(base_nz.get(spos.into_array()) * 0.5 + 1.0).clamped(0.0, 1.0) as f32;
|
||||
|
||||
const RAIN_CLOUD_THRESHOLD: f32 = 0.26;
|
||||
cell.cloud = (1.0 - pressure) * 0.5;
|
||||
cell.rain = (1.0 - pressure - RAIN_CLOUD_THRESHOLD).max(0.0).powf(1.0);
|
||||
cell.wind = Vec2::new(
|
||||
rain_nz.get(spos.into_array()).powi(3) as f32,
|
||||
rain_nz.get((spos + 1.0).into_array()).powi(3) as f32,
|
||||
) * 200.0
|
||||
* (1.0 - pressure);
|
||||
const RAIN_CLOUD_THRESHOLD: f32 = 0.26;
|
||||
cell.cloud = (1.0 - pressure) * 0.5;
|
||||
cell.rain = (1.0 - pressure - RAIN_CLOUD_THRESHOLD).max(0.0).powf(1.0);
|
||||
cell.wind = Vec2::new(
|
||||
rain_nz.get(spos.into_array()).powi(3) as f32,
|
||||
rain_nz.get((spos + 1.0).into_array()).powi(3) as f32,
|
||||
) * 200.0
|
||||
* (1.0 - pressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user