aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/game.gd2
-rw-r--r--client/game.tscn5
-rw-r--r--client/map/auto_setup/environment_setup.gd3
-rw-r--r--client/map/environment.gd10
-rw-r--r--client/map/environment.tscn73
-rw-r--r--client/multiplayer.gd3
-rw-r--r--data/maps/debug.yaml1
-rw-r--r--server/protocol/src/lib.rs6
-rw-r--r--server/src/entity/mod.rs12
-rw-r--r--server/src/entity/weather.rs51
-rw-r--r--server/src/game.rs23
-rw-r--r--server/src/lib.rs19
-rw-r--r--test-client/protocol.ts7
13 files changed, 204 insertions, 11 deletions
diff --git a/client/game.gd b/client/game.gd
index e0b06772..b1f209a1 100644
--- a/client/game.gd
+++ b/client/game.gd
@@ -257,6 +257,8 @@ func _ready():
if lobby_state and not join_sent:
join()
)
+
+ mp.update_environment.connect($Environment.update)
func join():
join_sent = true
diff --git a/client/game.tscn b/client/game.tscn
index 345a35b8..a30e5fe2 100644
--- a/client/game.tscn
+++ b/client/game.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=10 format=3 uid="uid://c6krh36hoqfg8"]
+[gd_scene load_steps=11 format=3 uid="uid://c6krh36hoqfg8"]
[ext_resource type="Script" path="res://game.gd" id="1_sftfn"]
[ext_resource type="PackedScene" uid="uid://b31mlnao6ybt8" path="res://player/follow_camera.tscn" id="2_s8y6o"]
@@ -6,6 +6,7 @@
[ext_resource type="Script" path="res://map/auto_setup/light_setup.gd" id="5_6fpff"]
[ext_resource type="Script" path="res://multiplayer.gd" id="6_fbxu8"]
[ext_resource type="PackedScene" uid="uid://b4gone8fu53r7" path="res://map/map.tscn" id="6_prg6t"]
+[ext_resource type="PackedScene" uid="uid://nroo08m5og0" path="res://map/environment.tscn" id="7_v2apt"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_py7px"]
ground_bottom_color = Color(0.0826605, 0.065772, 0.0461518, 1)
@@ -45,3 +46,5 @@ script = ExtResource("5_6fpff")
[node name="Map" parent="." instance=ExtResource("6_prg6t")]
[node name="Center" type="Node3D" parent="."]
+
+[node name="Environment" parent="." instance=ExtResource("7_v2apt")]
diff --git a/client/map/auto_setup/environment_setup.gd b/client/map/auto_setup/environment_setup.gd
index 96feb86b..75dc900f 100644
--- a/client/map/auto_setup/environment_setup.gd
+++ b/client/map/auto_setup/environment_setup.gd
@@ -14,7 +14,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
extends WorldEnvironment
-class_name EnvironmentSetup
+class_name EnvironmentSetup
@export var allow_sdfgi := true
@@ -26,3 +26,4 @@ func apply_settings():
environment.ssao_enabled = Global.get_setting("ssao")
environment.sdfgi_enabled = Global.get_setting("sdfgi") and allow_sdfgi
environment.glow_enabled = Global.get_setting("glow")
+
diff --git a/client/map/environment.gd b/client/map/environment.gd
new file mode 100644
index 00000000..f36b51b6
--- /dev/null
+++ b/client/map/environment.gd
@@ -0,0 +1,10 @@
+extends Node3D
+
+func update(params: Dictionary):
+ $Wind.emitting = params["wind"] > 0.1
+ var a: ParticleProcessMaterial = $Wind.process_material
+ a.initial_velocity_max = params["wind"] * 15.
+ a.initial_velocity_min = params["wind"] * 15.
+
+ $Rain.emitting = params["rain"] > 0.1
+ $Rain.amount = params["rain"] * 500
diff --git a/client/map/environment.tscn b/client/map/environment.tscn
new file mode 100644
index 00000000..ee51b490
--- /dev/null
+++ b/client/map/environment.tscn
@@ -0,0 +1,73 @@
+[gd_scene load_steps=10 format=3 uid="uid://nroo08m5og0"]
+
+[ext_resource type="Script" path="res://map/environment.gd" id="1_qy481"]
+
+[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_fyrr8"]
+emission_shape_offset = Vector3(0, 5, 0)
+emission_shape = 3
+emission_box_extents = Vector3(5, 5, 5)
+direction = Vector3(0, -1, 0)
+spread = 5.0
+initial_velocity_min = 10.0
+initial_velocity_max = 10.0
+scale_over_velocity_max = 1000.0
+turbulence_influence_min = 0.01
+turbulence_influence_max = 0.01
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6mo3f"]
+depth_draw_mode = 2
+albedo_color = Color(0.792157, 0.74902, 1, 1)
+roughness = 0.07
+
+[sub_resource type="CapsuleMesh" id="CapsuleMesh_6kss1"]
+material = SubResource("StandardMaterial3D_6mo3f")
+radius = 0.01
+height = 0.5
+radial_segments = 4
+rings = 4
+
+[sub_resource type="Curve" id="Curve_frsnm"]
+_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.111524, 0.771057), 1.26444, 1.26444, 0, 0, Vector2(0.866171, 0.820827), -1.03348, -1.03348, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
+point_count = 4
+
+[sub_resource type="CurveTexture" id="CurveTexture_3gn1x"]
+curve = SubResource("Curve_frsnm")
+
+[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_pyn6b"]
+emission_shape_offset = Vector3(-2.5, 0, 0)
+emission_shape = 3
+emission_box_extents = Vector3(5, 5, 5)
+spread = 5.0
+initial_velocity_min = 2.0
+initial_velocity_max = 2.0
+gravity = Vector3(0, 0, 0)
+scale_curve = SubResource("CurveTexture_3gn1x")
+scale_over_velocity_max = 1000.0
+turbulence_enabled = true
+turbulence_influence_min = 0.01
+turbulence_influence_max = 0.01
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ig8qu"]
+depth_draw_mode = 2
+albedo_color = Color(0.412412, 0.527344, 0.250208, 1)
+
+[sub_resource type="SphereMesh" id="SphereMesh_4trvf"]
+material = SubResource("StandardMaterial3D_ig8qu")
+radius = 0.03
+height = 0.06
+
+[node name="Environment" type="Node3D"]
+script = ExtResource("1_qy481")
+
+[node name="Rain" type="GPUParticles3D" parent="."]
+emitting = false
+amount = 500
+process_material = SubResource("ParticleProcessMaterial_fyrr8")
+draw_pass_1 = SubResource("CapsuleMesh_6kss1")
+
+[node name="Wind" type="GPUParticles3D" parent="."]
+emitting = false
+amount = 150
+lifetime = 5.0
+process_material = SubResource("ParticleProcessMaterial_pyn6b")
+draw_pass_1 = SubResource("SphereMesh_4trvf")
diff --git a/client/multiplayer.gd b/client/multiplayer.gd
index cd697672..0e3b450a 100644
--- a/client/multiplayer.gd
+++ b/client/multiplayer.gd
@@ -50,6 +50,7 @@ signal set_tile_finished(tile: Vector2i, warn: bool)
signal set_player_finished(player: int, warn: bool)
signal set_ingame(state: bool, lobby: bool)
signal score(demands_failed: int, demands_completed: int, points: int, time_remaining: float)
+signal update_environment(params: Dictionary)
signal server_message(text: String)
signal replay_start()
signal connection_closed(reason: String)
@@ -265,6 +266,8 @@ func handle_packet(bytes: PackedByteArray):
"server_message":
var text = decoded["text"]
server_message.emit(text)
+ "update_environment":
+ update_environment.emit(decoded)
"replay_start": replay_start.emit()
_:
push_error("Unrecognized packet type: %s" % packet_type)
diff --git a/data/maps/debug.yaml b/data/maps/debug.yaml
index d571dc13..d31e2f5b 100644
--- a/data/maps/debug.yaml
+++ b/data/maps/debug.yaml
@@ -92,6 +92,7 @@ entities:
- !customers
- !player_portal { from: [12.5, 8.5], to: [12.5, 2.5] }
- !item_portal { from: [14, 4], to: [14, 6] }
+ - !weather
tile_entities:
"}": !conveyor { dir: [1, 0], filter: dough-foodprocessor }
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs
index f4513641..2fcc5598 100644
--- a/server/protocol/src/lib.rs
+++ b/server/protocol/src/lib.rs
@@ -231,9 +231,9 @@ pub struct Score {
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Default)]
pub struct Environment {
- rain: f64, // 0-1; clear..raining
- wind: f64, // 0-1; still..stormy
- time: f64, // 0-1; night..morning..noon..evening
+ pub rain: f32, // 0-1; clear..raining
+ pub wind: f32, // 0-1; still..stormy
+ pub time: f32, // 0-1; night..morning..noon..evening
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Copy, PartialEq, Eq, Hash)]
diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs
index ad0bde6f..d042d458 100644
--- a/server/src/entity/mod.rs
+++ b/server/src/entity/mod.rs
@@ -19,6 +19,7 @@ pub mod conveyor;
pub mod customers;
pub mod item_portal;
pub mod player_portal;
+pub mod weather;
use crate::{data::ItemTileRegistry, game::Game, interaction::Recipe};
use anyhow::{anyhow, Result};
@@ -32,6 +33,7 @@ use item_portal::ItemPortal;
use player_portal::PlayerPortal;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet, VecDeque};
+use weather::WeatherController;
pub trait EntityT: Clone {
fn tick(&mut self, game: &mut Game, packet_out: &mut VecDeque<PacketC>, dt: f32) -> Result<()>;
@@ -49,7 +51,13 @@ macro_rules! entities {
};
}
-entities!(Conveyor, ItemPortal, PlayerPortal, Customers);
+entities!(
+ Conveyor,
+ ItemPortal,
+ PlayerPortal,
+ Customers,
+ WeatherController
+);
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
@@ -71,6 +79,7 @@ pub enum EntityDecl {
to: Vec2,
},
Customers {},
+ Weather {},
}
pub fn construct_entity(
@@ -125,5 +134,6 @@ pub fn construct_entity(
.collect();
Entity::Customers(Customers::new(chairs, demands)?)
}
+ EntityDecl::Weather {} => Entity::WeatherController(WeatherController::default()),
})
}
diff --git a/server/src/entity/weather.rs b/server/src/entity/weather.rs
new file mode 100644
index 00000000..79ac364f
--- /dev/null
+++ b/server/src/entity/weather.rs
@@ -0,0 +1,51 @@
+use super::EntityT;
+use crate::{game::Game, InterpolateExt};
+use hurrycurry_protocol::PacketC;
+use rand::{random, seq::IndexedRandom, thread_rng};
+use std::{
+ collections::VecDeque,
+ time::{Duration, Instant},
+};
+
+#[derive(Clone, Debug)]
+pub struct WeatherController {
+ next_event: Instant,
+ event: usize,
+}
+
+impl Default for WeatherController {
+ fn default() -> Self {
+ Self {
+ next_event: Instant::now() + Duration::from_secs(10),
+ event: Default::default(),
+ }
+ }
+}
+impl EntityT for WeatherController {
+ fn tick(
+ &mut self,
+ game: &mut Game,
+ _packet_out: &mut VecDeque<PacketC>,
+ dt: f32,
+ ) -> anyhow::Result<()> {
+ if self.next_event < Instant::now() {
+ self.next_event += Duration::from_secs_f32(30. + random::<f32>() * 40.);
+ if self.event > 0 {
+ self.event = 0;
+ } else {
+ self.event = *[0, 0, 1, 2].choose(&mut thread_rng()).unwrap();
+ }
+ }
+
+ let (rain, wind) = match self.event {
+ 0 => (0., 0.),
+ 1 => (0.7, 0.),
+ 2 => (0., 0.7),
+ _ => (1., 1.),
+ };
+ game.environment.wind.exp_to(wind, dt * 0.05);
+ game.environment.rain.exp_to(rain, dt * 0.15);
+ eprintln!("{:#?}", game.environment);
+ Ok(())
+ }
+}
diff --git a/server/src/game.rs b/server/src/game.rs
index d649d87e..69272159 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -25,8 +25,8 @@ use anyhow::{anyhow, bail, Result};
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
movement::MovementBase,
- ClientGamedata, ItemIndex, ItemLocation, Menu, Message, PacketC, PacketS, PlayerID,
- RecipeIndex, Score, TileIndex,
+ ClientGamedata, Environment, ItemIndex, ItemLocation, Menu, Message, PacketC, PacketS,
+ PlayerID, RecipeIndex, Score, TileIndex,
};
use log::{info, warn};
use std::{
@@ -76,6 +76,8 @@ pub struct Game {
end: Option<Instant>,
pub lobby: bool,
+ pub environment_next_update: Instant,
+ pub environment: Environment,
pub score_changed: bool,
pub score: Score,
}
@@ -93,11 +95,13 @@ impl Game {
data: Gamedata::default().into(),
players: HashMap::new(),
tiles: HashMap::new(),
+ environment_next_update: Instant::now(),
walkable: HashSet::new(),
end: None,
entities: Arc::new(RwLock::new(vec![])),
players_spatial_index: SpatialIndex::default(),
score: Score::default(),
+ environment: Environment::default(),
score_changed: false,
}
}
@@ -117,6 +121,9 @@ impl Game {
neighbors: [None, None, None, None],
})
}
+ self.score = Score::default();
+ self.end = None;
+ self.environment = Environment::default();
self.walkable.clear();
}
pub fn load(
@@ -215,6 +222,7 @@ impl Game {
.collect(),
},
});
+ out.push(PacketC::UpdateEnvironment(self.environment.clone()));
for (&id, player) in &self.players {
out.push(PacketC::AddPlayer {
id,
@@ -589,9 +597,16 @@ impl Game {
}
}
+ let now = Instant::now();
+
+ if self.environment_next_update < now {
+ packet_out.push_back(PacketC::UpdateEnvironment(self.environment.clone()));
+ self.environment_next_update += Duration::from_secs(5);
+ }
+
if let Some(end) = self.end {
- self.score.time_remaining = (end - Instant::now()).as_secs_f64();
- if end < Instant::now() {
+ self.score.time_remaining = (end - now).as_secs_f64();
+ if end < now {
let relative_score = (self.score.points * 100) / self.data.score_baseline.max(1);
self.score.stars = match relative_score {
100.. => 3,
diff --git a/server/src/lib.rs b/server/src/lib.rs
index a59aad11..2cbcc10b 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -20,5 +20,22 @@ pub mod data;
pub mod entity;
pub mod game;
pub mod interaction;
-pub mod state;
pub mod spatial_index;
+pub mod state;
+
+use hurrycurry_protocol::glam::Vec2;
+
+pub trait InterpolateExt {
+ fn exp_to(&mut self, target: Self, dt: f32);
+}
+impl InterpolateExt for Vec2 {
+ fn exp_to(&mut self, target: Self, dt: f32) {
+ self.x = target.x + (self.x - target.x) * (-dt).exp();
+ self.y = target.y + (self.y - target.y) * (-dt).exp();
+ }
+}
+impl InterpolateExt for f32 {
+ fn exp_to(&mut self, target: Self, dt: f32) {
+ *self = target + (*self - target) * (-dt).exp();
+ }
+}
diff --git a/test-client/protocol.ts b/test-client/protocol.ts
index 432f30e2..ef216ffd 100644
--- a/test-client/protocol.ts
+++ b/test-client/protocol.ts
@@ -59,6 +59,7 @@ export type PacketC =
| { type: "score" } & Score // Supplies information for score OSD
| { type: "menu" } & Menu // Open a menu on the client-side
| { type: "movement_sync" } // Your movement is difference on the server, you should update your position from a `position` packet
+ | { type: "update_environment" } & Environment
| { type: "set_ingame", state: boolean, lobby: boolean } // Set to false when entering the game or switching maps
| { type: "error", message: string } // Your client did something wrong.
@@ -86,3 +87,9 @@ export type Message =
export type ItemLocation =
{ player: PlayerID }
| { tile: Vec2 }
+
+export interface Environment {
+ rain: number, // 0-1; clear..raining
+ wind: number, // 0-1; still..stormy
+ time: number, // 0-1; night..morning..noon..evening
+} \ No newline at end of file