aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--book/book.js4
-rw-r--r--book/book.typ4
-rw-r--r--data/.gitignore1
-rw-r--r--data/makefile7
-rw-r--r--data/recipes/.gitignore1
-rw-r--r--server/protocol/src/helpers.rs110
-rw-r--r--server/protocol/src/lib.rs129
-rw-r--r--server/src/commands.rs2
-rw-r--r--server/src/data/mod.rs19
-rw-r--r--server/src/entity/book.rs2
-rw-r--r--server/src/entity/customers.rs1
-rw-r--r--server/src/entity/mod.rs7
-rw-r--r--server/src/server.rs4
13 files changed, 177 insertions, 114 deletions
diff --git a/book/book.js b/book/book.js
index 84c0e9dc..094068c0 100644
--- a/book/book.js
+++ b/book/book.js
@@ -1,5 +1,5 @@
-const tr = t => ({ translated: t })
+const tr = t => ({ translation: { id: t, params: [] } })
const plain = t => ({ text: t })
const par_raw = (s, th, bold) => ({
t: "par", es: [{
@@ -42,7 +42,7 @@ const about = () => ({
par("b.about.name"),
{ t: "text", s: tr("b.about.image_attrib"), color: "#000000", size: 20, font: "FreeSans" },
{
- t: "table", cols: 2, es: [
+ t: "table", es: [
[par("b.about.image_attrib.name", false, true), par("b.about.image_attrib.author", false, true)],
[par("b.nigiri"), par_raw(plain("Ahtziri Lagarde(unsplash)"))],
[par("b.icecream"), par_raw(plain("Markus Spiske(unsplash), adapted"))],
diff --git a/book/book.typ b/book/book.typ
index 39f3cc46..14e617a0 100644
--- a/book/book.typ
+++ b/book/book.typ
@@ -21,7 +21,7 @@
}
#let tr(s) = translations.at(s, default: text(fill: red)[TRANSLATION: #s])
-#let msgstr(o) = if "translated" in o { tr(o.translated) } else { o.text }
+#let msgstr(o) = if "translation" in o { tr(o.translation.id) } else { o.text }
#let element(elem) = if elem.t == "document" [
#for e in elem.es [
@@ -50,7 +50,7 @@
] else if elem.t == "par" [
#par(..elem.es.map(element))
] else if elem.t == "table" [
- #table(columns: elem.cols, ..elem.es.flatten().map(element))
+ #table(columns: elem.es.at(0).len(), ..elem.es.flatten().map(element))
] else [
#elem
]
diff --git a/data/.gitignore b/data/.gitignore
new file mode 100644
index 00000000..08711f05
--- /dev/null
+++ b/data/.gitignore
@@ -0,0 +1 @@
+/book.json
diff --git a/data/makefile b/data/makefile
index 4c948a81..a5041803 100644
--- a/data/makefile
+++ b/data/makefile
@@ -14,11 +14,14 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
SETS = default none anticurry
-all: $(patsubst %,recipes/%.yaml,$(SETS))
+ALL = $(patsubst %,recipes/%.yaml,$(SETS)) book.json
+all: $(ALL)
graphs: recipes/default.svg
JSR = deno run
+book.json: ../book/book.json
+ cp $< $@
recipes/none.yaml:
echo > $@
recipes/anticurry.yaml: recipes/default.yaml
@@ -33,4 +36,4 @@ recipes/%.svg: recipes/%.gv.txt
dot -Tsvg -Kdot >$@<$<
clean:
- $(RM) -r recipes/*.{yaml,gv.txt,svg}
+ $(RM) $(ALL)
diff --git a/data/recipes/.gitignore b/data/recipes/.gitignore
index a9e52ca2..77c36404 100644
--- a/data/recipes/.gitignore
+++ b/data/recipes/.gitignore
@@ -1,4 +1,3 @@
*.yaml
-!newRecipes.yaml
*.gv.txt
*.svg
diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs
new file mode 100644
index 00000000..924d0886
--- /dev/null
+++ b/server/protocol/src/helpers.rs
@@ -0,0 +1,110 @@
+use std::fmt::Display;
+
+use crate::{
+ DocumentElement, Gamedata, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, TileIndex,
+};
+
+impl Gamedata {
+ pub fn tile_name(&self, index: TileIndex) -> &String {
+ &self.tile_names[index.0]
+ }
+ pub fn is_tile_colliding(&self, index: TileIndex) -> bool {
+ self.tile_collide[index.0]
+ }
+ pub fn is_tile_interactable(&self, index: TileIndex) -> bool {
+ self.tile_interact[index.0]
+ }
+ pub fn item_name(&self, index: ItemIndex) -> &String {
+ &self.item_names[index.0]
+ }
+ pub fn recipe(&self, index: RecipeIndex) -> &Recipe {
+ &self.recipes[index.0]
+ }
+ pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> {
+ self.tile_names
+ .iter()
+ .position(|t| t == name)
+ .map(TileIndex)
+ }
+ pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> {
+ self.item_names
+ .iter()
+ .position(|t| t == name)
+ .map(ItemIndex)
+ }
+ pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> {
+ self.recipes
+ .iter()
+ .enumerate()
+ .map(|(i, e)| (RecipeIndex(i), e))
+ }
+}
+
+impl Recipe {
+ pub fn tile(&self) -> Option<TileIndex> {
+ match self {
+ Recipe::Passive { tile, .. } => *tile,
+ Recipe::Active { tile, .. } => *tile,
+ Recipe::Instant { tile, .. } => *tile,
+ }
+ }
+ pub fn speed(&self) -> Option<f32> {
+ match self {
+ Recipe::Passive { speed, .. } => Some(*speed),
+ Recipe::Active { speed, .. } => Some(*speed),
+ _ => None,
+ }
+ }
+ pub fn revert_speed(&self) -> Option<f32> {
+ match self {
+ Recipe::Passive { revert_speed, .. } => *revert_speed,
+ _ => None,
+ }
+ }
+ pub fn warn(&self) -> bool {
+ match self {
+ Recipe::Passive { warn, .. } => *warn,
+ _ => false,
+ }
+ }
+ pub fn inputs(&self) -> Vec<ItemIndex> {
+ match self {
+ Recipe::Passive { input, .. } => vec![*input],
+ Recipe::Active { input, .. } => vec![*input],
+ Recipe::Instant { inputs, .. } => inputs.iter().flat_map(|e| e.to_owned()).collect(),
+ }
+ }
+ pub fn outputs(&self) -> Vec<ItemIndex> {
+ match self {
+ Recipe::Passive { output, .. } => output.iter().copied().collect(),
+ Recipe::Active { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(),
+ Recipe::Instant { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(),
+ }
+ }
+ pub fn supports_tile(&self, tile: Option<TileIndex>) -> bool {
+ if let Some(tile_constraint) = self.tile() {
+ if let Some(tile) = tile {
+ tile == tile_constraint
+ } else {
+ false
+ }
+ } else {
+ true
+ }
+ }
+}
+
+impl Display for ItemLocation {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ItemLocation::Tile(pos) => write!(f, "tile({pos})"),
+ ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"),
+ }
+ }
+}
+
+impl Default for DocumentElement {
+ fn default() -> Self {
+ Self::Document { es: vec![] }
+ }
+}
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs
index 1acf7997..3337db0a 100644
--- a/server/protocol/src/lib.rs
+++ b/server/protocol/src/lib.rs
@@ -21,10 +21,11 @@ use bincode::{
};
use glam::{IVec2, Vec2};
use serde::{Deserialize, Serialize};
-use std::{collections::HashSet, fmt::Display, sync::LazyLock};
+use std::{collections::HashSet, sync::LazyLock};
pub use glam;
+pub mod helpers;
pub mod movement;
pub mod registry;
@@ -290,7 +291,7 @@ pub enum PacketC {
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "snake_case", tag = "menu", content = "data")]
pub enum Menu {
- Book,
+ Document(DocumentElement),
Score(Score),
}
@@ -339,96 +340,6 @@ pub enum Recipe {
},
}
-impl Gamedata {
- pub fn tile_name(&self, index: TileIndex) -> &String {
- &self.tile_names[index.0]
- }
- pub fn is_tile_colliding(&self, index: TileIndex) -> bool {
- self.tile_collide[index.0]
- }
- pub fn is_tile_interactable(&self, index: TileIndex) -> bool {
- self.tile_interact[index.0]
- }
- pub fn item_name(&self, index: ItemIndex) -> &String {
- &self.item_names[index.0]
- }
- pub fn recipe(&self, index: RecipeIndex) -> &Recipe {
- &self.recipes[index.0]
- }
- pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> {
- self.tile_names
- .iter()
- .position(|t| t == name)
- .map(TileIndex)
- }
- pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> {
- self.item_names
- .iter()
- .position(|t| t == name)
- .map(ItemIndex)
- }
- pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> {
- self.recipes
- .iter()
- .enumerate()
- .map(|(i, e)| (RecipeIndex(i), e))
- }
-}
-
-impl Recipe {
- pub fn tile(&self) -> Option<TileIndex> {
- match self {
- Recipe::Passive { tile, .. } => *tile,
- Recipe::Active { tile, .. } => *tile,
- Recipe::Instant { tile, .. } => *tile,
- }
- }
- pub fn speed(&self) -> Option<f32> {
- match self {
- Recipe::Passive { speed, .. } => Some(*speed),
- Recipe::Active { speed, .. } => Some(*speed),
- _ => None,
- }
- }
- pub fn revert_speed(&self) -> Option<f32> {
- match self {
- Recipe::Passive { revert_speed, .. } => *revert_speed,
- _ => None,
- }
- }
- pub fn warn(&self) -> bool {
- match self {
- Recipe::Passive { warn, .. } => *warn,
- _ => false,
- }
- }
- pub fn inputs(&self) -> Vec<ItemIndex> {
- match self {
- Recipe::Passive { input, .. } => vec![*input],
- Recipe::Active { input, .. } => vec![*input],
- Recipe::Instant { inputs, .. } => inputs.iter().flat_map(|e| e.to_owned()).collect(),
- }
- }
- pub fn outputs(&self) -> Vec<ItemIndex> {
- match self {
- Recipe::Passive { output, .. } => output.iter().copied().collect(),
- Recipe::Active { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(),
- Recipe::Instant { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(),
- }
- }
- pub fn supports_tile(&self, tile: Option<TileIndex>) -> bool {
- if let Some(tile_constraint) = self.tile() {
- if let Some(tile) = tile {
- tile == tile_constraint
- } else {
- false
- }
- } else {
- true
- }
- }
-}
-
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum ItemLocation {
@@ -436,11 +347,31 @@ pub enum ItemLocation {
Player(PlayerID),
}
-impl Display for ItemLocation {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- ItemLocation::Tile(pos) => write!(f, "tile({pos})"),
- ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"),
- }
- }
+#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
+#[serde(rename_all = "snake_case", tag = "t")]
+pub enum DocumentElement {
+ Document {
+ es: Vec<DocumentElement>,
+ },
+ Page {
+ background: Option<String>,
+ es: Vec<DocumentElement>,
+ },
+ List {
+ es: Vec<DocumentElement>,
+ },
+ Table {
+ es: Vec<Vec<DocumentElement>>,
+ },
+ Par {
+ es: Vec<DocumentElement>,
+ },
+ Text {
+ s: Message,
+ color: String,
+ size: f32,
+ font: String,
+ #[serde(default)]
+ bold: bool,
+ },
}
diff --git a/server/src/commands.rs b/server/src/commands.rs
index feb6ab65..25b51bff 100644
--- a/server/src/commands.rs
+++ b/server/src/commands.rs
@@ -195,7 +195,7 @@ impl Server {
.await
.map_err(|e| TrError::Plain(e.to_string()))?;
}
- Command::Book => replies.push(PacketC::Menu(Menu::Book)),
+ Command::Book => replies.push(PacketC::Menu(Menu::Document(self.data.book.clone()))),
Command::Download { r#type, name } => {
let source = match r#type {
DownloadType::Map => self.index.read_map(&name).await,
diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs
index 39684c6f..549574c7 100644
--- a/server/src/data/mod.rs
+++ b/server/src/data/mod.rs
@@ -20,12 +20,12 @@ pub mod demands;
pub mod index;
use crate::entity::{construct_entity, Entities, EntityDecl};
-use anyhow::{anyhow, bail, Result};
+use anyhow::{anyhow, bail, Context, Result};
use demands::generate_demands;
use hurrycurry_bot::algos::ALGO_CONSTRUCTORS;
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
- Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex,
+ DocumentElement, Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex,
};
use serde::{Deserialize, Serialize};
use std::{
@@ -87,14 +87,15 @@ pub struct DemandDecl {
points: i64,
}
-#[derive(Debug,Clone, Default)]
+#[derive(Debug, Clone, Default)]
#[rustfmt::skip]
pub struct Serverdata {
pub initial_map: HashMap<IVec2, (TileIndex, Option<ItemIndex>)>,
pub chef_spawn: Vec2,
pub customer_spawn: Vec2,
pub score_baseline: i64,
- pub default_timer: Option<Duration>
+ pub default_timer: Option<Duration>,
+ pub book: DocumentElement
}
#[derive(Debug, Deserialize, Default)]
@@ -145,7 +146,13 @@ impl DataIndex {
.read_recipes(map_in.recipes.as_deref().unwrap_or("default"))
.await?,
)?;
- build_data(&self.maps, map.to_string(), map_in, recipes_in)
+ let book = serde_json::from_str(
+ &read_to_string(data_dir().join("book.json"))
+ .await
+ .context("loading book")?,
+ )
+ .context("invalid book")?;
+ build_data(&self.maps, map.to_string(), map_in, recipes_in, book)
}
}
@@ -154,6 +161,7 @@ pub fn build_data(
map_name: String,
map_in: MapDecl,
recipes_in: Vec<RecipeDecl>,
+ book: DocumentElement,
) -> Result<(Gamedata, Serverdata, Entities)> {
let reg = ItemTileRegistry::default();
let mut recipes = Vec::new();
@@ -304,6 +312,7 @@ pub fn build_data(
chef_spawn,
customer_spawn,
default_timer,
+ book,
score_baseline: map_in.score_baseline,
},
entities,
diff --git a/server/src/entity/book.rs b/server/src/entity/book.rs
index 70197764..8b152bb9 100644
--- a/server/src/entity/book.rs
+++ b/server/src/entity/book.rs
@@ -32,7 +32,7 @@ impl Entity for Book {
) -> Result<bool, TrError> {
if pos == Some(self.0) {
if let Some(r) = c.replies {
- r.push(PacketC::Menu(Menu::Book));
+ r.push(PacketC::Menu(Menu::Document(c.serverdata.book.clone())));
}
return Ok(true);
}
diff --git a/server/src/entity/customers.rs b/server/src/entity/customers.rs
index 1e8b6b13..0c3ced8d 100644
--- a/server/src/entity/customers.rs
+++ b/server/src/entity/customers.rs
@@ -65,6 +65,7 @@ impl Entity for Customers {
for bot in &mut self.customers {
bot.tick(EntityContext {
game: c.game,
+ serverdata: c.serverdata,
packet_out: c.packet_out,
packet_in: c.packet_in,
score_changed: c.score_changed,
diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs
index 87656f0d..044fccab 100644
--- a/server/src/entity/mod.rs
+++ b/server/src/entity/mod.rs
@@ -25,7 +25,11 @@ pub mod item_portal;
pub mod player_portal;
pub mod tutorial;
-use crate::{data::ItemTileRegistry, message::TrError, scoreboard::ScoreboardStore};
+use crate::{
+ data::{ItemTileRegistry, Serverdata},
+ message::TrError,
+ scoreboard::ScoreboardStore,
+};
use anyhow::{anyhow, Result};
use book::Book;
use campaign::{Gate, GateCondition, Map};
@@ -47,6 +51,7 @@ pub type Entities = Vec<DynEntity>;
pub struct EntityContext<'a> {
pub game: &'a mut Game,
+ pub serverdata: &'a Serverdata,
pub packet_out: &'a mut VecDeque<PacketC>,
pub packet_in: &'a mut VecDeque<PacketS>,
pub score_changed: &'a mut bool,
diff --git a/server/src/server.rs b/server/src/server.rs
index 18f78fc0..d3e9ab59 100644
--- a/server/src/server.rs
+++ b/server/src/server.rs
@@ -340,6 +340,7 @@ impl Server {
packet_in: &mut self.packet_loopback,
score_changed: &mut self.score_changed,
scoreboard: &self.scoreboard,
+ serverdata: self.data.as_ref(),
replies: None,
dt: 0.,
load_map: &mut None,
@@ -447,6 +448,7 @@ impl Server {
packet_in: &mut self.packet_loopback,
score_changed: &mut self.score_changed,
load_map: &mut None,
+ serverdata: &self.data,
scoreboard: &self.scoreboard,
replies: Some(replies),
dt: 0.,
@@ -699,6 +701,7 @@ impl Server {
score_changed: &mut self.score_changed,
packet_in: &mut self.packet_loopback,
scoreboard: &self.scoreboard,
+ serverdata: &self.data,
replies: None,
dt,
}) {
@@ -714,6 +717,7 @@ impl Server {
score_changed: &mut self.score_changed,
packet_in: &mut self.packet_loopback,
scoreboard: &self.scoreboard,
+ serverdata: &self.data,
replies: None,
dt: 0.,
});