aboutsummaryrefslogtreecommitdiff
path: root/pixel-client/src/game.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pixel-client/src/game.rs')
-rw-r--r--pixel-client/src/game.rs518
1 files changed, 0 insertions, 518 deletions
diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs
deleted file mode 100644
index d71b676e..00000000
--- a/pixel-client/src/game.rs
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- Hurry Curry! - a game about cooking
- Copyright (C) 2025 Hurry Curry! Contributors
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, version 3 of the License only.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-use crate::{
- config::Config,
- helper::InterpolateExt,
- render::{
- misc::MiscTextures,
- sprite::{Sprite, SpriteDraw},
- AtlasLayout, Renderer,
- },
- tilemap::Tilemap,
- State,
-};
-use hurrycurry_client_lib::{network::sync::Network, spatial_index::SpatialIndex, Involvement};
-use hurrycurry_protocol::{
- glam::{IVec2, Vec2},
- movement::MovementBase,
- Character, Gamedata, Hand, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS,
- PlayerClass, PlayerID, RecipeIndex, Score, TileIndex,
-};
-use log::{info, warn};
-use sdl2::{
- keyboard::{KeyboardState, Scancode},
- rect::Rect,
-};
-use std::collections::{HashMap, HashSet};
-
-pub struct Game {
- network: Network,
-
- data: Gamedata,
- tiles: HashMap<IVec2, Tile>,
- tilemap: Tilemap,
- walkable: HashSet<IVec2>,
- players: HashMap<PlayerID, Player>,
- players_spatial_index: SpatialIndex<PlayerID>,
- items_removed: Vec<Item>,
- my_id: PlayerID,
-
- camera_center: Vec2,
- misc_textures: MiscTextures,
- item_sprites: Vec<Sprite>,
- movement_send_cooldown: f32,
- interacting: bool,
- score: Score,
-}
-
-pub struct Tile {
- _kind: TileIndex,
- item: Option<Item>,
-}
-
-pub struct Player {
- movement: MovementBase,
- items: [Option<Item>; 2],
- message_persist: Option<(Message, MessageTimeout)>,
- _name: String,
- _character: Character,
- _class: PlayerClass,
- interact_target_anim: Vec2,
- interact_target_anim_pressed: f32,
-}
-
-pub struct Item {
- position: Vec2,
- parent_position: Vec2,
- kind: ItemIndex,
- alive: f32,
- active: Option<Involvement>,
-}
-
-impl Game {
- pub fn new(mut network: Network, config: &Config, layout: &AtlasLayout) -> Self {
- network.queue_out.push_back(PacketS::Join {
- id: None,
- name: config.username.clone(),
- class: PlayerClass::Chef,
- character: Character::default(),
- position: None,
- });
-
- Self {
- network,
- tiles: HashMap::new(),
- players: HashMap::new(),
- tilemap: Tilemap::default(),
- my_id: PlayerID(0),
- data: Gamedata::default(),
- walkable: HashSet::new(),
- movement_send_cooldown: 0.,
- misc_textures: MiscTextures::init(layout),
- item_sprites: Vec::new(),
- items_removed: Vec::new(),
- interacting: false,
- score: Score::default(),
- players_spatial_index: SpatialIndex::default(),
- camera_center: Vec2::ZERO,
- }
- }
-
- pub fn tick(
- &mut self,
- dt: f32,
- keyboard: &KeyboardState,
- layout: &AtlasLayout,
- ) -> Option<Box<State>> {
- if let Err(e) = self.network.poll() {
- eprintln!("network error: {e}");
- return Some(Box::new(State::Quit));
- }
-
- for packet in self.network.queue_in.drain(..).collect::<Vec<_>>() {
- self.packet_in(packet, layout);
- }
-
- let mut direction = IVec2::new(
- keyboard.is_scancode_pressed(Scancode::D) as i32
- - keyboard.is_scancode_pressed(Scancode::A) as i32,
- keyboard.is_scancode_pressed(Scancode::S) as i32
- - keyboard.is_scancode_pressed(Scancode::W) as i32,
- )
- .as_vec2();
- let boost = keyboard.is_scancode_pressed(Scancode::K);
- let interact = keyboard.is_scancode_pressed(Scancode::Space)
- | keyboard.is_scancode_pressed(Scancode::J);
-
- if interact {
- direction *= 0.;
- }
-
- self.movement_send_cooldown -= dt;
- let send_movement = self.movement_send_cooldown < 0.;
- if send_movement {
- self.movement_send_cooldown += 0.04
- }
-
- self.score.time_remaining -= dt as f64;
- self.score.time_remaining -= self.score.time_remaining.max(0.);
-
- if interact != self.interacting {
- if interact {
- 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(0),
- });
- } else {
- self.network.queue_out.push_back(PacketS::Interact {
- player: self.my_id,
- pos: None,
- hand: Hand(0),
- });
- }
- self.interacting = interact;
- }
-
- if let Some(player) = self.players.get_mut(&self.my_id) {
- player.movement.input(direction, boost);
-
- if send_movement {
- self.network
- .queue_out
- .push_back(player.movement.movement_packet_s(self.my_id));
- }
-
- player.interact_target_anim.exp_to(
- player.movement.get_interact_target().as_vec2() + Vec2::new(0., -0.4),
- dt * 20.,
- );
- player
- .interact_target_anim_pressed
- .exp_to(if interact { 1. } else { 0. }, dt * 10.);
-
- self.camera_center.exp_to(player.movement.position, dt * 5.);
- }
-
- for (&pid, player) in &mut self.players {
- player.movement.update(&self.walkable, dt);
- if let Some((_, timeout)) = &mut player.message_persist {
- timeout.remaining -= dt;
- if timeout.remaining < 0. {
- player.message_persist = None;
- }
- }
- self.players_spatial_index
- .update_entry(pid, player.movement.position);
- }
-
- self.players_spatial_index.all(|p1, pos1| {
- self.players_spatial_index.query(pos1, 2., |p2, _pos2| {
- if p1 != p2 {
- if let [Some(a), Some(b)] = self.players.get_disjoint_mut([&p1, &p2]) {
- a.movement.collide(&mut b.movement, dt)
- }
- }
- })
- });
-
- for player in self.players.values_mut() {
- for item in player.items.iter_mut().flatten() {
- item.parent_position = player.movement.position;
- item.tick(1., dt);
- }
- }
- for tile in self.tiles.values_mut() {
- if let Some(item) = &mut tile.item {
- item.tick(1., dt)
- }
- }
- self.items_removed.retain_mut(|i| {
- i.tick(0., dt);
- i.alive > 0.01
- });
-
- None
- }
-
- pub fn packet_in(&mut self, packet: PacketC, layout: &AtlasLayout) {
- match packet {
- PacketC::Joined { id } => self.my_id = id,
- PacketC::Data { data } => {
- self.tilemap.init(&data.tile_names, layout);
- self.item_sprites = data
- .item_names
- .iter()
- .map(|name| {
- Sprite::new(
- layout
- .get(&format!("{name}+a"))
- .copied()
- .unwrap_or_else(|| {
- warn!("no sprite for item {name:?}");
- Rect::new(0, 0, 32, 24)
- }),
- Vec2::new(0., 0.0),
- 0.1,
- )
- })
- .collect();
- self.data = data;
- }
- PacketC::UpdateMap {
- tile,
- kind,
- neighbors,
- } => {
- if let Some(kind) = kind {
- self.tiles.insert(
- tile,
- Tile {
- _kind: kind,
- item: None,
- },
- );
- if self.data.tile_collide[kind.0] {
- self.walkable.remove(&tile);
- } else {
- self.walkable.insert(tile);
- }
- } else {
- self.tiles.remove(&tile);
- self.walkable.remove(&tile);
- }
- self.tilemap.set(tile, kind, neighbors);
- }
- PacketC::AddPlayer {
- id,
- position,
- character,
- name,
- class,
- } => {
- info!("add player {} {name:?}", id.0);
- self.players.insert(
- id,
- Player {
- interact_target_anim: position,
- interact_target_anim_pressed: 0.,
- _class: class,
- _character: character,
- _name: name,
- message_persist: None,
- items: [const { None }; 2],
- movement: MovementBase {
- position,
- input_direction: Vec2::ZERO,
- input_boost: false,
- facing: Vec2::X,
- rotation: 0.,
- velocity: Vec2::ZERO,
- boosting: false,
- stamina: 0.,
- },
- },
- );
- }
- PacketC::RemovePlayer { id } => {
- info!("remove player {}", id.0);
- self.players_spatial_index.remove_entry(id);
- self.players.remove(&id);
- }
- PacketC::Movement {
- player,
- pos,
- rot,
- boost,
- dir,
- } => {
- if player != self.my_id {
- if let Some(p) = self.players.get_mut(&player) {
- p.movement.position = pos;
- p.movement.rotation = rot;
- p.movement.input(dir, boost);
- }
- }
- }
- PacketC::MoveItem { from, to } => {
- 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).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) => self
- .players
- .get_mut(&pid)
- .unwrap()
- .items
- .get_mut(hand.0)
- .unwrap(),
- };
- self.items_removed.extend(slot.take());
- *slot = item.map(|kind| Item {
- kind,
- parent_position: position,
- alive: 0.,
- position,
- active: None,
- })
- }
- PacketC::ClearProgress { item } => {
- if let Some(Some(item)) = self.get_item(item) {
- item.active = None;
- }
- }
- PacketC::SetProgress {
- item,
- position,
- speed,
- player,
- warn,
- } => {
- if let Some(Some(item)) = self.get_item(item) {
- item.active = Some(Involvement {
- position,
- speed,
- player,
- warn,
- recipe: RecipeIndex(0),
- });
- }
- }
- PacketC::ServerMessage { .. } => {
- // TODO
- }
- PacketC::Score(score) => {
- self.score = score;
- }
- PacketC::SetIngame { state: _, lobby: _ } => {
- // TODO
- }
- PacketC::Communicate {
- player,
- message,
- timeout: Some(timeout),
- } => {
- if let Some(player) = self.players.get_mut(&player) {
- player.message_persist = message.map(|m| (m, timeout));
- }
- }
- _ => (),
- }
- }
-
- pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option<Item>> {
- match location {
- ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item),
- ItemLocation::Player(pid, hand) => {
- Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?)
- }
- }
- }
- 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,
- }
- }
-
- pub fn draw(&self, ctx: &mut Renderer) {
- ctx.set_world_view(
- -self.camera_center + (ctx.size / ctx.get_world_scale() / 2.),
- ctx.size.min_element() / 32. / 10.,
- );
-
- self.tilemap.draw(ctx);
-
- if let Some(me) = self.players.get(&self.my_id) {
- ctx.draw_world(
- self.misc_textures
- .interact_target
- .at(me.interact_target_anim)
- .tint(
- 100,
- 100 + (me.interact_target_anim_pressed * 150.) as u8,
- 100 + ((1. - me.interact_target_anim_pressed) * 150.) as u8,
- ),
- )
- }
-
- for p in self.players.values() {
- p.draw(ctx, &self.item_sprites)
- }
- for tile in self.tiles.values() {
- if let Some(item) = &tile.item {
- item.draw(ctx, &self.item_sprites)
- }
- }
- for item in &self.items_removed {
- item.draw(ctx, &self.item_sprites)
- }
- }
-}
-
-impl Item {
- pub fn tick(&mut self, alive: f32, dt: f32) {
- self.position.exp_to(self.parent_position, dt * 20.);
- self.alive.exp_to(alive, dt * 20.);
- if let Some(active) = &mut self.active {
- active.position += active.speed * dt;
- }
- }
- pub fn draw(&self, ctx: &mut Renderer, item_sprites: &[Sprite]) {
- ctx.draw_world(
- item_sprites[self.kind.0]
- .at(self.position)
- .alpha(self.alive),
- );
- if let Some(Involvement { position, warn, .. }) = self.active {
- let (bg, fg) = if warn {
- ([100, 0, 0, 200], [255, 0, 0, 200])
- } else {
- ([0, 100, 0, 200], [0, 255, 0, 200])
- };
- ctx.draw_world(SpriteDraw::overlay(
- ctx.misc_textures.solid,
- self.position + Vec2::new(-0.5, -1.3),
- Vec2::new(1., 0.2),
- Some(bg),
- ));
- ctx.draw_world(SpriteDraw::overlay(
- ctx.misc_textures.solid,
- self.position + Vec2::new(-0.5, -1.3),
- Vec2::new(position, 0.2),
- Some(fg),
- ))
- }
- }
-}
-
-impl Player {
- pub fn draw(&self, ctx: &mut Renderer, item_sprites: &[Sprite]) {
- ctx.draw_world(
- match self._class {
- PlayerClass::Chef | PlayerClass::Bot => &ctx.misc_textures.chef,
- _ => &ctx.misc_textures.customer,
- }
- .at(self.movement.position),
- );
- if let Some((message, _timeout)) = &self.message_persist {
- match message {
- Message::Text(_) => (), // TODO
- Message::Item(item) => {
- ctx.draw_world(ctx.misc_textures.itembubble.at(self.movement.position));
- ctx.draw_world(
- item_sprites[item.0]
- .at(self.movement.position)
- .elevate(1.2)
- .scale(0.8),
- );
- }
- _ => (),
- }
- }
- for item in self.items.iter().flatten() {
- item.draw(ctx, item_sprites)
- }
- }
-}