aboutsummaryrefslogtreecommitdiff
path: root/server/src/entity
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/entity')
-rw-r--r--server/src/entity/campaign.rs87
-rw-r--r--server/src/entity/customers.rs1
-rw-r--r--server/src/entity/mod.rs33
3 files changed, 116 insertions, 5 deletions
diff --git a/server/src/entity/campaign.rs b/server/src/entity/campaign.rs
index c4c8ab52..934f7542 100644
--- a/server/src/entity/campaign.rs
+++ b/server/src/entity/campaign.rs
@@ -16,8 +16,14 @@
*/
use super::{Entity, EntityContext};
+use crate::{scoreboard::ScoreboardStore, server::GameServerExt};
use anyhow::Result;
-use hurrycurry_protocol::glam::Vec2;
+use hurrycurry_protocol::{
+ glam::{IVec2, Vec2},
+ PacketC, PlayerID, TileIndex,
+};
+use serde::{Deserialize, Serialize};
+use std::fmt::Write;
#[derive(Debug, Default, Clone)]
pub struct Map {
@@ -39,3 +45,82 @@ impl Entity for Map {
Ok(())
}
}
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum GateCondition {
+ All(Vec<GateCondition>),
+ Any(Vec<GateCondition>),
+ Stars(String, u8),
+}
+
+#[derive(Debug, Clone)]
+pub struct Gate {
+ pub active: bool,
+ pub unlocked: bool,
+ pub location: IVec2,
+ pub blocker_tile: TileIndex,
+ pub condition: GateCondition,
+}
+impl Entity for Gate {
+ fn tick(&mut self, c: EntityContext<'_>) -> Result<()> {
+ if self.active {
+ self.active = false;
+ self.unlocked = self.condition.check(c.scoreboard);
+ if !self.unlocked {
+ c.game
+ .set_tile(self.location, Some(self.blocker_tile), c.packet_out)
+ }
+ }
+ Ok(())
+ }
+ fn interact(
+ &mut self,
+ c: EntityContext<'_>,
+ pos: Option<IVec2>,
+ _player: PlayerID,
+ ) -> Result<bool> {
+ if !self.unlocked && pos == Some(self.location) {
+ c.packet_out.push_back(PacketC::ServerMessage {
+ text: format!("To unlock: \n\n{}", self.condition.show(c.scoreboard)),
+ });
+ return Ok(true);
+ }
+ Ok(false)
+ }
+}
+
+impl GateCondition {
+ fn check(&self, scoreboard: &ScoreboardStore) -> bool {
+ match self {
+ GateCondition::All(cs) => cs.iter().all(|c| c.check(scoreboard)),
+ GateCondition::Any(cs) => cs.iter().any(|c| c.check(scoreboard)),
+ GateCondition::Stars(map, thres) => scoreboard.get(map).map_or(false, |s| {
+ s.best.first().map_or(false, |b| b.score.stars >= *thres)
+ }),
+ }
+ }
+ pub fn show(&self, scoreboard: &ScoreboardStore) -> String {
+ match self {
+ GateCondition::All(cs) => {
+ let mut o = String::new();
+ for c in cs {
+ if !c.check(scoreboard) {
+ writeln!(o, "- {}", c.show(scoreboard)).unwrap();
+ }
+ }
+ o
+ }
+ GateCondition::Any(cs) => {
+ let mut o = String::new();
+ for c in cs {
+ writeln!(o, "- {}", c.show(scoreboard)).unwrap();
+ }
+ o
+ }
+ GateCondition::Stars(map, thres) => {
+ format!("Reach at least {thres} stars in {map}")
+ }
+ }
+ }
+}
diff --git a/server/src/entity/customers.rs b/server/src/entity/customers.rs
index d75f5b51..da905bc0 100644
--- a/server/src/entity/customers.rs
+++ b/server/src/entity/customers.rs
@@ -54,6 +54,7 @@ impl Entity for Customers {
packet_in: c.packet_in,
score_changed: c.score_changed,
dt: c.dt,
+ scoreboard: c.scoreboard,
load_map: c.load_map,
})?;
}
diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs
index 8055b50d..c8424934 100644
--- a/server/src/entity/mod.rs
+++ b/server/src/entity/mod.rs
@@ -23,16 +23,16 @@ pub mod environment_effect;
pub mod item_portal;
pub mod player_portal;
-use crate::data::ItemTileRegistry;
+use crate::{data::ItemTileRegistry, scoreboard::ScoreboardStore};
use anyhow::{anyhow, Result};
-use campaign::Map;
+use campaign::{Gate, GateCondition, Map};
use conveyor::Conveyor;
use customers::Customers;
use environment_effect::{EnvironmentController, EnvironmentEffect, EnvironmentEffectController};
use hurrycurry_client_lib::Game;
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
- PacketC, PacketS,
+ PacketC, PacketS, PlayerID,
};
use item_portal::ItemPortal;
use player_portal::PlayerPortal;
@@ -48,12 +48,21 @@ pub struct EntityContext<'a> {
pub packet_in: &'a mut VecDeque<PacketS>,
pub score_changed: &'a mut bool,
pub load_map: &'a mut Option<String>,
+ pub scoreboard: &'a ScoreboardStore,
pub dt: f32,
}
pub trait Entity {
fn tick(&mut self, c: EntityContext<'_>) -> Result<()>;
fn destructor(&mut self, _c: EntityContext<'_>) {}
+ fn interact(
+ &mut self,
+ _c: EntityContext<'_>,
+ _pos: Option<IVec2>,
+ _player: PlayerID,
+ ) -> Result<bool> {
+ Ok(false)
+ }
}
// macro_rules! entities {
@@ -104,6 +113,10 @@ pub enum EntityDecl {
},
EnvironmentEffect(EnvironmentEffect),
Environment(Vec<String>),
+ Gate {
+ location: Option<IVec2>,
+ condition: GateCondition,
+ },
}
pub fn construct_entity(
@@ -113,7 +126,9 @@ pub fn construct_entity(
) -> Result<DynEntity> {
Ok(match decl.to_owned() {
EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal {
- from: from.or(pos).ok_or(anyhow!("Item portal start without start"))?,
+ from: from
+ .or(pos)
+ .ok_or(anyhow!("Item portal start without start"))?,
to,
}),
EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal {
@@ -149,6 +164,16 @@ pub fn construct_entity(
.ok_or(anyhow!("no location"))?,
name,
}),
+ EntityDecl::Gate {
+ condition,
+ location,
+ } => Box::new(Gate {
+ condition,
+ unlocked: false,
+ location: location.or(pos).ok_or(anyhow!("no location"))?,
+ blocker_tile: reg.register_tile("fence".to_string()),
+ active: true,
+ }),
EntityDecl::Customers {} => Box::new(Customers::new()?),
EntityDecl::EnvironmentEffect(config) => Box::new(EnvironmentEffectController::new(config)),
EntityDecl::Environment(names) => Box::new(EnvironmentController(names)),