From 1a9f7e9a20bff0fbc854454f131d4bc546e73e72 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Dec 2024 15:37:11 +0100 Subject: two-handed server --- pixel-client/src/game.rs | 34 +++++++---- server/bot/src/algos/customer.rs | 13 +++- server/bot/src/algos/simple.rs | 13 ++-- server/bot/src/main.rs | 3 +- server/client-lib/src/lib.rs | 21 ++++--- server/protocol/src/helpers.rs | 30 ++++++++- server/protocol/src/lib.rs | 10 ++- server/src/entity/bot.rs | 3 +- server/src/entity/tutorial.rs | 4 +- server/src/server.rs | 127 ++++++++++++++++++++++----------------- 10 files changed, 167 insertions(+), 91 deletions(-) diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs index e7754366..4dc43b72 100644 --- a/pixel-client/src/game.rs +++ b/pixel-client/src/game.rs @@ -30,8 +30,8 @@ use hurrycurry_client_lib::{network::sync::Network, spatial_index::SpatialIndex, use hurrycurry_protocol::{ glam::{IVec2, Vec2}, movement::MovementBase, - Gamedata, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS, PlayerClass, - PlayerID, RecipeIndex, Score, TileIndex, + Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS, + PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, }; use log::{info, warn}; use sdl2::{ @@ -67,7 +67,7 @@ pub struct Tile { pub struct Player { movement: MovementBase, - item: Option, + items: [Option; 2], message_persist: Option<(Message, MessageTimeout)>, _name: String, _character: i32, @@ -156,11 +156,13 @@ impl Game { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: Some(self.players[&self.my_id].movement.get_interact_target()), + hand: Hand::Left, }); } else { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: None, + hand: Hand::Left, }); } self.interacting = interact; @@ -209,9 +211,11 @@ impl Game { }); for player in self.players.values_mut() { - if let Some(item) = &mut player.item { - item.parent_position = player.movement.position; - item.tick(1., dt); + for item in &mut player.items { + if let Some(item) = item { + item.parent_position = player.movement.position; + item.tick(1., dt); + } } } for tile in self.tiles.values_mut() { @@ -292,7 +296,7 @@ impl Game { _character: character, _name: name, message_persist: None, - item: None, + items: [const { None }; 2], movement: MovementBase { position, input_direction: Vec2::ZERO, @@ -337,7 +341,9 @@ impl Game { let position = self.get_location_position(location); let slot = match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } }; self.items_removed.extend(slot.take()); *slot = item.map(|kind| Item { @@ -389,13 +395,15 @@ impl Game { pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } } } pub fn get_location_position(&self, location: ItemLocation) -> Vec2 { match location { ItemLocation::Tile(pos) => pos.as_vec2() + 0.5, - ItemLocation::Player(p) => self.players[&p].movement.position, + ItemLocation::Player(p, _) => self.players[&p].movement.position, } } @@ -494,8 +502,10 @@ impl Player { _ => (), } } - if let Some(item) = &self.item { - item.draw(ctx, item_sprites) + for item in &self.items { + if let Some(item) = item { + item.draw(ctx, item_sprites) + } } } } diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs index b243bd55..826ab534 100644 --- a/server/bot/src/algos/customer.rs +++ b/server/bot/src/algos/customer.rs @@ -22,7 +22,7 @@ use crate::{ use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, - DemandIndex, Message, PacketS, PlayerClass, PlayerID, Score, + DemandIndex, Hand, Message, PacketS, PlayerClass, PlayerID, Score, }; use log::info; use rand::{random, seq::IndexedRandom, thread_rng}; @@ -313,6 +313,7 @@ impl CustomerState { PacketS::Interact { pos: Some(pos), player: me, + hand: Hand::Left, }, PacketS::ApplyScore(Score { demands_completed: 1, @@ -322,6 +323,7 @@ impl CustomerState { PacketS::Interact { pos: None, player: me, + hand: Hand::Left, }, ], ..Default::default() @@ -354,6 +356,7 @@ impl CustomerState { extra: vec![PacketS::ReplaceHand { player: me, item: demand.output, + hand: Hand::Left, }], ..Default::default() }; @@ -369,7 +372,11 @@ impl CustomerState { cooldown, } => { *cooldown -= dt; - if game.players.get(&me).is_some_and(|pl| pl.item.is_none()) { + if game + .players + .get(&me) + .is_some_and(|pl| pl.items[Hand::Left.index()].is_none()) + { if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), *origin) { *self = CustomerState::Exiting { path }; } @@ -383,10 +390,12 @@ impl CustomerState { PacketS::Interact { player: me, pos: Some(*table), + hand: Hand::Left, }, PacketS::Interact { player: me, pos: None, + hand: Hand::Left, }, ], direction, diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index 14eb38c4..5a7e61a0 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -21,7 +21,7 @@ use crate::{ }; use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ - glam::IVec2, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, + glam::IVec2, Hand, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, }; use log::{debug, warn}; @@ -106,16 +106,17 @@ impl State for Simple { impl Context<'_, S> { pub fn is_hand_item(&self, item: ItemIndex) -> bool { - self.game - .players - .get(&self.me) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + self.game.players.get(&self.me).is_some_and(|p| { + p.items[Hand::Left.index()] + .as_ref() + .is_some_and(|i| i.kind == item) + }) } pub fn is_hand_occupied(&self) -> bool { self.game .players .get(&self.me) - .map(|p| p.item.is_some()) + .map(|p| p.items[Hand::Left.index()].is_some()) .unwrap_or(false) } pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 61ae1c1c..0ffbf4b6 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use clap::Parser; use hurrycurry_bot::{algos::ALGO_CONSTRUCTORS, BotAlgo, BotInput}; use hurrycurry_client_lib::{network::sync::Network, Game}; -use hurrycurry_protocol::{PacketC, PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketC, PacketS, PlayerClass, PlayerID}; use log::warn; use std::{thread::sleep, time::Duration}; @@ -109,6 +109,7 @@ fn main() -> Result<()> { network.queue_out.push_back(PacketS::Interact { player: b.id, pos: interact, + hand: Hand::Left, }) } network.queue_out.push_back(PacketS::Movement { diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs index 5d5e55d5..54c7cd6e 100644 --- a/server/client-lib/src/lib.rs +++ b/server/client-lib/src/lib.rs @@ -20,8 +20,7 @@ pub mod network; pub mod spatial_index; use hurrycurry_protocol::{ - glam::IVec2, movement::MovementBase, Gamedata, ItemIndex, ItemLocation, Message, - MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, + glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex }; use spatial_index::SpatialIndex; use std::{ @@ -54,8 +53,8 @@ pub struct Player { pub name: String, pub class: PlayerClass, pub character: i32, - pub interacting: Option, - pub item: Option, + pub interacting: Option<(IVec2, Hand)>, + pub items: [Option; 2], pub communicate_persist: Option<(Message, MessageTimeout)>, pub movement: MovementBase, @@ -95,7 +94,7 @@ impl Game { character, class, interacting: None, - item: None, + items: [const { None }; 2], communicate_persist: None, movement: MovementBase::new(position), }, @@ -200,9 +199,11 @@ impl Game { } for player in self.players.values_mut() { - if let Some(item) = &mut player.item { - if let Some(active) = &mut item.active { - active.position += active.speed; + for item in &mut player.items { + if let Some(item) = item { + if let Some(active) = &mut item.active { + active.position += active.speed; + } } } } @@ -226,7 +227,9 @@ impl Game { pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } } } } diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs index 924d0886..21835101 100644 --- a/server/protocol/src/helpers.rs +++ b/server/protocol/src/helpers.rs @@ -1,7 +1,8 @@ use std::fmt::Display; use crate::{ - DocumentElement, Gamedata, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, TileIndex, + DocumentElement, Gamedata, Hand, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, + TileIndex, }; impl Gamedata { @@ -98,7 +99,32 @@ 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})"), + ItemLocation::Player(PlayerID(id), hand) => write!(f, "player({id}_{hand})"), + } + } +} + +impl Display for Hand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Hand::Left => "left", + Hand::Right => "right", + }) + } +} + +impl Hand { + pub fn index(&self) -> usize { + match self { + Hand::Left => 0, + Hand::Right => 1, + } + } + pub fn from_index(i: usize) -> Self { + match i { + 0 => Hand::Left, + 1 => Hand::Right, + _ => Hand::Left, } } } diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 2ef07015..6ccd2367 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -71,6 +71,12 @@ pub struct RecipeIndex(pub usize); #[serde(transparent)] pub struct DemandIndex(pub usize); +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] +pub enum Hand { + Left, + Right, +} + #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct MapMetadata { pub name: String, @@ -124,6 +130,7 @@ pub enum PacketS { }, Interact { player: PlayerID, + hand: Hand, #[bincode(with_serde)] pos: Option, }, @@ -144,6 +151,7 @@ pub enum PacketS { /// For internal use only (customers) ReplaceHand { player: PlayerID, + hand: Hand, item: Option, }, #[serde(skip)] @@ -344,7 +352,7 @@ pub enum Recipe { #[serde(rename_all = "snake_case")] pub enum ItemLocation { Tile(#[bincode(with_serde)] IVec2), - Player(PlayerID), + Player(PlayerID, Hand), } #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] diff --git a/server/src/entity/bot.rs b/server/src/entity/bot.rs index fe4d711f..368f8c9d 100644 --- a/server/src/entity/bot.rs +++ b/server/src/entity/bot.rs @@ -18,7 +18,7 @@ use super::{Entity, EntityContext}; use anyhow::Result; use hurrycurry_bot::{BotAlgo, DynBotAlgo}; -use hurrycurry_protocol::{PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketS, PlayerClass, PlayerID}; use log::info; use rand::random; use std::any::Any; @@ -72,6 +72,7 @@ impl Entity for BotDriver { c.packet_in.push_back(PacketS::Interact { player: self.id, pos: input.interact, + hand: Hand::Left, }) } c.packet_in.push_back(PacketS::Movement { diff --git a/server/src/entity/tutorial.rs b/server/src/entity/tutorial.rs index 44244862..33c0e507 100644 --- a/server/src/entity/tutorial.rs +++ b/server/src/entity/tutorial.rs @@ -144,7 +144,7 @@ impl StepContext<'_> { .game .players .get(&self.player) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item)) } pub fn find_demand(&self, item: ItemIndex) -> Option { self.ent @@ -228,7 +228,7 @@ impl StepContext<'_> { .game .players .get(&self.player) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item)) { if let Some(pos) = self.find_demand(item) { Err((Some(pos), trm!("s.tutorial.serve"))) diff --git a/server/src/server.rs b/server/src/server.rs index 9928ac90..9b90d6b3 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -28,8 +28,8 @@ use hurrycurry_client_lib::{Game, Involvement, Item, Player, Tile}; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, movement::MovementBase, - Gamedata, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, Score, - TileIndex, + Gamedata, Hand, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, + Score, TileIndex, }; use log::{info, warn}; use rand::random; @@ -163,26 +163,29 @@ impl GameServerExt for Game { character: player.character, name: player.name.clone(), }); - if let Some(item) = &player.item { - out.push(PacketC::SetItem { - location: ItemLocation::Player(id), - item: Some(item.kind), - }); - if let Some(Involvement { - player, - position, - speed, - warn, - .. - }) = item.active - { - out.push(PacketC::SetProgress { + for (i, item) in player.items.iter().enumerate() { + let hand = Hand::from_index(i); + if let Some(item) = &item { + out.push(PacketC::SetItem { + location: ItemLocation::Player(id, hand), + item: Some(item.kind), + }); + if let Some(Involvement { player, - item: ItemLocation::Player(id), position, speed, warn, - }); + .. + }) = item.active + { + out.push(PacketC::SetProgress { + player, + item: ItemLocation::Player(id, hand), + position, + speed, + warn, + }); + } } } if let Some((message, timeout)) = &player.communicate_persist { @@ -252,7 +255,7 @@ impl GameServerExt for Game { self.players.insert( id, Player { - item: None, + items: [const { None }; 2], character, class, movement: MovementBase::new(position), @@ -399,15 +402,18 @@ impl Server { self.game.players_spatial_index.remove_entry(player); - if let Some(item) = p.item { - let pos = p.movement.position.floor().as_ivec2(); - if let Some(tile) = self.game.tiles.get_mut(&pos) { - if tile.item.is_none() { - self.packet_out.push_back(PacketC::SetItem { - location: ItemLocation::Tile(pos), - item: Some(item.kind), - }); - tile.item = Some(item); + // TODO if holding two, one is destroyed + for item in p.items { + if let Some(item) = item { + let pos = p.movement.position.floor().as_ivec2(); + if let Some(tile) = self.game.tiles.get_mut(&pos) { + if tile.item.is_none() { + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), + item: Some(item.kind), + }); + tile.item = Some(item); + } } } } @@ -445,7 +451,7 @@ impl Server { } } } - PacketS::Interact { pos, player } => { + PacketS::Interact { pos, player, hand } => { for e in &mut self.entities { if e.interact( EntityContext { @@ -473,7 +479,9 @@ impl Server { .get_mut(&pid) .ok_or(tre!("s.error.no_player"))?; - let (pos, edge) = match (pos, player.interacting) { + let pos = pos.map(|p| (p, hand)); + + let ((pos, hand), edge) = match (pos, player.interacting) { (None, None) => return Ok(()), // this is silent because of auto release (None, Some(pos)) => (pos, false), (Some(pos), None) => (pos, true), @@ -493,7 +501,7 @@ impl Server { // No going back from here on - player.interacting = if edge { Some(pos) } else { None }; + player.interacting = if edge { Some((pos, hand)) } else { None }; let other_pid = if !self.game.data.is_tile_interactable(tile.kind) { self.game @@ -524,10 +532,10 @@ impl Server { edge, None, Some(pid), - &mut this.item, - ItemLocation::Player(base_pid), - &mut other.item, - ItemLocation::Player(pid), + &mut this.items[hand.index()], + ItemLocation::Player(base_pid, hand), + &mut other.items[hand.index()], + ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, false, @@ -547,8 +555,8 @@ impl Server { Some(pid), &mut tile.item, ItemLocation::Tile(pos), - &mut player.item, - ItemLocation::Player(pid), + &mut player.items[hand.index()], + ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, false, @@ -588,14 +596,14 @@ impl Server { timeout, }); } - PacketS::ReplaceHand { item, player } => { + PacketS::ReplaceHand { item, player, hand } => { let pdata = self.game.players.get_mut(&player).ok_or(tre!(""))?; - pdata.item = item.map(|i| Item { + pdata.items[hand.index()] = item.map(|i| Item { kind: i, active: None, }); self.packet_out.push_back(PacketC::SetItem { - location: ItemLocation::Player(player), + location: ItemLocation::Player(player, hand), item, }) } @@ -661,17 +669,19 @@ impl Server { rot: player.movement.rotation, }); - tick_slot( - dt, - &self.game.data, - &self.gamedata_index, - None, - &mut player.item, - ItemLocation::Player(pid), - &mut self.game.score, - &mut self.score_changed, - &mut self.packet_out, - ); + for (i, item) in player.items.iter_mut().enumerate() { + tick_slot( + dt, + &self.game.data, + &self.gamedata_index, + None, + item, + ItemLocation::Player(pid, Hand::from_index(i)), + &mut self.game.score, + &mut self.score_changed, + &mut self.packet_out, + ); + } } let mut players_auto_release = Vec::new(); @@ -682,20 +692,27 @@ impl Server { player.communicate_persist = None; } } - if let Some(pos) = player.interacting { + if let Some((pos, hand)) = player.interacting { if let Some(tile) = self.game.tiles.get(&pos) { if let Some(item) = &tile.item { if let Some(involvement) = &item.active { if involvement.position >= 1. { - players_auto_release.push(*pid); + players_auto_release.push((*pid, hand)); } } } } } } - for player in players_auto_release.drain(..) { - let _ = self.packet_in(PacketS::Interact { pos: None, player }, &mut vec![]); + for (player, hand) in players_auto_release.drain(..) { + let _ = self.packet_in( + PacketS::Interact { + pos: None, + player, + hand, + }, + &mut vec![], + ); } let mut load_map = None; -- cgit v1.2.3-70-g09d2 From cec07a36025d12a0344f19d1ca4c8e51f007b796 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Dec 2024 16:41:57 +0100 Subject: two-handed mostly works --- client/game.gd | 29 ++++++------ client/global.gd | 5 ++ .../communicate/popup_message/popup_message.gd | 2 +- client/multiplayer.gd | 3 +- client/player/controllable_player.gd | 35 +++++++------- client/player/onscreen_controls/controls.gd | 4 +- client/player/player.gd | 54 ++++++++++++---------- client/project.godot | 8 +++- server/protocol/src/lib.rs | 1 + 9 files changed, 82 insertions(+), 59 deletions(-) diff --git a/client/game.gd b/client/game.gd index e7d39aa2..850d539e 100644 --- a/client/game.gd +++ b/client/game.gd @@ -128,8 +128,9 @@ func handle_packet(p): if p.id == player_id: set_join_state(JoinState.SPECTATING) camera.target = $Center - if player.hand != null: - player.hand.queue_free() + for h in player.hand: + if h != null: + h.queue_free() players.erase(p.id) player.queue_free() update_players.emit(players) @@ -143,13 +144,13 @@ func handle_packet(p): player_instance.position_ = last_position "move_item": if "player" in p.from and "player" in p.to: - players[p.from.player].pass_to(players[p.to.player]) + players[p.from.player[0]].pass_to(players[p.to.player[0]], p.from.player[1], p.to.player[1]) elif "tile" in p.from and "player" in p.to: var t: Tile = map.get_tile_instance(p.from.tile) - players[p.to.player].take_item(t) + players[p.to.player[0]].take_item(t, p.to.player[1]) elif "player" in p.from and "tile" in p.to: var t: Tile = map.get_tile_instance(p.to.tile) - players[p.from.player].put_item(t) + players[p.from.player[0]].put_item(t, p.from.player[1]) elif "tile" in p.from and "tile" in p.to: var from_tile2: Tile = map.get_tile_instance(p.from.tile) var to_tile2: Tile = map.get_tile_instance(p.to.tile) @@ -159,7 +160,7 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.progress(p.position, p.speed, p.warn, players.get(p.player)) else: - players[p.item.player].progress(p.position, p.speed, p.warn) + players[p.item.player[0]].progress(p.position, p.speed, p.warn, p.item.player[1]) "clear_progress": if "tile" in p.item: var t: Tile = map.get_tile_instance(p.item.tile) @@ -177,21 +178,23 @@ func handle_packet(p): i.name = item_names[p.item] t.set_item(i) else: - var pl: Player = players[p.location.player] - var i = ItemFactory.produce(item_names[p.item], pl.hand_base) - i.position = pl.hand_base.global_position + var pl: Player = players[p.location.player[0]] + var h = p.location.player[1] + var i = ItemFactory.produce(item_names[p.item], pl.hand_base[G.hand_to_index(h)]) + i.position = pl.hand_base[G.hand_to_index(h)].global_position add_child(i) i.name = item_names[p.item] - pl.set_item(i) + pl.set_item(i, h) else: if "tile" in p.location: var t: Tile = map.get_tile_instance(p.location.tile) t.finish() t.set_item(null) else: - var player: Player = players[p.location.player] - player.finish() - player.set_item(null) + var pl: Player = players[p.location.player[0]] + var h = p.location.player[1] + pl.finish() + pl.set_item(null, h) "update_map": var neighbors: Array = p["neighbors"] if p.kind != null: diff --git a/client/global.gd b/client/global.gd index 374f0e44..ce24f85d 100644 --- a/client/global.gd +++ b/client/global.gd @@ -268,3 +268,8 @@ func configure_viewport_aa(vp: Viewport, aa: String) -> void: "ms4x": vp.msaa_3d = Viewport.MSAA_4X vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + +static func hand_to_index(h): + match h: + "left": return 0 + "right": return 1 diff --git a/client/menu/communicate/popup_message/popup_message.gd b/client/menu/communicate/popup_message/popup_message.gd index 98bd94e3..56c8961a 100644 --- a/client/menu/communicate/popup_message/popup_message.gd +++ b/client/menu/communicate/popup_message/popup_message.gd @@ -141,7 +141,7 @@ func _input(_event): Global.set_hint("has_rotated", true) if any_action_just_pressed(["zoom_in", "zoom_out"]): Global.set_hint("has_zoomed", true) - if Input.is_action_just_pressed("interact"): + if Input.is_action_just_pressed("interact_left") or Input.is_action_just_pressed("interact_right"): Global.set_hint("has_interacted", true) if Input.is_action_just_pressed("reset"): Global.set_hint("has_reset", true) diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 57031221..14eaf482 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -106,11 +106,12 @@ func send_movement(player, pos: Vector2, direction: Vector2, boost: bool): "boost": boost }) -func send_tile_interact(player, pos: Vector2i, edge: bool): +func send_tile_interact(player, pos: Vector2i, edge: bool, hand: String): @warning_ignore("incompatible_ternary") send_packet({ "type": "interact", "player": player, + "hand": hand, "pos": [pos.x, pos.y] if edge else null, }) diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd index 9db36ad4..d241bc2e 100644 --- a/client/player/controllable_player.gd +++ b/client/player/controllable_player.gd @@ -83,7 +83,7 @@ func _process_movement(delta): var boost = Input.is_action_pressed("boost") or (Global.get_setting("gameplay.latch_boost") and boosting) - if Input.is_action_pressed("interact") or Input.is_action_just_released("interact"): + if Input.is_action_pressed("interact_left") or Input.is_action_just_released("interact_left") or Input.is_action_pressed("interact_right") or Input.is_action_just_released("interact_right"): input *= 0 else: target = Vector2i( @@ -167,8 +167,8 @@ func aabb_point_distance(mi: Vector2, ma: Vector2, p: Vector2) -> float: func update_position(_new_position: Vector2, _new_rotation: float, _new_boosting: bool): pass -func progress(position__: float, speed: float, warn: bool): - super(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h): + super(position__, speed, warn, h) if warn: current_vibration_strength = position__ current_vibration_change = speed @@ -190,14 +190,14 @@ func _on_vibration_timeout(): Input.vibrate_handheld(100, vibration_strength) vibration_timer.start() -func put_item(tile: Tile): - super(tile) +func put_item(tile: Tile, h): + super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) -func take_item(tile: Tile): - super(tile) +func take_item(tile: Tile, h): + super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) @@ -210,19 +210,20 @@ func interact(): # clear last interaction if target has moved since if last_interaction != null and not last_interaction == target: - game.mp.send_tile_interact(game.player_id, last_interaction, false) + game.mp.send_tile_interact(game.player_id, last_interaction, false, "left") marker.set_interacting(false) last_interaction = null marker.set_interactive(game.get_tile_interactive(target)) marker_target = tile.item_base.global_position - if Input.is_action_just_pressed("interact") and last_interaction == null: - last_interaction = target - game.mp.send_tile_interact(game.player_id, target, true) - tile.interact() - marker.set_interacting(true) - if Input.is_action_just_released("interact"): - last_interaction = null - game.mp.send_tile_interact(game.player_id, target, false) - marker.set_interacting(false) + for h in ["left", "right"]: + if Input.is_action_just_pressed("interact_"+h) and last_interaction == null: + last_interaction = target + game.mp.send_tile_interact(game.player_id, target, true, h) + tile.interact() + marker.set_interacting(true) + if Input.is_action_just_released("interact_"+h): + last_interaction = null + game.mp.send_tile_interact(game.player_id, target, false, h) + marker.set_interacting(false) else: marker.visible = false diff --git a/client/player/onscreen_controls/controls.gd b/client/player/onscreen_controls/controls.gd index 0d240ddb..06efb82e 100644 --- a/client/player/onscreen_controls/controls.gd +++ b/client/player/onscreen_controls/controls.gd @@ -42,11 +42,11 @@ func _on_boost_released(): boost.modulate = Color.WHITE func _on_interact_pressed(): - Input.action_press("interact") + Input.action_press("interact_left") interact.modulate = modulate_color func _on_interact_released(): - Input.action_release("interact") + Input.action_release("interact_left") interact.modulate = Color.WHITE func _on_pause_pressed(): diff --git a/client/player/player.gd b/client/player/player.gd index c314dad2..3285cafc 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -41,16 +41,17 @@ var marker_target = Vector3(0, 0, 0) var clear_timer: Timer = Timer.new() -var hand: Item = null -var hand_base: Node3D = Node3D.new() +var hand = [null, null] +var hand_base = [Node3D.new(), Node3D.new()] var character_idx: int var is_customer: bool var current_item_message = null var _anim_angle: float = 0.0 -var hand_base_position: Vector3 = DEFAULT_HAND_BASE_POSITION -const DEFAULT_HAND_BASE_POSITION: Vector3 = Vector3(0, .425, .4) +var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] +const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) +const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new_game: Game): add_child(movement_base) @@ -62,9 +63,12 @@ func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new game = new_game username = new_name - hand_base.name = "HandBase" - hand_base.position = hand_base_position - movement_base.add_child(hand_base) + hand_base[0].name = "HandBaseLeft" + hand_base[1].name = "HandBaseRight" + hand_base[0].position = hand_base_position[0] + hand_base[1].position = hand_base_position[1] + movement_base.add_child(hand_base[0]) + movement_base.add_child(hand_base[1]) movement_base.add_child(chat_bubble) movement_base.add_child(item_bubble) @@ -96,45 +100,47 @@ func update_username_tag(state): tag.text = username tag.visible = state -func set_item(i: Item): - if hand != null: hand.remove() +func set_item(i: Item, h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].remove() if i != null: @warning_ignore("static_called_on_instance") - hand_base_position = DEFAULT_HAND_BASE_POSITION - Vector3(0.,i.height() * 0.5, 0.) + hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) + @warning_ignore("static_called_on_instance") + hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null - hand = i - if hand != null: hand.owned_by = hand_base + hand[G.hand_to_index(h)] = i + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].owned_by = hand_base[G.hand_to_index(h)] -func remove_item(): - var i = hand +func remove_item(h): + var i = hand[G.hand_to_index(h)] if i == null: push_error("holding nothing") - hand = null + hand[G.hand_to_index(h)] = null character.holding = false return i -func progress(position__: float, speed: float, warn: bool): - if hand != null: hand.progress(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) func finish(): if hand != null: hand.finish() -func take_item(tile: Tile): +func take_item(tile: Tile, h): if hand != null: push_error("already holding an item") var i = tile.take_item() i.take() - set_item(i) + set_item(i, h) -func put_item(tile: Tile): - var i = remove_item() +func put_item(tile: Tile, h): + var i = remove_item(h) i.put() tile.put_item(i) -func pass_to(player: Player): - var i = remove_item() +func pass_to(player: Player, hfrom, hto): + var i = remove_item(hfrom) i.player_owned_timer = 0 if player.hand != null: push_error("target is already holding an item") - player.set_item(i) + player.set_item(i, hto) func _process(delta): _anim_angle = fmod(_anim_angle + delta, TAU) diff --git a/client/project.godot b/client/project.godot index a0b11050..2239120b 100644 --- a/client/project.godot +++ b/client/project.godot @@ -108,13 +108,19 @@ rotate_down={ , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":1.0,"script":null) ] } -interact={ +interact_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":74,"key_label":0,"unicode":106,"location":0,"echo":false,"script":null) ] } +interact_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":76,"key_label":0,"unicode":108,"location":0,"echo":false,"script":null) +] +} boost={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 6ccd2367..5c7ddeb5 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -72,6 +72,7 @@ pub struct RecipeIndex(pub usize); pub struct DemandIndex(pub usize); #[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] +#[serde(rename_all = "snake_case")] pub enum Hand { Left, Right, -- cgit v1.2.3-70-g09d2 From 462bc5a62f24a35e05d6a171566495e2641583a8 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 24 Dec 2024 21:59:26 +0100 Subject: fix two-handed clear progres --- client/game.gd | 4 ++-- client/menu/settings/input/input_value_node.gd | 1 - client/player/player.gd | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/client/game.gd b/client/game.gd index 850d539e..9a6b8808 100644 --- a/client/game.gd +++ b/client/game.gd @@ -166,7 +166,7 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.finish() else: - players[p.item.player].finish() + players[p.item.player[0]].finish(p.item.player[1]) "set_item": var location: Dictionary = p["location"] if p.item != null: @@ -193,7 +193,7 @@ func handle_packet(p): else: var pl: Player = players[p.location.player[0]] var h = p.location.player[1] - pl.finish() + pl.finish(h) pl.set_item(null, h) "update_map": var neighbors: Array = p["neighbors"] diff --git a/client/menu/settings/input/input_value_node.gd b/client/menu/settings/input/input_value_node.gd index ef00c09b..0eb356f3 100644 --- a/client/menu/settings/input/input_value_node.gd +++ b/client/menu/settings/input/input_value_node.gd @@ -72,5 +72,4 @@ func events_equal(e1: InputEvent, e2: InputEvent) -> bool: func _on_add_pressed() -> void: listening = not listening - add_button.text = tr("Press any key...") if listening else add_text diff --git a/client/player/player.gd b/client/player/player.gd index 3285cafc..223d2c88 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -121,11 +121,11 @@ func remove_item(h): func progress(position__: float, speed: float, warn: bool, h): if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) -func finish(): - if hand != null: hand.finish() +func finish(h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].finish() func take_item(tile: Tile, h): - if hand != null: push_error("already holding an item") + if hand[G.hand_to_index(h)] != null: push_error("already holding an item") var i = tile.take_item() i.take() set_item(i, h) -- cgit v1.2.3-70-g09d2 From bd74a4e0c0e4e42717c2931eab3febfae219ac9c Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 25 Dec 2024 19:37:20 +0100 Subject: variable hand count --- client/game.gd | 14 +++++----- client/global.gd | 9 ++++--- client/multiplayer.gd | 3 ++- client/player/controllable_player.gd | 12 ++++----- client/player/player.gd | 31 +++++++++++----------- locale/en.ini | 1 + pixel-client/src/game.rs | 50 ++++++++++++++++++++++------------- server/bot/src/algos/customer.rs | 13 ++++----- server/bot/src/algos/simple.rs | 13 +++++---- server/bot/src/main.rs | 2 +- server/client-lib/src/lib.rs | 51 ++++++++++++++++++++++++------------ server/protocol/src/helpers.rs | 21 +-------------- server/protocol/src/lib.rs | 8 +++--- server/src/data/mod.rs | 1 + server/src/entity/bot.rs | 2 +- server/src/server.rs | 33 ++++++++++++++--------- 16 files changed, 144 insertions(+), 120 deletions(-) diff --git a/client/game.gd b/client/game.gd index 9a6b8808..9502d2fc 100644 --- a/client/game.gd +++ b/client/game.gd @@ -144,13 +144,13 @@ func handle_packet(p): player_instance.position_ = last_position "move_item": if "player" in p.from and "player" in p.to: - players[p.from.player[0]].pass_to(players[p.to.player[0]], p.from.player[1], p.to.player[1]) + players[p.from.player[0]].pass_to(players[p.to.player[0]], int(p.from.player[1]), int(p.to.player[1])) elif "tile" in p.from and "player" in p.to: var t: Tile = map.get_tile_instance(p.from.tile) - players[p.to.player[0]].take_item(t, p.to.player[1]) + players[p.to.player[0]].take_item(t, int(p.to.player[1])) elif "player" in p.from and "tile" in p.to: var t: Tile = map.get_tile_instance(p.to.tile) - players[p.from.player[0]].put_item(t, p.from.player[1]) + players[p.from.player[0]].put_item(t, int(p.from.player[1])) elif "tile" in p.from and "tile" in p.to: var from_tile2: Tile = map.get_tile_instance(p.from.tile) var to_tile2: Tile = map.get_tile_instance(p.to.tile) @@ -160,13 +160,13 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.progress(p.position, p.speed, p.warn, players.get(p.player)) else: - players[p.item.player[0]].progress(p.position, p.speed, p.warn, p.item.player[1]) + players[p.item.player[0]].progress(p.position, p.speed, p.warn, int(p.item.player[1])) "clear_progress": if "tile" in p.item: var t: Tile = map.get_tile_instance(p.item.tile) t.finish() else: - players[p.item.player[0]].finish(p.item.player[1]) + players[p.item.player[0]].finish(int(p.item.player[1])) "set_item": var location: Dictionary = p["location"] if p.item != null: @@ -180,8 +180,8 @@ func handle_packet(p): else: var pl: Player = players[p.location.player[0]] var h = p.location.player[1] - var i = ItemFactory.produce(item_names[p.item], pl.hand_base[G.hand_to_index(h)]) - i.position = pl.hand_base[G.hand_to_index(h)].global_position + var i = ItemFactory.produce(item_names[p.item], pl.hand_base[h]) + i.position = pl.hand_base[h].global_position add_child(i) i.name = item_names[p.item] pl.set_item(i, h) diff --git a/client/global.gd b/client/global.gd index ce24f85d..93c73e13 100644 --- a/client/global.gd +++ b/client/global.gd @@ -269,7 +269,8 @@ func configure_viewport_aa(vp: Viewport, aa: String) -> void: vp.msaa_3d = Viewport.MSAA_4X vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED -static func hand_to_index(h): - match h: - "left": return 0 - "right": return 1 +static func index_to_hand(i): + match i: + 0: return "left" + 1: return "right" + _: return "unknown" diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 14eaf482..11a5bd84 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -66,6 +66,7 @@ func fix_packet_types(val): if typeof(val[k]) == TYPE_ARRAY and val[k].size() == 2 and typeof(val[k][0]) == TYPE_FLOAT and typeof(val[k][1]) == TYPE_FLOAT: if k in ["tile"]: newval[k] = Vector2i(val[k][0], val[k][1]) elif k in ["pos", "position"]: newval[k] = Vector2(val[k][0], val[k][1]) + else: newval[k] = val[k] # TODO reenable when fixed # elif k in ["player", "id"] and typeof(val[k]) == TYPE_FLOAT: # newval[k] = int(val[k]) @@ -106,7 +107,7 @@ func send_movement(player, pos: Vector2, direction: Vector2, boost: bool): "boost": boost }) -func send_tile_interact(player, pos: Vector2i, edge: bool, hand: String): +func send_tile_interact(player, pos: Vector2i, edge: bool, hand: int): @warning_ignore("incompatible_ternary") send_packet({ "type": "interact", diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd index d241bc2e..99625762 100644 --- a/client/player/controllable_player.gd +++ b/client/player/controllable_player.gd @@ -190,13 +190,13 @@ func _on_vibration_timeout(): Input.vibrate_handheld(100, vibration_strength) vibration_timer.start() -func put_item(tile: Tile, h): +func put_item(tile: Tile, h: int): super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) -func take_item(tile: Tile, h): +func take_item(tile: Tile, h: int): super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) @@ -210,18 +210,18 @@ func interact(): # clear last interaction if target has moved since if last_interaction != null and not last_interaction == target: - game.mp.send_tile_interact(game.player_id, last_interaction, false, "left") + game.mp.send_tile_interact(game.player_id, last_interaction, false, 0) marker.set_interacting(false) last_interaction = null marker.set_interactive(game.get_tile_interactive(target)) marker_target = tile.item_base.global_position - for h in ["left", "right"]: - if Input.is_action_just_pressed("interact_"+h) and last_interaction == null: + for h in [0, 1]: + if Input.is_action_just_pressed("interact_"+G.index_to_hand(h)) and last_interaction == null: last_interaction = target game.mp.send_tile_interact(game.player_id, target, true, h) tile.interact() marker.set_interacting(true) - if Input.is_action_just_released("interact_"+h): + if Input.is_action_just_released("interact_"+G.index_to_hand(h)): last_interaction = null game.mp.send_tile_interact(game.player_id, target, false, h) marker.set_interacting(false) diff --git a/client/player/player.gd b/client/player/player.gd index 223d2c88..39b9fb3e 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -50,6 +50,7 @@ var current_item_message = null var _anim_angle: float = 0.0 var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] +const DEFAULT_HAND_BASE_POSITION_BOTH: Vector3 = Vector3(0, .425, .4) const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) @@ -100,42 +101,42 @@ func update_username_tag(state): tag.text = username tag.visible = state -func set_item(i: Item, h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].remove() +func set_item(i: Item, h: int): + if hand[h] != null: hand[h].remove() if i != null: @warning_ignore("static_called_on_instance") hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) @warning_ignore("static_called_on_instance") hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null - hand[G.hand_to_index(h)] = i - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].owned_by = hand_base[G.hand_to_index(h)] + hand[h] = i + if hand[h] != null: hand[h].owned_by = hand_base[h] -func remove_item(h): - var i = hand[G.hand_to_index(h)] +func remove_item(h: int): + var i = hand[h] if i == null: push_error("holding nothing") - hand[G.hand_to_index(h)] = null + hand[h] = null character.holding = false return i -func progress(position__: float, speed: float, warn: bool, h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h: int): + if hand[h] != null: hand[h].progress(position__, speed, warn) -func finish(h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].finish() +func finish(h: int): + if hand[h] != null: hand[h].finish() -func take_item(tile: Tile, h): - if hand[G.hand_to_index(h)] != null: push_error("already holding an item") +func take_item(tile: Tile, h: int): + if hand[h] != null: push_error("already holding an item") var i = tile.take_item() i.take() set_item(i, h) -func put_item(tile: Tile, h): +func put_item(tile: Tile, h: int): var i = remove_item(h) i.put() tile.put_item(i) -func pass_to(player: Player, hfrom, hto): +func pass_to(player: Player, hfrom: int, hto: int): var i = remove_item(hfrom) i.player_owned_timer = 0 if player.hand != null: diff --git a/locale/en.ini b/locale/en.ini index e1013727..63ffa715 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -223,6 +223,7 @@ s.error.must_be_alone=You must be alone in this server to reload s.error.no_info=No information available. s.error.no_player=Player does not exist. s.error.no_tile=Tile does not exist. +s.error.no_hand=Hand does not exist. s.error.packet_not_supported=Packet not supported in this session. s.error.packet_sender_invalid=Packet sent to a player that is not owned by this connection. s.error.quoting_invalid=Command quoting invalid diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs index 4dc43b72..cbcc62d5 100644 --- a/pixel-client/src/game.rs +++ b/pixel-client/src/game.rs @@ -156,13 +156,13 @@ impl Game { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: Some(self.players[&self.my_id].movement.get_interact_target()), - hand: Hand::Left, + hand: Hand(0), }); } else { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: None, - hand: Hand::Left, + hand: Hand(0), }); } self.interacting = interact; @@ -331,19 +331,23 @@ impl Game { } } PacketC::MoveItem { from, to } => { - let mut item = self.get_item(from).take(); + let mut item = self.get_item(from).unwrap().take(); if let Some(item) = &mut item { item.parent_position = self.get_location_position(to); } - *self.get_item(to) = item; + *self.get_item(to).unwrap() = item; } PacketC::SetItem { location, item } => { let position = self.get_location_position(location); let slot = match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] - } + ItemLocation::Player(pid, hand) => self + .players + .get_mut(&pid) + .unwrap() + .items + .get_mut(hand.0) + .unwrap(), }; self.items_removed.extend(slot.take()); *slot = item.map(|kind| Item { @@ -354,7 +358,13 @@ impl Game { active: None, }) } - PacketC::ClearProgress { item } => self.get_item(item).as_mut().unwrap().active = None, + PacketC::ClearProgress { item } => { + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = None; + } + } + } PacketC::SetProgress { item, position, @@ -362,13 +372,17 @@ impl Game { player, warn, } => { - self.get_item(item).as_mut().unwrap().active = Some(Involvement { - position, - speed, - player, - warn, - recipe: RecipeIndex(0), - }); + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = Some(Involvement { + position, + speed, + player, + warn, + recipe: RecipeIndex(0), + }); + } + } } PacketC::ServerMessage { .. } => { // TODO @@ -392,11 +406,11 @@ impl Game { } } - pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { + pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option> { match location { - ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, + ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item), ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?) } } } diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs index 826ab534..b0ece9dd 100644 --- a/server/bot/src/algos/customer.rs +++ b/server/bot/src/algos/customer.rs @@ -313,7 +313,7 @@ impl CustomerState { PacketS::Interact { pos: Some(pos), player: me, - hand: Hand::Left, + hand: Hand(0), }, PacketS::ApplyScore(Score { demands_completed: 1, @@ -323,7 +323,7 @@ impl CustomerState { PacketS::Interact { pos: None, player: me, - hand: Hand::Left, + hand: Hand(0), }, ], ..Default::default() @@ -356,7 +356,7 @@ impl CustomerState { extra: vec![PacketS::ReplaceHand { player: me, item: demand.output, - hand: Hand::Left, + hand: Hand(0), }], ..Default::default() }; @@ -375,7 +375,8 @@ impl CustomerState { if game .players .get(&me) - .is_some_and(|pl| pl.items[Hand::Left.index()].is_none()) + .is_some_and(|pl| pl.items[0].is_none()) + // TODO index out of bounds? { if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), *origin) { *self = CustomerState::Exiting { path }; @@ -390,12 +391,12 @@ impl CustomerState { PacketS::Interact { player: me, pos: Some(*table), - hand: Hand::Left, + hand: Hand(0), }, PacketS::Interact { player: me, pos: None, - hand: Hand::Left, + hand: Hand(0), }, ], direction, diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index 5a7e61a0..452f59d3 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -21,7 +21,7 @@ use crate::{ }; use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ - glam::IVec2, Hand, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, + glam::IVec2, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, }; use log::{debug, warn}; @@ -106,17 +106,16 @@ impl State for Simple { impl Context<'_, S> { pub fn is_hand_item(&self, item: ItemIndex) -> bool { - self.game.players.get(&self.me).is_some_and(|p| { - p.items[Hand::Left.index()] - .as_ref() - .is_some_and(|i| i.kind == item) - }) + self.game + .players + .get(&self.me) + .is_some_and(|p| p.items[0].as_ref().is_some_and(|i| i.kind == item)) } pub fn is_hand_occupied(&self) -> bool { self.game .players .get(&self.me) - .map(|p| p.items[Hand::Left.index()].is_some()) + .map(|p| p.items[0].is_some()) .unwrap_or(false) } pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 0ffbf4b6..918be7e1 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -109,7 +109,7 @@ fn main() -> Result<()> { network.queue_out.push_back(PacketS::Interact { player: b.id, pos: interact, - hand: Hand::Left, + hand: Hand(0), }) } network.queue_out.push_back(PacketS::Movement { diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs index 54c7cd6e..a40eafc1 100644 --- a/server/client-lib/src/lib.rs +++ b/server/client-lib/src/lib.rs @@ -20,7 +20,8 @@ pub mod network; pub mod spatial_index; use hurrycurry_protocol::{ - glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex + glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, + MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, }; use spatial_index::SpatialIndex; use std::{ @@ -54,7 +55,7 @@ pub struct Player { pub class: PlayerClass, pub character: i32, pub interacting: Option<(IVec2, Hand)>, - pub items: [Option; 2], + pub items: Vec>, pub communicate_persist: Option<(Message, MessageTimeout)>, pub movement: MovementBase, @@ -94,7 +95,7 @@ impl Game { character, class, interacting: None, - items: [const { None }; 2], + items: (0..self.data.hand_count).map(|_| None).collect(), communicate_persist: None, movement: MovementBase::new(position), }, @@ -116,15 +117,27 @@ impl Game { p.movement.rotation = rot; } } - PacketC::MoveItem { from, to } => { - *self.get_item(to) = self.get_item(from).take(); + if let Some(item) = self.get_item(to).map(|e| e.take()) { + if let Some(to) = self.get_item(from) { + *to = item; + } else { + // TODO perhaps restore to original position? + } + } } PacketC::SetItem { location, item } => { - *self.get_item(location) = item.map(|kind| Item { kind, active: None }); + let location = self.get_item(location); + if let Some(location) = location { + *location = item.map(|kind| Item { kind, active: None }); + } } PacketC::ClearProgress { item } => { - self.get_item(item).as_mut().unwrap().active = None; + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = None; + } + } } PacketC::SetProgress { item, @@ -133,13 +146,17 @@ impl Game { speed, warn, } => { - self.get_item(item).as_mut().unwrap().active = Some(Involvement { - player, - speed, - warn, - position, - recipe: RecipeIndex(0), - }); + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = Some(Involvement { + player, + speed, + warn, + position, + recipe: RecipeIndex(0), + }); + } + } } PacketC::UpdateMap { tile, @@ -224,11 +241,11 @@ impl Game { }); } - pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { + pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option> { match location { - ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, + ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item), ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?) } } } diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs index 21835101..b85c2f84 100644 --- a/server/protocol/src/helpers.rs +++ b/server/protocol/src/helpers.rs @@ -106,26 +106,7 @@ impl Display for ItemLocation { impl Display for Hand { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Hand::Left => "left", - Hand::Right => "right", - }) - } -} - -impl Hand { - pub fn index(&self) -> usize { - match self { - Hand::Left => 0, - Hand::Right => 1, - } - } - pub fn from_index(i: usize) -> Self { - match i { - 0 => Hand::Left, - 1 => Hand::Right, - _ => Hand::Left, - } + write!(f, "h{}", self.0) } } diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 5c7ddeb5..74d463a1 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -72,11 +72,8 @@ pub struct RecipeIndex(pub usize); pub struct DemandIndex(pub usize); #[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] -pub enum Hand { - Left, - Right, -} +#[serde(transparent)] +pub struct Hand(pub usize); #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct MapMetadata { @@ -105,6 +102,7 @@ pub struct Gamedata { pub bot_algos: Vec, pub recipes: Vec, pub demands: Vec, + pub hand_count: usize, } #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 57051fe9..15fdfa6b 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -314,6 +314,7 @@ pub fn build_data( item_names, demands, tile_names, + hand_count: 2, }, Serverdata { initial_map, diff --git a/server/src/entity/bot.rs b/server/src/entity/bot.rs index 368f8c9d..6e6c9162 100644 --- a/server/src/entity/bot.rs +++ b/server/src/entity/bot.rs @@ -72,7 +72,7 @@ impl Entity for BotDriver { c.packet_in.push_back(PacketS::Interact { player: self.id, pos: input.interact, - hand: Hand::Left, + hand: Hand(0), }) } c.packet_in.push_back(PacketS::Movement { diff --git a/server/src/server.rs b/server/src/server.rs index 9b90d6b3..f59cd2b9 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -164,10 +164,9 @@ impl GameServerExt for Game { name: player.name.clone(), }); for (i, item) in player.items.iter().enumerate() { - let hand = Hand::from_index(i); if let Some(item) = &item { out.push(PacketC::SetItem { - location: ItemLocation::Player(id, hand), + location: ItemLocation::Player(id, Hand(i)), item: Some(item.kind), }); if let Some(Involvement { @@ -180,7 +179,7 @@ impl GameServerExt for Game { { out.push(PacketC::SetProgress { player, - item: ItemLocation::Player(id, hand), + item: ItemLocation::Player(id, Hand(i)), position, speed, warn, @@ -255,7 +254,7 @@ impl GameServerExt for Game { self.players.insert( id, Player { - items: [const { None }; 2], + items: (0..self.data.hand_count).map(|_| None).collect(), character, class, movement: MovementBase::new(position), @@ -527,14 +526,17 @@ impl Server { return Err(tre!("s.error.customer_interact")); } + let this_hslot = this.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; + let other_hslot = other.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; + interact( &self.game.data, edge, None, Some(pid), - &mut this.items[hand.index()], + this_hslot, ItemLocation::Player(base_pid, hand), - &mut other.items[hand.index()], + other_hslot, ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, @@ -548,6 +550,11 @@ impl Server { .get_mut(&pid) .ok_or(tre!("s.error.no_player"))?; + let hslot = player + .items + .get_mut(hand.0) + .ok_or(tre!("s.error.no_hand"))?; + interact( &self.game.data, edge, @@ -555,7 +562,7 @@ impl Server { Some(pid), &mut tile.item, ItemLocation::Tile(pos), - &mut player.items[hand.index()], + hslot, ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, @@ -598,10 +605,12 @@ impl Server { } PacketS::ReplaceHand { item, player, hand } => { let pdata = self.game.players.get_mut(&player).ok_or(tre!(""))?; - pdata.items[hand.index()] = item.map(|i| Item { - kind: i, - active: None, - }); + if let Some(slot) = pdata.items.get_mut(hand.0) { + *slot = item.map(|i| Item { + kind: i, + active: None, + }); + } self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Player(player, hand), item, @@ -676,7 +685,7 @@ impl Server { &self.gamedata_index, None, item, - ItemLocation::Player(pid, Hand::from_index(i)), + ItemLocation::Player(pid, Hand(i)), &mut self.game.score, &mut self.score_changed, &mut self.packet_out, -- cgit v1.2.3-70-g09d2 From 64082ef5ca1bc6baeff23930d85e154e90b3c69f Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 25 Dec 2024 19:58:35 +0100 Subject: map-specific two-handed mode --- client/game.gd | 2 ++ client/player/player.gd | 37 +++++++++++++++++++++++-------------- data/maps/village.yaml | 1 + server/src/data/mod.rs | 3 ++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/client/game.gd b/client/game.gd index 9502d2fc..6c152d8a 100644 --- a/client/game.gd +++ b/client/game.gd @@ -53,6 +53,7 @@ var tile_collide: Array = [] var tile_interact: Array = [] var maps: Array = [] var bot_algos: Array +var hand_count = 0 var text_message_history: Array[TextMessage] = [] var join_state: JoinState = JoinState.SPECTATING @@ -95,6 +96,7 @@ func handle_packet(p): tile_interact = p["data"]["tile_interact"] maps = p["data"]["maps"] bot_algos = p["data"]["bot_algos"] + hand_count = p["data"]["hand_count"] tile_index_by_name.clear() for id in tile_names.size(): diff --git a/client/player/player.gd b/client/player/player.gd index 39b9fb3e..031cba29 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -42,15 +42,14 @@ var marker_target = Vector3(0, 0, 0) var clear_timer: Timer = Timer.new() var hand = [null, null] -var hand_base = [Node3D.new(), Node3D.new()] +var hand_base var character_idx: int var is_customer: bool var current_item_message = null var _anim_angle: float = 0.0 -var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] -const DEFAULT_HAND_BASE_POSITION_BOTH: Vector3 = Vector3(0, .425, .4) +const DEFAULT_HAND_BASE_POSITION_CENTER: Vector3 = Vector3(0, .425, .4) const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) @@ -64,12 +63,22 @@ func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new game = new_game username = new_name - hand_base[0].name = "HandBaseLeft" - hand_base[1].name = "HandBaseRight" - hand_base[0].position = hand_base_position[0] - hand_base[1].position = hand_base_position[1] - movement_base.add_child(hand_base[0]) - movement_base.add_child(hand_base[1]) + if game.hand_count == 1: + var center = Node3D.new() + center.name = "HandBaseCenter" + center.position = DEFAULT_HAND_BASE_POSITION_CENTER + hand_base = [center] + else: + var left = Node3D.new() + var right = Node3D.new() + left.name = "HandBaseLeft" + right.name = "HandBaseRight" + left.position = DEFAULT_HAND_BASE_POSITION_LEFT + right.position = DEFAULT_HAND_BASE_POSITION_RIGHT + hand_base = [left, right] + + for h in hand_base: + movement_base.add_child(h) movement_base.add_child(chat_bubble) movement_base.add_child(item_bubble) @@ -103,11 +112,11 @@ func update_username_tag(state): func set_item(i: Item, h: int): if hand[h] != null: hand[h].remove() - if i != null: - @warning_ignore("static_called_on_instance") - hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) - @warning_ignore("static_called_on_instance") - hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) + # if i != null: + # @warning_ignore("static_called_on_instance") + # hand_base_position[h] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) + # @warning_ignore("static_called_on_instance") + # hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null hand[h] = i if hand[h] != null: hand[h].owned_by = hand_base[h] diff --git a/data/maps/village.yaml b/data/maps/village.yaml index 9538ae9d..56cd49b0 100644 --- a/data/maps/village.yaml +++ b/data/maps/village.yaml @@ -17,6 +17,7 @@ # along with this program. If not, see . # score_baseline: 200 +hand_count: 2 map: - "''''''''''''''~_''''''''''''''" - "''''''''''''''__''''''''''''''" diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 15fdfa6b..64f40217 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -73,6 +73,7 @@ pub struct MapDecl { walkable: Vec, chef_spawn: char, customer_spawn: char, + #[serde(default)] hand_count: Option, #[serde(default)] entities: Vec, #[serde(default)] tile_entities: HashMap, #[serde(default)] score_baseline: i64, @@ -314,7 +315,7 @@ pub fn build_data( item_names, demands, tile_names, - hand_count: 2, + hand_count: map_in.hand_count.unwrap_or(1), }, Serverdata { initial_map, -- cgit v1.2.3-70-g09d2 From b0df9b7c27a3d6316969d7feff4d912c3abf99f6 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Dec 2024 15:37:11 +0100 Subject: two-handed server --- pixel-client/src/game.rs | 34 +++++++---- server/bot/src/algos/customer.rs | 13 +++- server/bot/src/algos/simple.rs | 13 ++-- server/bot/src/main.rs | 3 +- server/client-lib/src/lib.rs | 21 ++++--- server/protocol/src/helpers.rs | 30 ++++++++- server/protocol/src/lib.rs | 10 ++- server/src/entity/bot.rs | 3 +- server/src/entity/tutorial.rs | 4 +- server/src/server.rs | 127 ++++++++++++++++++++++----------------- 10 files changed, 167 insertions(+), 91 deletions(-) diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs index e7754366..4dc43b72 100644 --- a/pixel-client/src/game.rs +++ b/pixel-client/src/game.rs @@ -30,8 +30,8 @@ use hurrycurry_client_lib::{network::sync::Network, spatial_index::SpatialIndex, use hurrycurry_protocol::{ glam::{IVec2, Vec2}, movement::MovementBase, - Gamedata, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS, PlayerClass, - PlayerID, RecipeIndex, Score, TileIndex, + Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS, + PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, }; use log::{info, warn}; use sdl2::{ @@ -67,7 +67,7 @@ pub struct Tile { pub struct Player { movement: MovementBase, - item: Option, + items: [Option; 2], message_persist: Option<(Message, MessageTimeout)>, _name: String, _character: i32, @@ -156,11 +156,13 @@ impl Game { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: Some(self.players[&self.my_id].movement.get_interact_target()), + hand: Hand::Left, }); } else { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: None, + hand: Hand::Left, }); } self.interacting = interact; @@ -209,9 +211,11 @@ impl Game { }); for player in self.players.values_mut() { - if let Some(item) = &mut player.item { - item.parent_position = player.movement.position; - item.tick(1., dt); + for item in &mut player.items { + if let Some(item) = item { + item.parent_position = player.movement.position; + item.tick(1., dt); + } } } for tile in self.tiles.values_mut() { @@ -292,7 +296,7 @@ impl Game { _character: character, _name: name, message_persist: None, - item: None, + items: [const { None }; 2], movement: MovementBase { position, input_direction: Vec2::ZERO, @@ -337,7 +341,9 @@ impl Game { let position = self.get_location_position(location); let slot = match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } }; self.items_removed.extend(slot.take()); *slot = item.map(|kind| Item { @@ -389,13 +395,15 @@ impl Game { pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } } } pub fn get_location_position(&self, location: ItemLocation) -> Vec2 { match location { ItemLocation::Tile(pos) => pos.as_vec2() + 0.5, - ItemLocation::Player(p) => self.players[&p].movement.position, + ItemLocation::Player(p, _) => self.players[&p].movement.position, } } @@ -494,8 +502,10 @@ impl Player { _ => (), } } - if let Some(item) = &self.item { - item.draw(ctx, item_sprites) + for item in &self.items { + if let Some(item) = item { + item.draw(ctx, item_sprites) + } } } } diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs index b243bd55..826ab534 100644 --- a/server/bot/src/algos/customer.rs +++ b/server/bot/src/algos/customer.rs @@ -22,7 +22,7 @@ use crate::{ use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, - DemandIndex, Message, PacketS, PlayerClass, PlayerID, Score, + DemandIndex, Hand, Message, PacketS, PlayerClass, PlayerID, Score, }; use log::info; use rand::{random, seq::IndexedRandom, thread_rng}; @@ -313,6 +313,7 @@ impl CustomerState { PacketS::Interact { pos: Some(pos), player: me, + hand: Hand::Left, }, PacketS::ApplyScore(Score { demands_completed: 1, @@ -322,6 +323,7 @@ impl CustomerState { PacketS::Interact { pos: None, player: me, + hand: Hand::Left, }, ], ..Default::default() @@ -354,6 +356,7 @@ impl CustomerState { extra: vec![PacketS::ReplaceHand { player: me, item: demand.output, + hand: Hand::Left, }], ..Default::default() }; @@ -369,7 +372,11 @@ impl CustomerState { cooldown, } => { *cooldown -= dt; - if game.players.get(&me).is_some_and(|pl| pl.item.is_none()) { + if game + .players + .get(&me) + .is_some_and(|pl| pl.items[Hand::Left.index()].is_none()) + { if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), *origin) { *self = CustomerState::Exiting { path }; } @@ -383,10 +390,12 @@ impl CustomerState { PacketS::Interact { player: me, pos: Some(*table), + hand: Hand::Left, }, PacketS::Interact { player: me, pos: None, + hand: Hand::Left, }, ], direction, diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index 14eb38c4..5a7e61a0 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -21,7 +21,7 @@ use crate::{ }; use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ - glam::IVec2, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, + glam::IVec2, Hand, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, }; use log::{debug, warn}; @@ -106,16 +106,17 @@ impl State for Simple { impl Context<'_, S> { pub fn is_hand_item(&self, item: ItemIndex) -> bool { - self.game - .players - .get(&self.me) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + self.game.players.get(&self.me).is_some_and(|p| { + p.items[Hand::Left.index()] + .as_ref() + .is_some_and(|i| i.kind == item) + }) } pub fn is_hand_occupied(&self) -> bool { self.game .players .get(&self.me) - .map(|p| p.item.is_some()) + .map(|p| p.items[Hand::Left.index()].is_some()) .unwrap_or(false) } pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 61ae1c1c..0ffbf4b6 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use clap::Parser; use hurrycurry_bot::{algos::ALGO_CONSTRUCTORS, BotAlgo, BotInput}; use hurrycurry_client_lib::{network::sync::Network, Game}; -use hurrycurry_protocol::{PacketC, PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketC, PacketS, PlayerClass, PlayerID}; use log::warn; use std::{thread::sleep, time::Duration}; @@ -109,6 +109,7 @@ fn main() -> Result<()> { network.queue_out.push_back(PacketS::Interact { player: b.id, pos: interact, + hand: Hand::Left, }) } network.queue_out.push_back(PacketS::Movement { diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs index 5d5e55d5..54c7cd6e 100644 --- a/server/client-lib/src/lib.rs +++ b/server/client-lib/src/lib.rs @@ -20,8 +20,7 @@ pub mod network; pub mod spatial_index; use hurrycurry_protocol::{ - glam::IVec2, movement::MovementBase, Gamedata, ItemIndex, ItemLocation, Message, - MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, + glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex }; use spatial_index::SpatialIndex; use std::{ @@ -54,8 +53,8 @@ pub struct Player { pub name: String, pub class: PlayerClass, pub character: i32, - pub interacting: Option, - pub item: Option, + pub interacting: Option<(IVec2, Hand)>, + pub items: [Option; 2], pub communicate_persist: Option<(Message, MessageTimeout)>, pub movement: MovementBase, @@ -95,7 +94,7 @@ impl Game { character, class, interacting: None, - item: None, + items: [const { None }; 2], communicate_persist: None, movement: MovementBase::new(position), }, @@ -200,9 +199,11 @@ impl Game { } for player in self.players.values_mut() { - if let Some(item) = &mut player.item { - if let Some(active) = &mut item.active { - active.position += active.speed; + for item in &mut player.items { + if let Some(item) = item { + if let Some(active) = &mut item.active { + active.position += active.speed; + } } } } @@ -226,7 +227,9 @@ impl Game { pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, + ItemLocation::Player(pid, hand) => { + &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + } } } } diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs index 924d0886..21835101 100644 --- a/server/protocol/src/helpers.rs +++ b/server/protocol/src/helpers.rs @@ -1,7 +1,8 @@ use std::fmt::Display; use crate::{ - DocumentElement, Gamedata, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, TileIndex, + DocumentElement, Gamedata, Hand, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, + TileIndex, }; impl Gamedata { @@ -98,7 +99,32 @@ 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})"), + ItemLocation::Player(PlayerID(id), hand) => write!(f, "player({id}_{hand})"), + } + } +} + +impl Display for Hand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Hand::Left => "left", + Hand::Right => "right", + }) + } +} + +impl Hand { + pub fn index(&self) -> usize { + match self { + Hand::Left => 0, + Hand::Right => 1, + } + } + pub fn from_index(i: usize) -> Self { + match i { + 0 => Hand::Left, + 1 => Hand::Right, + _ => Hand::Left, } } } diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 2ef07015..6ccd2367 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -71,6 +71,12 @@ pub struct RecipeIndex(pub usize); #[serde(transparent)] pub struct DemandIndex(pub usize); +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] +pub enum Hand { + Left, + Right, +} + #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct MapMetadata { pub name: String, @@ -124,6 +130,7 @@ pub enum PacketS { }, Interact { player: PlayerID, + hand: Hand, #[bincode(with_serde)] pos: Option, }, @@ -144,6 +151,7 @@ pub enum PacketS { /// For internal use only (customers) ReplaceHand { player: PlayerID, + hand: Hand, item: Option, }, #[serde(skip)] @@ -344,7 +352,7 @@ pub enum Recipe { #[serde(rename_all = "snake_case")] pub enum ItemLocation { Tile(#[bincode(with_serde)] IVec2), - Player(PlayerID), + Player(PlayerID, Hand), } #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] diff --git a/server/src/entity/bot.rs b/server/src/entity/bot.rs index fe4d711f..368f8c9d 100644 --- a/server/src/entity/bot.rs +++ b/server/src/entity/bot.rs @@ -18,7 +18,7 @@ use super::{Entity, EntityContext}; use anyhow::Result; use hurrycurry_bot::{BotAlgo, DynBotAlgo}; -use hurrycurry_protocol::{PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketS, PlayerClass, PlayerID}; use log::info; use rand::random; use std::any::Any; @@ -72,6 +72,7 @@ impl Entity for BotDriver { c.packet_in.push_back(PacketS::Interact { player: self.id, pos: input.interact, + hand: Hand::Left, }) } c.packet_in.push_back(PacketS::Movement { diff --git a/server/src/entity/tutorial.rs b/server/src/entity/tutorial.rs index 44244862..33c0e507 100644 --- a/server/src/entity/tutorial.rs +++ b/server/src/entity/tutorial.rs @@ -144,7 +144,7 @@ impl StepContext<'_> { .game .players .get(&self.player) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item)) } pub fn find_demand(&self, item: ItemIndex) -> Option { self.ent @@ -228,7 +228,7 @@ impl StepContext<'_> { .game .players .get(&self.player) - .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) + .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item)) { if let Some(pos) = self.find_demand(item) { Err((Some(pos), trm!("s.tutorial.serve"))) diff --git a/server/src/server.rs b/server/src/server.rs index 462e95d4..19153864 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -28,8 +28,8 @@ use hurrycurry_client_lib::{Game, Involvement, Item, Player, Tile}; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, movement::MovementBase, - Gamedata, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, Score, - TileIndex, + Gamedata, Hand, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, + Score, TileIndex, }; use log::{info, warn}; use rand::random; @@ -163,26 +163,29 @@ impl GameServerExt for Game { character: player.character, name: player.name.clone(), }); - if let Some(item) = &player.item { - out.push(PacketC::SetItem { - location: ItemLocation::Player(id), - item: Some(item.kind), - }); - if let Some(Involvement { - player, - position, - speed, - warn, - .. - }) = item.active - { - out.push(PacketC::SetProgress { + for (i, item) in player.items.iter().enumerate() { + let hand = Hand::from_index(i); + if let Some(item) = &item { + out.push(PacketC::SetItem { + location: ItemLocation::Player(id, hand), + item: Some(item.kind), + }); + if let Some(Involvement { player, - item: ItemLocation::Player(id), position, speed, warn, - }); + .. + }) = item.active + { + out.push(PacketC::SetProgress { + player, + item: ItemLocation::Player(id, hand), + position, + speed, + warn, + }); + } } } if let Some((message, timeout)) = &player.communicate_persist { @@ -252,7 +255,7 @@ impl GameServerExt for Game { self.players.insert( id, Player { - item: None, + items: [const { None }; 2], character, class, movement: MovementBase::new(position), @@ -403,15 +406,18 @@ impl Server { self.game.players_spatial_index.remove_entry(player); - if let Some(item) = p.item { - let pos = p.movement.position.floor().as_ivec2(); - if let Some(tile) = self.game.tiles.get_mut(&pos) { - if tile.item.is_none() { - self.packet_out.push_back(PacketC::SetItem { - location: ItemLocation::Tile(pos), - item: Some(item.kind), - }); - tile.item = Some(item); + // TODO if holding two, one is destroyed + for item in p.items { + if let Some(item) = item { + let pos = p.movement.position.floor().as_ivec2(); + if let Some(tile) = self.game.tiles.get_mut(&pos) { + if tile.item.is_none() { + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), + item: Some(item.kind), + }); + tile.item = Some(item); + } } } } @@ -449,7 +455,7 @@ impl Server { } } } - PacketS::Interact { pos, player } => { + PacketS::Interact { pos, player, hand } => { for e in &mut self.entities { if e.interact( EntityContext { @@ -477,7 +483,9 @@ impl Server { .get_mut(&pid) .ok_or(tre!("s.error.no_player"))?; - let (pos, edge) = match (pos, player.interacting) { + let pos = pos.map(|p| (p, hand)); + + let ((pos, hand), edge) = match (pos, player.interacting) { (None, None) => return Ok(()), // this is silent because of auto release (None, Some(pos)) => (pos, false), (Some(pos), None) => (pos, true), @@ -497,7 +505,7 @@ impl Server { // No going back from here on - player.interacting = if edge { Some(pos) } else { None }; + player.interacting = if edge { Some((pos, hand)) } else { None }; let other_pid = if !self.game.data.is_tile_interactable(tile.kind) { self.game @@ -528,10 +536,10 @@ impl Server { edge, None, Some(pid), - &mut this.item, - ItemLocation::Player(base_pid), - &mut other.item, - ItemLocation::Player(pid), + &mut this.items[hand.index()], + ItemLocation::Player(base_pid, hand), + &mut other.items[hand.index()], + ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, false, @@ -551,8 +559,8 @@ impl Server { Some(pid), &mut tile.item, ItemLocation::Tile(pos), - &mut player.item, - ItemLocation::Player(pid), + &mut player.items[hand.index()], + ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, false, @@ -592,14 +600,14 @@ impl Server { timeout, }); } - PacketS::ReplaceHand { item, player } => { + PacketS::ReplaceHand { item, player, hand } => { let pdata = self.game.players.get_mut(&player).ok_or(tre!(""))?; - pdata.item = item.map(|i| Item { + pdata.items[hand.index()] = item.map(|i| Item { kind: i, active: None, }); self.packet_out.push_back(PacketC::SetItem { - location: ItemLocation::Player(player), + location: ItemLocation::Player(player, hand), item, }) } @@ -665,17 +673,19 @@ impl Server { rot: player.movement.rotation, }); - tick_slot( - dt, - &self.game.data, - &self.gamedata_index, - None, - &mut player.item, - ItemLocation::Player(pid), - &mut self.game.score, - &mut self.score_changed, - &mut self.packet_out, - ); + for (i, item) in player.items.iter_mut().enumerate() { + tick_slot( + dt, + &self.game.data, + &self.gamedata_index, + None, + item, + ItemLocation::Player(pid, Hand::from_index(i)), + &mut self.game.score, + &mut self.score_changed, + &mut self.packet_out, + ); + } } let mut players_auto_release = Vec::new(); @@ -686,20 +696,27 @@ impl Server { player.communicate_persist = None; } } - if let Some(pos) = player.interacting { + if let Some((pos, hand)) = player.interacting { if let Some(tile) = self.game.tiles.get(&pos) { if let Some(item) = &tile.item { if let Some(involvement) = &item.active { if involvement.position >= 1. { - players_auto_release.push(*pid); + players_auto_release.push((*pid, hand)); } } } } } } - for player in players_auto_release.drain(..) { - let _ = self.packet_in(PacketS::Interact { pos: None, player }, &mut vec![]); + for (player, hand) in players_auto_release.drain(..) { + let _ = self.packet_in( + PacketS::Interact { + pos: None, + player, + hand, + }, + &mut vec![], + ); } let mut load_map = None; -- cgit v1.2.3-70-g09d2 From 469d554381597a383aa799b29261786de19fb08e Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Dec 2024 16:41:57 +0100 Subject: two-handed mostly works --- client/game.gd | 29 ++++++------ client/global.gd | 5 ++ .../communicate/popup_message/popup_message.gd | 2 +- client/multiplayer.gd | 3 +- client/player/controllable_player.gd | 35 +++++++------- client/player/onscreen_controls/controls.gd | 4 +- client/player/player.gd | 54 ++++++++++++---------- client/project.godot | 8 +++- server/protocol/src/lib.rs | 1 + 9 files changed, 82 insertions(+), 59 deletions(-) diff --git a/client/game.gd b/client/game.gd index e7d39aa2..850d539e 100644 --- a/client/game.gd +++ b/client/game.gd @@ -128,8 +128,9 @@ func handle_packet(p): if p.id == player_id: set_join_state(JoinState.SPECTATING) camera.target = $Center - if player.hand != null: - player.hand.queue_free() + for h in player.hand: + if h != null: + h.queue_free() players.erase(p.id) player.queue_free() update_players.emit(players) @@ -143,13 +144,13 @@ func handle_packet(p): player_instance.position_ = last_position "move_item": if "player" in p.from and "player" in p.to: - players[p.from.player].pass_to(players[p.to.player]) + players[p.from.player[0]].pass_to(players[p.to.player[0]], p.from.player[1], p.to.player[1]) elif "tile" in p.from and "player" in p.to: var t: Tile = map.get_tile_instance(p.from.tile) - players[p.to.player].take_item(t) + players[p.to.player[0]].take_item(t, p.to.player[1]) elif "player" in p.from and "tile" in p.to: var t: Tile = map.get_tile_instance(p.to.tile) - players[p.from.player].put_item(t) + players[p.from.player[0]].put_item(t, p.from.player[1]) elif "tile" in p.from and "tile" in p.to: var from_tile2: Tile = map.get_tile_instance(p.from.tile) var to_tile2: Tile = map.get_tile_instance(p.to.tile) @@ -159,7 +160,7 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.progress(p.position, p.speed, p.warn, players.get(p.player)) else: - players[p.item.player].progress(p.position, p.speed, p.warn) + players[p.item.player[0]].progress(p.position, p.speed, p.warn, p.item.player[1]) "clear_progress": if "tile" in p.item: var t: Tile = map.get_tile_instance(p.item.tile) @@ -177,21 +178,23 @@ func handle_packet(p): i.name = item_names[p.item] t.set_item(i) else: - var pl: Player = players[p.location.player] - var i = ItemFactory.produce(item_names[p.item], pl.hand_base) - i.position = pl.hand_base.global_position + var pl: Player = players[p.location.player[0]] + var h = p.location.player[1] + var i = ItemFactory.produce(item_names[p.item], pl.hand_base[G.hand_to_index(h)]) + i.position = pl.hand_base[G.hand_to_index(h)].global_position add_child(i) i.name = item_names[p.item] - pl.set_item(i) + pl.set_item(i, h) else: if "tile" in p.location: var t: Tile = map.get_tile_instance(p.location.tile) t.finish() t.set_item(null) else: - var player: Player = players[p.location.player] - player.finish() - player.set_item(null) + var pl: Player = players[p.location.player[0]] + var h = p.location.player[1] + pl.finish() + pl.set_item(null, h) "update_map": var neighbors: Array = p["neighbors"] if p.kind != null: diff --git a/client/global.gd b/client/global.gd index 374f0e44..ce24f85d 100644 --- a/client/global.gd +++ b/client/global.gd @@ -268,3 +268,8 @@ func configure_viewport_aa(vp: Viewport, aa: String) -> void: "ms4x": vp.msaa_3d = Viewport.MSAA_4X vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + +static func hand_to_index(h): + match h: + "left": return 0 + "right": return 1 diff --git a/client/menu/communicate/popup_message/popup_message.gd b/client/menu/communicate/popup_message/popup_message.gd index 98bd94e3..56c8961a 100644 --- a/client/menu/communicate/popup_message/popup_message.gd +++ b/client/menu/communicate/popup_message/popup_message.gd @@ -141,7 +141,7 @@ func _input(_event): Global.set_hint("has_rotated", true) if any_action_just_pressed(["zoom_in", "zoom_out"]): Global.set_hint("has_zoomed", true) - if Input.is_action_just_pressed("interact"): + if Input.is_action_just_pressed("interact_left") or Input.is_action_just_pressed("interact_right"): Global.set_hint("has_interacted", true) if Input.is_action_just_pressed("reset"): Global.set_hint("has_reset", true) diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 57031221..14eaf482 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -106,11 +106,12 @@ func send_movement(player, pos: Vector2, direction: Vector2, boost: bool): "boost": boost }) -func send_tile_interact(player, pos: Vector2i, edge: bool): +func send_tile_interact(player, pos: Vector2i, edge: bool, hand: String): @warning_ignore("incompatible_ternary") send_packet({ "type": "interact", "player": player, + "hand": hand, "pos": [pos.x, pos.y] if edge else null, }) diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd index 9db36ad4..d241bc2e 100644 --- a/client/player/controllable_player.gd +++ b/client/player/controllable_player.gd @@ -83,7 +83,7 @@ func _process_movement(delta): var boost = Input.is_action_pressed("boost") or (Global.get_setting("gameplay.latch_boost") and boosting) - if Input.is_action_pressed("interact") or Input.is_action_just_released("interact"): + if Input.is_action_pressed("interact_left") or Input.is_action_just_released("interact_left") or Input.is_action_pressed("interact_right") or Input.is_action_just_released("interact_right"): input *= 0 else: target = Vector2i( @@ -167,8 +167,8 @@ func aabb_point_distance(mi: Vector2, ma: Vector2, p: Vector2) -> float: func update_position(_new_position: Vector2, _new_rotation: float, _new_boosting: bool): pass -func progress(position__: float, speed: float, warn: bool): - super(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h): + super(position__, speed, warn, h) if warn: current_vibration_strength = position__ current_vibration_change = speed @@ -190,14 +190,14 @@ func _on_vibration_timeout(): Input.vibrate_handheld(100, vibration_strength) vibration_timer.start() -func put_item(tile: Tile): - super(tile) +func put_item(tile: Tile, h): + super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) -func take_item(tile: Tile): - super(tile) +func take_item(tile: Tile, h): + super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) @@ -210,19 +210,20 @@ func interact(): # clear last interaction if target has moved since if last_interaction != null and not last_interaction == target: - game.mp.send_tile_interact(game.player_id, last_interaction, false) + game.mp.send_tile_interact(game.player_id, last_interaction, false, "left") marker.set_interacting(false) last_interaction = null marker.set_interactive(game.get_tile_interactive(target)) marker_target = tile.item_base.global_position - if Input.is_action_just_pressed("interact") and last_interaction == null: - last_interaction = target - game.mp.send_tile_interact(game.player_id, target, true) - tile.interact() - marker.set_interacting(true) - if Input.is_action_just_released("interact"): - last_interaction = null - game.mp.send_tile_interact(game.player_id, target, false) - marker.set_interacting(false) + for h in ["left", "right"]: + if Input.is_action_just_pressed("interact_"+h) and last_interaction == null: + last_interaction = target + game.mp.send_tile_interact(game.player_id, target, true, h) + tile.interact() + marker.set_interacting(true) + if Input.is_action_just_released("interact_"+h): + last_interaction = null + game.mp.send_tile_interact(game.player_id, target, false, h) + marker.set_interacting(false) else: marker.visible = false diff --git a/client/player/onscreen_controls/controls.gd b/client/player/onscreen_controls/controls.gd index 0d240ddb..06efb82e 100644 --- a/client/player/onscreen_controls/controls.gd +++ b/client/player/onscreen_controls/controls.gd @@ -42,11 +42,11 @@ func _on_boost_released(): boost.modulate = Color.WHITE func _on_interact_pressed(): - Input.action_press("interact") + Input.action_press("interact_left") interact.modulate = modulate_color func _on_interact_released(): - Input.action_release("interact") + Input.action_release("interact_left") interact.modulate = Color.WHITE func _on_pause_pressed(): diff --git a/client/player/player.gd b/client/player/player.gd index c314dad2..3285cafc 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -41,16 +41,17 @@ var marker_target = Vector3(0, 0, 0) var clear_timer: Timer = Timer.new() -var hand: Item = null -var hand_base: Node3D = Node3D.new() +var hand = [null, null] +var hand_base = [Node3D.new(), Node3D.new()] var character_idx: int var is_customer: bool var current_item_message = null var _anim_angle: float = 0.0 -var hand_base_position: Vector3 = DEFAULT_HAND_BASE_POSITION -const DEFAULT_HAND_BASE_POSITION: Vector3 = Vector3(0, .425, .4) +var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] +const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) +const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new_game: Game): add_child(movement_base) @@ -62,9 +63,12 @@ func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new game = new_game username = new_name - hand_base.name = "HandBase" - hand_base.position = hand_base_position - movement_base.add_child(hand_base) + hand_base[0].name = "HandBaseLeft" + hand_base[1].name = "HandBaseRight" + hand_base[0].position = hand_base_position[0] + hand_base[1].position = hand_base_position[1] + movement_base.add_child(hand_base[0]) + movement_base.add_child(hand_base[1]) movement_base.add_child(chat_bubble) movement_base.add_child(item_bubble) @@ -96,45 +100,47 @@ func update_username_tag(state): tag.text = username tag.visible = state -func set_item(i: Item): - if hand != null: hand.remove() +func set_item(i: Item, h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].remove() if i != null: @warning_ignore("static_called_on_instance") - hand_base_position = DEFAULT_HAND_BASE_POSITION - Vector3(0.,i.height() * 0.5, 0.) + hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) + @warning_ignore("static_called_on_instance") + hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null - hand = i - if hand != null: hand.owned_by = hand_base + hand[G.hand_to_index(h)] = i + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].owned_by = hand_base[G.hand_to_index(h)] -func remove_item(): - var i = hand +func remove_item(h): + var i = hand[G.hand_to_index(h)] if i == null: push_error("holding nothing") - hand = null + hand[G.hand_to_index(h)] = null character.holding = false return i -func progress(position__: float, speed: float, warn: bool): - if hand != null: hand.progress(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) func finish(): if hand != null: hand.finish() -func take_item(tile: Tile): +func take_item(tile: Tile, h): if hand != null: push_error("already holding an item") var i = tile.take_item() i.take() - set_item(i) + set_item(i, h) -func put_item(tile: Tile): - var i = remove_item() +func put_item(tile: Tile, h): + var i = remove_item(h) i.put() tile.put_item(i) -func pass_to(player: Player): - var i = remove_item() +func pass_to(player: Player, hfrom, hto): + var i = remove_item(hfrom) i.player_owned_timer = 0 if player.hand != null: push_error("target is already holding an item") - player.set_item(i) + player.set_item(i, hto) func _process(delta): _anim_angle = fmod(_anim_angle + delta, TAU) diff --git a/client/project.godot b/client/project.godot index a0b11050..2239120b 100644 --- a/client/project.godot +++ b/client/project.godot @@ -108,13 +108,19 @@ rotate_down={ , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":1.0,"script":null) ] } -interact={ +interact_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":74,"key_label":0,"unicode":106,"location":0,"echo":false,"script":null) ] } +interact_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":76,"key_label":0,"unicode":108,"location":0,"echo":false,"script":null) +] +} boost={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 6ccd2367..5c7ddeb5 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -72,6 +72,7 @@ pub struct RecipeIndex(pub usize); pub struct DemandIndex(pub usize); #[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] +#[serde(rename_all = "snake_case")] pub enum Hand { Left, Right, -- cgit v1.2.3-70-g09d2 From 15be00667282a253fb438fec9d6347f5af89d9a0 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 24 Dec 2024 21:59:26 +0100 Subject: fix two-handed clear progres --- client/game.gd | 4 ++-- client/menu/settings/input/input_value_node.gd | 1 - client/player/player.gd | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/client/game.gd b/client/game.gd index 850d539e..9a6b8808 100644 --- a/client/game.gd +++ b/client/game.gd @@ -166,7 +166,7 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.finish() else: - players[p.item.player].finish() + players[p.item.player[0]].finish(p.item.player[1]) "set_item": var location: Dictionary = p["location"] if p.item != null: @@ -193,7 +193,7 @@ func handle_packet(p): else: var pl: Player = players[p.location.player[0]] var h = p.location.player[1] - pl.finish() + pl.finish(h) pl.set_item(null, h) "update_map": var neighbors: Array = p["neighbors"] diff --git a/client/menu/settings/input/input_value_node.gd b/client/menu/settings/input/input_value_node.gd index 44a65ec5..9cf8327a 100644 --- a/client/menu/settings/input/input_value_node.gd +++ b/client/menu/settings/input/input_value_node.gd @@ -72,5 +72,4 @@ func events_equal(e1: InputEvent, e2: InputEvent) -> bool: func _on_add_pressed() -> void: listening = not listening - add_button.text = tr("c.settings.input.press_any_key") if listening else add_text diff --git a/client/player/player.gd b/client/player/player.gd index 3285cafc..223d2c88 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -121,11 +121,11 @@ func remove_item(h): func progress(position__: float, speed: float, warn: bool, h): if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) -func finish(): - if hand != null: hand.finish() +func finish(h): + if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].finish() func take_item(tile: Tile, h): - if hand != null: push_error("already holding an item") + if hand[G.hand_to_index(h)] != null: push_error("already holding an item") var i = tile.take_item() i.take() set_item(i, h) -- cgit v1.2.3-70-g09d2 From 4083df5cfe76e42506c5356cf23d3dc9f3b6e6bf Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 25 Dec 2024 19:37:20 +0100 Subject: variable hand count --- client/game.gd | 14 +++++----- client/global.gd | 9 ++++--- client/multiplayer.gd | 3 ++- client/player/controllable_player.gd | 12 ++++----- client/player/player.gd | 31 +++++++++++----------- locale/en.ini | 1 + pixel-client/src/game.rs | 50 ++++++++++++++++++++++------------- server/bot/src/algos/customer.rs | 13 ++++----- server/bot/src/algos/simple.rs | 13 +++++---- server/bot/src/main.rs | 2 +- server/client-lib/src/lib.rs | 51 ++++++++++++++++++++++++------------ server/protocol/src/helpers.rs | 21 +-------------- server/protocol/src/lib.rs | 8 +++--- server/src/data/mod.rs | 1 + server/src/entity/bot.rs | 2 +- server/src/server.rs | 33 ++++++++++++++--------- 16 files changed, 144 insertions(+), 120 deletions(-) diff --git a/client/game.gd b/client/game.gd index 9a6b8808..9502d2fc 100644 --- a/client/game.gd +++ b/client/game.gd @@ -144,13 +144,13 @@ func handle_packet(p): player_instance.position_ = last_position "move_item": if "player" in p.from and "player" in p.to: - players[p.from.player[0]].pass_to(players[p.to.player[0]], p.from.player[1], p.to.player[1]) + players[p.from.player[0]].pass_to(players[p.to.player[0]], int(p.from.player[1]), int(p.to.player[1])) elif "tile" in p.from and "player" in p.to: var t: Tile = map.get_tile_instance(p.from.tile) - players[p.to.player[0]].take_item(t, p.to.player[1]) + players[p.to.player[0]].take_item(t, int(p.to.player[1])) elif "player" in p.from and "tile" in p.to: var t: Tile = map.get_tile_instance(p.to.tile) - players[p.from.player[0]].put_item(t, p.from.player[1]) + players[p.from.player[0]].put_item(t, int(p.from.player[1])) elif "tile" in p.from and "tile" in p.to: var from_tile2: Tile = map.get_tile_instance(p.from.tile) var to_tile2: Tile = map.get_tile_instance(p.to.tile) @@ -160,13 +160,13 @@ func handle_packet(p): var t: Tile = map.get_tile_instance(p.item.tile) t.progress(p.position, p.speed, p.warn, players.get(p.player)) else: - players[p.item.player[0]].progress(p.position, p.speed, p.warn, p.item.player[1]) + players[p.item.player[0]].progress(p.position, p.speed, p.warn, int(p.item.player[1])) "clear_progress": if "tile" in p.item: var t: Tile = map.get_tile_instance(p.item.tile) t.finish() else: - players[p.item.player[0]].finish(p.item.player[1]) + players[p.item.player[0]].finish(int(p.item.player[1])) "set_item": var location: Dictionary = p["location"] if p.item != null: @@ -180,8 +180,8 @@ func handle_packet(p): else: var pl: Player = players[p.location.player[0]] var h = p.location.player[1] - var i = ItemFactory.produce(item_names[p.item], pl.hand_base[G.hand_to_index(h)]) - i.position = pl.hand_base[G.hand_to_index(h)].global_position + var i = ItemFactory.produce(item_names[p.item], pl.hand_base[h]) + i.position = pl.hand_base[h].global_position add_child(i) i.name = item_names[p.item] pl.set_item(i, h) diff --git a/client/global.gd b/client/global.gd index ce24f85d..93c73e13 100644 --- a/client/global.gd +++ b/client/global.gd @@ -269,7 +269,8 @@ func configure_viewport_aa(vp: Viewport, aa: String) -> void: vp.msaa_3d = Viewport.MSAA_4X vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED -static func hand_to_index(h): - match h: - "left": return 0 - "right": return 1 +static func index_to_hand(i): + match i: + 0: return "left" + 1: return "right" + _: return "unknown" diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 14eaf482..11a5bd84 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -66,6 +66,7 @@ func fix_packet_types(val): if typeof(val[k]) == TYPE_ARRAY and val[k].size() == 2 and typeof(val[k][0]) == TYPE_FLOAT and typeof(val[k][1]) == TYPE_FLOAT: if k in ["tile"]: newval[k] = Vector2i(val[k][0], val[k][1]) elif k in ["pos", "position"]: newval[k] = Vector2(val[k][0], val[k][1]) + else: newval[k] = val[k] # TODO reenable when fixed # elif k in ["player", "id"] and typeof(val[k]) == TYPE_FLOAT: # newval[k] = int(val[k]) @@ -106,7 +107,7 @@ func send_movement(player, pos: Vector2, direction: Vector2, boost: bool): "boost": boost }) -func send_tile_interact(player, pos: Vector2i, edge: bool, hand: String): +func send_tile_interact(player, pos: Vector2i, edge: bool, hand: int): @warning_ignore("incompatible_ternary") send_packet({ "type": "interact", diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd index d241bc2e..99625762 100644 --- a/client/player/controllable_player.gd +++ b/client/player/controllable_player.gd @@ -190,13 +190,13 @@ func _on_vibration_timeout(): Input.vibrate_handheld(100, vibration_strength) vibration_timer.start() -func put_item(tile: Tile, h): +func put_item(tile: Tile, h: int): super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) Input.vibrate_handheld(75, 0.1) -func take_item(tile: Tile, h): +func take_item(tile: Tile, h: int): super(tile, h) if Global.get_setting("gameplay.vibration"): Input.start_joy_vibration(0, 0.1, 0.0, 0.075) @@ -210,18 +210,18 @@ func interact(): # clear last interaction if target has moved since if last_interaction != null and not last_interaction == target: - game.mp.send_tile_interact(game.player_id, last_interaction, false, "left") + game.mp.send_tile_interact(game.player_id, last_interaction, false, 0) marker.set_interacting(false) last_interaction = null marker.set_interactive(game.get_tile_interactive(target)) marker_target = tile.item_base.global_position - for h in ["left", "right"]: - if Input.is_action_just_pressed("interact_"+h) and last_interaction == null: + for h in [0, 1]: + if Input.is_action_just_pressed("interact_"+G.index_to_hand(h)) and last_interaction == null: last_interaction = target game.mp.send_tile_interact(game.player_id, target, true, h) tile.interact() marker.set_interacting(true) - if Input.is_action_just_released("interact_"+h): + if Input.is_action_just_released("interact_"+G.index_to_hand(h)): last_interaction = null game.mp.send_tile_interact(game.player_id, target, false, h) marker.set_interacting(false) diff --git a/client/player/player.gd b/client/player/player.gd index 223d2c88..39b9fb3e 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -50,6 +50,7 @@ var current_item_message = null var _anim_angle: float = 0.0 var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] +const DEFAULT_HAND_BASE_POSITION_BOTH: Vector3 = Vector3(0, .425, .4) const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) @@ -100,42 +101,42 @@ func update_username_tag(state): tag.text = username tag.visible = state -func set_item(i: Item, h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].remove() +func set_item(i: Item, h: int): + if hand[h] != null: hand[h].remove() if i != null: @warning_ignore("static_called_on_instance") hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) @warning_ignore("static_called_on_instance") hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null - hand[G.hand_to_index(h)] = i - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].owned_by = hand_base[G.hand_to_index(h)] + hand[h] = i + if hand[h] != null: hand[h].owned_by = hand_base[h] -func remove_item(h): - var i = hand[G.hand_to_index(h)] +func remove_item(h: int): + var i = hand[h] if i == null: push_error("holding nothing") - hand[G.hand_to_index(h)] = null + hand[h] = null character.holding = false return i -func progress(position__: float, speed: float, warn: bool, h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].progress(position__, speed, warn) +func progress(position__: float, speed: float, warn: bool, h: int): + if hand[h] != null: hand[h].progress(position__, speed, warn) -func finish(h): - if hand[G.hand_to_index(h)] != null: hand[G.hand_to_index(h)].finish() +func finish(h: int): + if hand[h] != null: hand[h].finish() -func take_item(tile: Tile, h): - if hand[G.hand_to_index(h)] != null: push_error("already holding an item") +func take_item(tile: Tile, h: int): + if hand[h] != null: push_error("already holding an item") var i = tile.take_item() i.take() set_item(i, h) -func put_item(tile: Tile, h): +func put_item(tile: Tile, h: int): var i = remove_item(h) i.put() tile.put_item(i) -func pass_to(player: Player, hfrom, hto): +func pass_to(player: Player, hfrom: int, hto: int): var i = remove_item(hfrom) i.player_owned_timer = 0 if player.hand != null: diff --git a/locale/en.ini b/locale/en.ini index c99b8e31..041e79bc 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -223,6 +223,7 @@ s.error.must_be_alone=You must be alone in this server to reload s.error.no_info=No information available. s.error.no_player=Player does not exist. s.error.no_tile=Tile does not exist. +s.error.no_hand=Hand does not exist. s.error.packet_not_supported=Packet not supported in this session. s.error.packet_sender_invalid=Packet sent to a player that is not owned by this connection. s.error.quoting_invalid=Command quoting invalid diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs index 4dc43b72..cbcc62d5 100644 --- a/pixel-client/src/game.rs +++ b/pixel-client/src/game.rs @@ -156,13 +156,13 @@ impl Game { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: Some(self.players[&self.my_id].movement.get_interact_target()), - hand: Hand::Left, + hand: Hand(0), }); } else { self.network.queue_out.push_back(PacketS::Interact { player: self.my_id, pos: None, - hand: Hand::Left, + hand: Hand(0), }); } self.interacting = interact; @@ -331,19 +331,23 @@ impl Game { } } PacketC::MoveItem { from, to } => { - let mut item = self.get_item(from).take(); + let mut item = self.get_item(from).unwrap().take(); if let Some(item) = &mut item { item.parent_position = self.get_location_position(to); } - *self.get_item(to) = item; + *self.get_item(to).unwrap() = item; } PacketC::SetItem { location, item } => { let position = self.get_location_position(location); let slot = match location { ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] - } + ItemLocation::Player(pid, hand) => self + .players + .get_mut(&pid) + .unwrap() + .items + .get_mut(hand.0) + .unwrap(), }; self.items_removed.extend(slot.take()); *slot = item.map(|kind| Item { @@ -354,7 +358,13 @@ impl Game { active: None, }) } - PacketC::ClearProgress { item } => self.get_item(item).as_mut().unwrap().active = None, + PacketC::ClearProgress { item } => { + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = None; + } + } + } PacketC::SetProgress { item, position, @@ -362,13 +372,17 @@ impl Game { player, warn, } => { - self.get_item(item).as_mut().unwrap().active = Some(Involvement { - position, - speed, - player, - warn, - recipe: RecipeIndex(0), - }); + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = Some(Involvement { + position, + speed, + player, + warn, + recipe: RecipeIndex(0), + }); + } + } } PacketC::ServerMessage { .. } => { // TODO @@ -392,11 +406,11 @@ impl Game { } } - pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { + pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option> { match location { - ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, + ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item), ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?) } } } diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs index 826ab534..b0ece9dd 100644 --- a/server/bot/src/algos/customer.rs +++ b/server/bot/src/algos/customer.rs @@ -313,7 +313,7 @@ impl CustomerState { PacketS::Interact { pos: Some(pos), player: me, - hand: Hand::Left, + hand: Hand(0), }, PacketS::ApplyScore(Score { demands_completed: 1, @@ -323,7 +323,7 @@ impl CustomerState { PacketS::Interact { pos: None, player: me, - hand: Hand::Left, + hand: Hand(0), }, ], ..Default::default() @@ -356,7 +356,7 @@ impl CustomerState { extra: vec![PacketS::ReplaceHand { player: me, item: demand.output, - hand: Hand::Left, + hand: Hand(0), }], ..Default::default() }; @@ -375,7 +375,8 @@ impl CustomerState { if game .players .get(&me) - .is_some_and(|pl| pl.items[Hand::Left.index()].is_none()) + .is_some_and(|pl| pl.items[0].is_none()) + // TODO index out of bounds? { if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), *origin) { *self = CustomerState::Exiting { path }; @@ -390,12 +391,12 @@ impl CustomerState { PacketS::Interact { player: me, pos: Some(*table), - hand: Hand::Left, + hand: Hand(0), }, PacketS::Interact { player: me, pos: None, - hand: Hand::Left, + hand: Hand(0), }, ], direction, diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index 5a7e61a0..452f59d3 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -21,7 +21,7 @@ use crate::{ }; use hurrycurry_client_lib::Game; use hurrycurry_protocol::{ - glam::IVec2, Hand, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, + glam::IVec2, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex, }; use log::{debug, warn}; @@ -106,17 +106,16 @@ impl State for Simple { impl Context<'_, S> { pub fn is_hand_item(&self, item: ItemIndex) -> bool { - self.game.players.get(&self.me).is_some_and(|p| { - p.items[Hand::Left.index()] - .as_ref() - .is_some_and(|i| i.kind == item) - }) + self.game + .players + .get(&self.me) + .is_some_and(|p| p.items[0].as_ref().is_some_and(|i| i.kind == item)) } pub fn is_hand_occupied(&self) -> bool { self.game .players .get(&self.me) - .map(|p| p.items[Hand::Left.index()].is_some()) + .map(|p| p.items[0].is_some()) .unwrap_or(false) } pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 0ffbf4b6..918be7e1 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -109,7 +109,7 @@ fn main() -> Result<()> { network.queue_out.push_back(PacketS::Interact { player: b.id, pos: interact, - hand: Hand::Left, + hand: Hand(0), }) } network.queue_out.push_back(PacketS::Movement { diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs index 54c7cd6e..a40eafc1 100644 --- a/server/client-lib/src/lib.rs +++ b/server/client-lib/src/lib.rs @@ -20,7 +20,8 @@ pub mod network; pub mod spatial_index; use hurrycurry_protocol::{ - glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex + glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message, + MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex, }; use spatial_index::SpatialIndex; use std::{ @@ -54,7 +55,7 @@ pub struct Player { pub class: PlayerClass, pub character: i32, pub interacting: Option<(IVec2, Hand)>, - pub items: [Option; 2], + pub items: Vec>, pub communicate_persist: Option<(Message, MessageTimeout)>, pub movement: MovementBase, @@ -94,7 +95,7 @@ impl Game { character, class, interacting: None, - items: [const { None }; 2], + items: (0..self.data.hand_count).map(|_| None).collect(), communicate_persist: None, movement: MovementBase::new(position), }, @@ -116,15 +117,27 @@ impl Game { p.movement.rotation = rot; } } - PacketC::MoveItem { from, to } => { - *self.get_item(to) = self.get_item(from).take(); + if let Some(item) = self.get_item(to).map(|e| e.take()) { + if let Some(to) = self.get_item(from) { + *to = item; + } else { + // TODO perhaps restore to original position? + } + } } PacketC::SetItem { location, item } => { - *self.get_item(location) = item.map(|kind| Item { kind, active: None }); + let location = self.get_item(location); + if let Some(location) = location { + *location = item.map(|kind| Item { kind, active: None }); + } } PacketC::ClearProgress { item } => { - self.get_item(item).as_mut().unwrap().active = None; + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = None; + } + } } PacketC::SetProgress { item, @@ -133,13 +146,17 @@ impl Game { speed, warn, } => { - self.get_item(item).as_mut().unwrap().active = Some(Involvement { - player, - speed, - warn, - position, - recipe: RecipeIndex(0), - }); + if let Some(slot) = self.get_item(item) { + if let Some(item) = slot { + item.active = Some(Involvement { + player, + speed, + warn, + position, + recipe: RecipeIndex(0), + }); + } + } } PacketC::UpdateMap { tile, @@ -224,11 +241,11 @@ impl Game { }); } - pub fn get_item(&mut self, location: ItemLocation) -> &mut Option { + pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option> { match location { - ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, + ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item), ItemLocation::Player(pid, hand) => { - &mut self.players.get_mut(&pid).unwrap().items[hand.index()] + Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?) } } } diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs index 21835101..b85c2f84 100644 --- a/server/protocol/src/helpers.rs +++ b/server/protocol/src/helpers.rs @@ -106,26 +106,7 @@ impl Display for ItemLocation { impl Display for Hand { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Hand::Left => "left", - Hand::Right => "right", - }) - } -} - -impl Hand { - pub fn index(&self) -> usize { - match self { - Hand::Left => 0, - Hand::Right => 1, - } - } - pub fn from_index(i: usize) -> Self { - match i { - 0 => Hand::Left, - 1 => Hand::Right, - _ => Hand::Left, - } + write!(f, "h{}", self.0) } } diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 5c7ddeb5..74d463a1 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -72,11 +72,8 @@ pub struct RecipeIndex(pub usize); pub struct DemandIndex(pub usize); #[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] -pub enum Hand { - Left, - Right, -} +#[serde(transparent)] +pub struct Hand(pub usize); #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct MapMetadata { @@ -105,6 +102,7 @@ pub struct Gamedata { pub bot_algos: Vec, pub recipes: Vec, pub demands: Vec, + pub hand_count: usize, } #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 57051fe9..15fdfa6b 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -314,6 +314,7 @@ pub fn build_data( item_names, demands, tile_names, + hand_count: 2, }, Serverdata { initial_map, diff --git a/server/src/entity/bot.rs b/server/src/entity/bot.rs index 368f8c9d..6e6c9162 100644 --- a/server/src/entity/bot.rs +++ b/server/src/entity/bot.rs @@ -72,7 +72,7 @@ impl Entity for BotDriver { c.packet_in.push_back(PacketS::Interact { player: self.id, pos: input.interact, - hand: Hand::Left, + hand: Hand(0), }) } c.packet_in.push_back(PacketS::Movement { diff --git a/server/src/server.rs b/server/src/server.rs index 19153864..0889cd71 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -164,10 +164,9 @@ impl GameServerExt for Game { name: player.name.clone(), }); for (i, item) in player.items.iter().enumerate() { - let hand = Hand::from_index(i); if let Some(item) = &item { out.push(PacketC::SetItem { - location: ItemLocation::Player(id, hand), + location: ItemLocation::Player(id, Hand(i)), item: Some(item.kind), }); if let Some(Involvement { @@ -180,7 +179,7 @@ impl GameServerExt for Game { { out.push(PacketC::SetProgress { player, - item: ItemLocation::Player(id, hand), + item: ItemLocation::Player(id, Hand(i)), position, speed, warn, @@ -255,7 +254,7 @@ impl GameServerExt for Game { self.players.insert( id, Player { - items: [const { None }; 2], + items: (0..self.data.hand_count).map(|_| None).collect(), character, class, movement: MovementBase::new(position), @@ -531,14 +530,17 @@ impl Server { return Err(tre!("s.error.customer_interact")); } + let this_hslot = this.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; + let other_hslot = other.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; + interact( &self.game.data, edge, None, Some(pid), - &mut this.items[hand.index()], + this_hslot, ItemLocation::Player(base_pid, hand), - &mut other.items[hand.index()], + other_hslot, ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, @@ -552,6 +554,11 @@ impl Server { .get_mut(&pid) .ok_or(tre!("s.error.no_player"))?; + let hslot = player + .items + .get_mut(hand.0) + .ok_or(tre!("s.error.no_hand"))?; + interact( &self.game.data, edge, @@ -559,7 +566,7 @@ impl Server { Some(pid), &mut tile.item, ItemLocation::Tile(pos), - &mut player.items[hand.index()], + hslot, ItemLocation::Player(pid, hand), &mut self.game.score, &mut self.score_changed, @@ -602,10 +609,12 @@ impl Server { } PacketS::ReplaceHand { item, player, hand } => { let pdata = self.game.players.get_mut(&player).ok_or(tre!(""))?; - pdata.items[hand.index()] = item.map(|i| Item { - kind: i, - active: None, - }); + if let Some(slot) = pdata.items.get_mut(hand.0) { + *slot = item.map(|i| Item { + kind: i, + active: None, + }); + } self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Player(player, hand), item, @@ -680,7 +689,7 @@ impl Server { &self.gamedata_index, None, item, - ItemLocation::Player(pid, Hand::from_index(i)), + ItemLocation::Player(pid, Hand(i)), &mut self.game.score, &mut self.score_changed, &mut self.packet_out, -- cgit v1.2.3-70-g09d2 From 502b284be74445d3cecace4dff82f37aee32c3e5 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 25 Dec 2024 19:58:35 +0100 Subject: map-specific two-handed mode --- client/game.gd | 2 ++ client/player/player.gd | 37 +++++++++++++++++++++++-------------- data/maps/village.yaml | 1 + server/src/data/mod.rs | 3 ++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/client/game.gd b/client/game.gd index 9502d2fc..6c152d8a 100644 --- a/client/game.gd +++ b/client/game.gd @@ -53,6 +53,7 @@ var tile_collide: Array = [] var tile_interact: Array = [] var maps: Array = [] var bot_algos: Array +var hand_count = 0 var text_message_history: Array[TextMessage] = [] var join_state: JoinState = JoinState.SPECTATING @@ -95,6 +96,7 @@ func handle_packet(p): tile_interact = p["data"]["tile_interact"] maps = p["data"]["maps"] bot_algos = p["data"]["bot_algos"] + hand_count = p["data"]["hand_count"] tile_index_by_name.clear() for id in tile_names.size(): diff --git a/client/player/player.gd b/client/player/player.gd index 39b9fb3e..031cba29 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -42,15 +42,14 @@ var marker_target = Vector3(0, 0, 0) var clear_timer: Timer = Timer.new() var hand = [null, null] -var hand_base = [Node3D.new(), Node3D.new()] +var hand_base var character_idx: int var is_customer: bool var current_item_message = null var _anim_angle: float = 0.0 -var hand_base_position = [DEFAULT_HAND_BASE_POSITION_LEFT, DEFAULT_HAND_BASE_POSITION_RIGHT] -const DEFAULT_HAND_BASE_POSITION_BOTH: Vector3 = Vector3(0, .425, .4) +const DEFAULT_HAND_BASE_POSITION_CENTER: Vector3 = Vector3(0, .425, .4) const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) @@ -64,12 +63,22 @@ func _init(_id: int, new_name: String, pos: Vector2, new_character_idx: int, new game = new_game username = new_name - hand_base[0].name = "HandBaseLeft" - hand_base[1].name = "HandBaseRight" - hand_base[0].position = hand_base_position[0] - hand_base[1].position = hand_base_position[1] - movement_base.add_child(hand_base[0]) - movement_base.add_child(hand_base[1]) + if game.hand_count == 1: + var center = Node3D.new() + center.name = "HandBaseCenter" + center.position = DEFAULT_HAND_BASE_POSITION_CENTER + hand_base = [center] + else: + var left = Node3D.new() + var right = Node3D.new() + left.name = "HandBaseLeft" + right.name = "HandBaseRight" + left.position = DEFAULT_HAND_BASE_POSITION_LEFT + right.position = DEFAULT_HAND_BASE_POSITION_RIGHT + hand_base = [left, right] + + for h in hand_base: + movement_base.add_child(h) movement_base.add_child(chat_bubble) movement_base.add_child(item_bubble) @@ -103,11 +112,11 @@ func update_username_tag(state): func set_item(i: Item, h: int): if hand[h] != null: hand[h].remove() - if i != null: - @warning_ignore("static_called_on_instance") - hand_base_position[0] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) - @warning_ignore("static_called_on_instance") - hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) + # if i != null: + # @warning_ignore("static_called_on_instance") + # hand_base_position[h] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) + # @warning_ignore("static_called_on_instance") + # hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null hand[h] = i if hand[h] != null: hand[h].owned_by = hand_base[h] diff --git a/data/maps/village.yaml b/data/maps/village.yaml index 9538ae9d..56cd49b0 100644 --- a/data/maps/village.yaml +++ b/data/maps/village.yaml @@ -17,6 +17,7 @@ # along with this program. If not, see . # score_baseline: 200 +hand_count: 2 map: - "''''''''''''''~_''''''''''''''" - "''''''''''''''__''''''''''''''" diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 15fdfa6b..64f40217 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -73,6 +73,7 @@ pub struct MapDecl { walkable: Vec, chef_spawn: char, customer_spawn: char, + #[serde(default)] hand_count: Option, #[serde(default)] entities: Vec, #[serde(default)] tile_entities: HashMap, #[serde(default)] score_baseline: i64, @@ -314,7 +315,7 @@ pub fn build_data( item_names, demands, tile_names, - hand_count: 2, + hand_count: map_in.hand_count.unwrap_or(1), }, Serverdata { initial_map, -- cgit v1.2.3-70-g09d2 From 86d9b8be09d30216666758e7e1b4ae7f0d69a1eb Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 25 Dec 2024 20:01:04 +0100 Subject: bump proto version --- Cargo.lock | 2 +- client/multiplayer.gd | 4 ++-- server/protocol/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 967b9358..b3d1ec3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,7 +1194,7 @@ dependencies = [ [[package]] name = "hurrycurry-protocol" -version = "7.5.0" +version = "8.0.0" dependencies = [ "bincode", "glam", diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 11a5bd84..5a9406ab 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -21,8 +21,8 @@ extends Node signal packet(packet: Dictionary) signal connection_closed() -static var VERSION_MAJOR: int = 7 -static var VERSION_MINOR: int = 5 +static var VERSION_MAJOR: int = 8 +static var VERSION_MINOR: int = 0 var connected := false var socket := WebSocketPeer.new() diff --git a/server/protocol/Cargo.toml b/server/protocol/Cargo.toml index 44533f9d..b6da120a 100644 --- a/server/protocol/Cargo.toml +++ b/server/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hurrycurry-protocol" -version = "7.5.0" +version = "8.0.0" edition = "2021" [dependencies] -- cgit v1.2.3-70-g09d2