aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-09-24 00:38:16 +0200
committermetamuffin <metamuffin@disroot.org>2024-09-24 00:38:46 +0200
commite00492214438711c3af7fcad75505539d41e2285 (patch)
treefaf8d44a5282846b537ed347b2f839607aa9df89
parentf1f0ae07172c24deb5815f8ee7926018db6d7dbc (diff)
downloadhurrycurry-e00492214438711c3af7fcad75505539d41e2285.tar
hurrycurry-e00492214438711c3af7fcad75505539d41e2285.tar.bz2
hurrycurry-e00492214438711c3af7fcad75505539d41e2285.tar.zst
pinned orders
-rw-r--r--server/bot/src/algos/customer.rs59
-rw-r--r--server/bot/src/algos/frank.rs1
-rw-r--r--server/protocol/src/lib.rs2
-rw-r--r--server/src/commands.rs7
-rw-r--r--server/src/interaction.rs3
-rw-r--r--server/src/server.rs4
-rw-r--r--server/src/state.rs1
-rw-r--r--test-client/main.ts27
-rw-r--r--test-client/protocol.ts5
-rw-r--r--test-client/visual.ts55
10 files changed, 117 insertions, 47 deletions
diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs
index ec8f2283..e602addc 100644
--- a/server/bot/src/algos/customer.rs
+++ b/server/bot/src/algos/customer.rs
@@ -39,6 +39,8 @@ pub enum Customer {
chair: IVec2,
timeout: f32,
origin: IVec2,
+ check: u8,
+ pinned: bool,
},
Eating {
demand: DemandIndex,
@@ -104,12 +106,15 @@ impl BotAlgo for Customer {
timeout,
demand,
origin: *origin,
+ check: 0,
+ pinned: false,
};
BotInput {
extra: vec![PacketS::Communicate {
message: Some(Message::Item(game.data.demands[demand.0].input)),
timeout: Some(timeout),
player: me,
+ pin: Some(false),
}],
..Default::default()
}
@@ -144,19 +149,23 @@ impl BotAlgo for Customer {
demand,
timeout,
origin,
+ check,
+ pinned,
} => {
*timeout -= dt;
+ *check += 1;
if *timeout <= 0. {
let path = find_path(&game.walkable, pos.as_ivec2(), *origin)
.expect("no path to exit");
info!("{me:?} -> exiting");
*self = Customer::Exiting { path };
- BotInput {
+ return BotInput {
extra: vec![
PacketS::Communicate {
message: None,
timeout: Some(0.),
player: me,
+ pin: Some(false),
},
PacketS::ApplyScore(Score {
points: -1,
@@ -169,9 +178,32 @@ impl BotAlgo for Customer {
},
],
..Default::default()
- }
- } else {
+ };
+ } else if *check > 10 {
let demand_data = &game.data.demands[demand.0];
+ *check = 0;
+
+ if !*pinned {
+ let mut pin = false;
+ game.players_spatial_index.query(pos, 3., |pid, _| {
+ if game.players.get(&pid).map_or(false, |p| p.character >= 0) {
+ pin = true
+ }
+ });
+ if pin {
+ *pinned = true;
+ return BotInput {
+ extra: vec![PacketS::Communicate {
+ player: me,
+ message: Some(Message::Item(demand_data.input)),
+ timeout: Some(*timeout),
+ pin: Some(true),
+ }],
+ ..Default::default()
+ };
+ }
+ }
+
let demand_pos = [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y]
.into_iter()
.find_map(|off| {
@@ -201,12 +233,19 @@ impl BotAlgo for Customer {
chair: *chair,
origin: *origin,
};
- BotInput {
+ return BotInput {
extra: vec![
PacketS::Communicate {
message: None,
timeout: Some(0.),
player: me,
+ pin: Some(false),
+ },
+ PacketS::Communicate {
+ message: None,
+ timeout: Some(0.),
+ player: me,
+ pin: Some(true),
},
PacketS::Effect {
name: "satisfied".to_string(),
@@ -222,15 +261,15 @@ impl BotAlgo for Customer {
},
],
..Default::default()
- }
- } else {
- BotInput {
- direction: (chair.as_vec2() + 0.5) - pos,
- ..Default::default()
- }
+ };
}
}
+ BotInput {
+ direction: (chair.as_vec2() + 0.5) - pos,
+ ..Default::default()
+ }
}
+
Customer::Eating {
demand,
target,
diff --git a/server/bot/src/algos/frank.rs b/server/bot/src/algos/frank.rs
index 95718d4c..854c73bb 100644
--- a/server/bot/src/algos/frank.rs
+++ b/server/bot/src/algos/frank.rs
@@ -84,6 +84,7 @@ impl BotAlgo for Frank {
player: me,
message: Some(Message::Text(message)),
timeout: Some(3.),
+ pin: Some(false),
}],
..Default::default()
};
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs
index b826edae..0203107d 100644
--- a/server/protocol/src/lib.rs
+++ b/server/protocol/src/lib.rs
@@ -118,6 +118,7 @@ pub enum PacketS {
player: PlayerID,
message: Option<Message>,
timeout: Option<f32>,
+ pin: Option<bool>,
},
/// For use in replay sessions only
@@ -266,6 +267,7 @@ pub enum Menu {
pub struct MessageTimeout {
pub remaining: f32,
pub initial: f32,
+ pub pinned: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Default)]
diff --git a/server/src/commands.rs b/server/src/commands.rs
index c03c8bcd..6fd95eac 100644
--- a/server/src/commands.rs
+++ b/server/src/commands.rs
@@ -23,7 +23,7 @@ use crate::{
use anyhow::{anyhow, bail, Result};
use clap::{Parser, ValueEnum};
use hurrycurry_bot::algos::ALGO_CONSTRUCTORS;
-use hurrycurry_protocol::{Menu, Message, MessageTimeout, PacketC, PlayerID};
+use hurrycurry_protocol::{Menu, Message, PacketC, PlayerID};
use std::{fmt::Write, time::Duration};
#[derive(Parser)]
@@ -294,10 +294,7 @@ impl Server {
id: message_id,
params: arguments.into_iter().map(|c| Message::Text(c)).collect(),
}),
- timeout: Some(MessageTimeout {
- initial: 5.,
- remaining: 5.,
- }),
+ timeout: None,
});
}
}
diff --git a/server/src/interaction.rs b/server/src/interaction.rs
index 7a8c2e9d..74d31cd8 100644
--- a/server/src/interaction.rs
+++ b/server/src/interaction.rs
@@ -15,13 +15,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::data::index::GamedataIndex;
use hurrycurry_client_lib::{Involvement, Item};
use hurrycurry_protocol::{Gamedata, ItemLocation, PacketC, PlayerID, Recipe, Score, TileIndex};
use log::info;
use std::collections::VecDeque;
-use crate::data::index::GamedataIndex;
-
#[allow(clippy::too_many_arguments)]
pub fn interact(
data: &Gamedata,
diff --git a/server/src/server.rs b/server/src/server.rs
index 06b6e5fd..0c3df634 100644
--- a/server/src/server.rs
+++ b/server/src/server.rs
@@ -528,8 +528,10 @@ impl Server {
message,
timeout,
player,
+ pin,
} => {
info!("{player:?} message {message:?}");
+ let pin = pin.unwrap_or(false);
if let Some(timeout) = timeout {
if let Some(player) = self.game.players.get_mut(&player) {
player.communicate_persist = message.clone().map(|m| {
@@ -538,6 +540,7 @@ impl Server {
MessageTimeout {
initial: timeout,
remaining: timeout,
+ pinned: pin,
},
)
});
@@ -549,6 +552,7 @@ impl Server {
timeout: timeout.map(|t| MessageTimeout {
initial: t,
remaining: t,
+ pinned: pin,
}),
})
}
diff --git a/server/src/state.rs b/server/src/state.rs
index 9009d4a1..65e6dfd2 100644
--- a/server/src/state.rs
+++ b/server/src/state.rs
@@ -53,6 +53,7 @@ impl Server {
message: Some(Message::Text(text)),
timeout: None,
player,
+ ..
} if let Some(command) = text.strip_prefix("/") => {
match self.handle_command_parse(*player, command).await {
Ok(packets) => return Ok(packets),
diff --git a/test-client/main.ts b/test-client/main.ts
index a3bf0105..5d3a6d11 100644
--- a/test-client/main.ts
+++ b/test-client/main.ts
@@ -88,6 +88,7 @@ export interface PlayerData extends MovementBase {
character: number,
anim_position: V2,
message_persist?: MessageData,
+ message_pinned?: MessageData,
message?: MessageData,
}
@@ -97,8 +98,9 @@ export interface TileData {
kind: TileIndex
item?: ItemData
}
+export type MessageStyle = "hint" | "normal" | "error" | "pinned"
export interface MessageData {
- style: "hint" | "normal" | "error"
+ style: MessageStyle
inner: Message
anim_position: V2,
anim_size: number,
@@ -120,7 +122,7 @@ export let camera_scale = 0.05;
export const interact_target_anim: V2 = { x: 0, y: 0 }
export let interact_possible_anim: number = 0
export let interact_active_anim: number = 0
-export let nametag_scale_anim: number = 0
+export let overlay_vis_anim: number = 0
export let is_lobby = false
let interacting: V2 | undefined;
let last_server_sent_position: V2 = { x: 0, y: 0 }
@@ -226,11 +228,18 @@ function packet(p: PacketC) {
inner: p.message,
anim_size: 0.,
anim_position: player.anim_position,
- timeout: p.timeout ?? { initial: 5, remaining: 5 },
+ timeout: p.timeout ?? { initial: 5, remaining: 5, pinned: false },
style: "normal" as const
};
if (p.timeout === undefined) player.message = message
else player.message_persist = message
+ if (p.timeout?.pinned) player.message_pinned = {
+ inner: p.message,
+ anim_size: 0,
+ anim_position: { x: 20, y: 0 },
+ style: "pinned",
+ timeout: p.timeout
+ }
} else if (p.timeout !== undefined) {
delete player.message_persist
}
@@ -248,7 +257,7 @@ function packet(p: PacketC) {
style: p.error ? "error" : "normal",
anim_size: 0.,
anim_position: { x: 0, y: 0 },
- timeout: { initial: 5, remaining: 5 }
+ timeout: { initial: 5, remaining: 5, pinned: false }
}
break;
case "set_ingame":
@@ -286,7 +295,7 @@ function packet(p: PacketC) {
case "book": open("https://s.metamuffin.org/static/hurrycurry/book.pdf"); break
case "score":
global_message = {
- timeout: { initial: 5, remaining: 5 },
+ timeout: { initial: 5, remaining: 5, pinned: false },
inner: { text: `Score: ${JSON.stringify(p.data, null, 4)}` },
anim_position: { x: 0, y: 0 },
anim_size: 0,
@@ -391,16 +400,18 @@ function frame_update(dt: number) {
if (item.active) item.active.position += item.active.speed * dt
}
+ let pin_xo = 0
for (const [pid, player] of players) {
if (pid == my_id) player.anim_position.x = player.position.x, player.anim_position.y = player.position.y
else lerp_exp_v2_mut(player.anim_position, player.position, dt * 15)
if (player.item !== undefined && player.item !== null) update_item(player.item)
if (player.message && tick_message(player.message, dt)) delete player.message
if (player.message_persist && tick_message(player.message_persist, dt)) delete player.message_persist
+ if (player.message_pinned && tick_message(player.message_pinned, dt)) delete player.message_pinned
+ if (player.message_pinned) lerp_exp_v2_mut(player.message_pinned.anim_position, { x: pin_xo++, y: 0 }, dt * 5)
}
- for (const [_, tile] of tiles) {
+ for (const [_, tile] of tiles)
if (tile.item !== undefined && tile.item !== null) update_item(tile.item)
- }
const remove: ItemData[] = []
for (const item of items_removed) {
@@ -426,7 +437,7 @@ function frame_update(dt: number) {
const zoom_target = Math.min(canvas.width, canvas.height) * (keys_down.has("KeyL") ? 0.05 : 0.1)
camera_scale = lerp_exp(camera_scale, zoom_target, dt * 5)
- nametag_scale_anim = lerp_exp(nametag_scale_anim, +keys_down.has("KeyL"), dt * 10)
+ overlay_vis_anim = lerp_exp(overlay_vis_anim, +keys_down.has("KeyL"), dt * 10)
tick_particles(dt)
}
diff --git a/test-client/protocol.ts b/test-client/protocol.ts
index c8c59ed2..64d740c3 100644
--- a/test-client/protocol.ts
+++ b/test-client/protocol.ts
@@ -40,7 +40,7 @@ export type PacketS =
| { type: "leave", player: PlayerID } // Despawns a character
| { type: "movement", player: PlayerID, pos: Vec2, dir: Vec2, boost: boolean }
| { type: "interact", player: PlayerID, pos?: Vec2 } // Interact with some tile. pos is a position when pressing and null when releasing interact button
- | { type: "communicate", player: PlayerID, message?: Message, timeout?: number } // Sends a message
+ | { type: "communicate", player: PlayerID, message?: Message, timeout?: number, pin?: boolean } // Sends a message
| { type: "effect", player: PlayerID, name: string } // Sends an effect
| { type: "replay_tick", dt: number } // Steps forward in replay.
@@ -73,7 +73,8 @@ export type Menu =
export interface MessageTimeout {
initial: number,
- remaining: number
+ remaining: number,
+ pinned: boolean,
}
export interface Score {
diff --git a/test-client/visual.ts b/test-client/visual.ts
index 9a267da0..784a36a3 100644
--- a/test-client/visual.ts
+++ b/test-client/visual.ts
@@ -16,7 +16,7 @@
*/
import { tr } from "./locale.ts";
-import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, is_lobby, items_removed, keys_down, my_id, nametag_scale_anim, players, score, server_hints, tiles } from "./main.ts";
+import { ItemData, MessageData, MessageStyle, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, is_lobby, items_removed, keys_down, my_id, overlay_vis_anim, players, score, server_hints, tiles } from "./main.ts";
import { PLAYER_SIZE } from "./movement.ts";
import { draw_item_sprite, draw_tile_sprite, ItemName, TileName } from "./tiles.ts";
import { V2, ceil_v2, floor_v2 } from "./util.ts";
@@ -67,16 +67,24 @@ export function draw_ingame() {
draw_interact_target()
for (const [_, player] of players)
- if (player.message) draw_message(player.message)
+ if (player.message) draw_message(player.message, true)
for (const [_, player] of players)
- if (player.message_persist) draw_message(player.message_persist)
+ if (player.message_persist) draw_message(player.message_persist, true)
for (const [_, player] of players)
draw_player_nametag(player)
for (const [_, message] of server_hints)
- draw_message(message)
+ draw_message(message, true)
+
+ ctx.restore()
+ ctx.save()
+ ctx.translate(50, 50)
+ ctx.scale(80, 80)
+
+ for (const [_, player] of players)
+ if (player.message_pinned) draw_message(player.message_pinned, false)
ctx.restore()
@@ -143,13 +151,13 @@ function draw_player(player: PlayerData) {
}
function draw_player_nametag(player: PlayerData) {
- if (nametag_scale_anim > 0.01) {
+ if (overlay_vis_anim > 0.01) {
ctx.save()
ctx.translate(player.anim_position.x, player.anim_position.y)
ctx.translate(0, -1)
ctx.textAlign = "center"
ctx.font = "15px sans-serif"
- ctx.scale(nametag_scale_anim / camera_scale, nametag_scale_anim / camera_scale)
+ ctx.scale(overlay_vis_anim / camera_scale, overlay_vis_anim / camera_scale)
const w = ctx.measureText(player.name).width + 20
ctx.fillStyle = "#fffa"
ctx.beginPath()
@@ -221,20 +229,27 @@ function message_str(m: Message): string {
return "[unknown message type]"
}
-const MESSAGE_BG = { "normal": "#fff", "hint": "#111", "error": "#fff" }
-const MESSAGE_FG = { "normal": "#000", "hint": "#fff", "error": "#a00" }
-function draw_message(m: MessageData, server?: boolean) {
+const MESSAGE_BG: { [key in MessageStyle]: string } = { normal: "#fff", hint: "#111", error: "#fff", pinned: "rgb(4, 32, 0)" }
+const MESSAGE_FG: { [key in MessageStyle]: string } = { normal: "#000", hint: "#fff", error: "#a00", pinned: "#000" }
+function draw_message(m: MessageData, world: boolean) {
ctx.save()
ctx.translate(m.anim_position.x, m.anim_position.y)
- const scale = Math.min(m.anim_size, 1 - nametag_scale_anim);
+ const scale = Math.min(m.anim_size, 1 - overlay_vis_anim);
ctx.scale(scale, scale)
if ("item" in m.inner) {
- ctx.fillStyle = "#fffa"
- ctx.beginPath()
- ctx.moveTo(0, -0.3)
- ctx.arc(0, -1, 0.5, Math.PI / 4, Math.PI - Math.PI / 4, true)
- ctx.closePath()
- ctx.fill()
+ ctx.fillStyle = MESSAGE_BG[m.style]
+ if (m.style == "pinned") {
+ ctx.translate(0, 1)
+ ctx.beginPath()
+ ctx.arc(0, -1, 0.5, 0, Math.PI * 2)
+ ctx.fill()
+ } else {
+ ctx.beginPath()
+ ctx.moveTo(0, -0.3)
+ ctx.arc(0, -1, 0.5, Math.PI / 4, Math.PI - Math.PI / 4, true)
+ ctx.closePath()
+ ctx.fill()
+ }
if (m.timeout) {
const t = m.timeout.remaining / m.timeout.initial;
@@ -253,13 +268,13 @@ function draw_message(m: MessageData, server?: boolean) {
ctx.translate(0, -1)
ctx.textAlign = "center"
- ctx.font = "15px " + (server ? "monospace" : "sans-serif")
- if (!server) ctx.scale(2 / camera_scale, 2 / camera_scale)
+ ctx.font = "15px " + (world ? "sans-serif" : "monospace")
+ if (world) ctx.scale(2 / camera_scale, 2 / camera_scale)
const lines = message_str(m.inner).split("\n")
const w = lines.reduce((a, v) => Math.max(a, ctx.measureText(v).width), 0) + 10
- if (!server) ctx.translate(0, -lines.length * 15 / 2)
+ if (world) ctx.translate(0, -lines.length * 15 / 2)
ctx.fillStyle = MESSAGE_BG[m.style]
ctx.beginPath()
@@ -282,7 +297,7 @@ function draw_global_message() {
ctx.save()
ctx.translate(canvas.width / 2, canvas.height / 6)
ctx.scale(2, 2)
- draw_message(global_message, true)
+ draw_message(global_message, false)
ctx.restore()
}