/* Hurry Curry! - a game about cooking Copyright 2024 metamuffin 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 . */ use hurrycurry_client_lib::{Involvement, Item}; use hurrycurry_protocol::{Gamedata, ItemLocation, PacketC, Recipe, Score, TileIndex}; use log::{info, warn}; use std::collections::VecDeque; pub fn interact( data: &Gamedata, edge: bool, tile: Option, this: &mut Option, this_loc: ItemLocation, other: &mut Option, other_loc: ItemLocation, score: &mut Score, score_changed: &mut bool, automated: bool, packet_out: &mut VecDeque, ) { let interactable = automated || tile .map(|tile| data.is_tile_interactable(tile)) .unwrap_or(true); if interactable && other.is_none() { if let Some(item) = this { if let Some(active) = &mut item.active { let recipe = &data.recipe(active.recipe); if recipe.supports_tile(tile) { if let Recipe::Active { outputs, speed, .. } = recipe { if edge { active.speed += speed; } else { active.speed -= speed; } if active.position >= 1. { let this_had_item = this.is_some(); let other_had_item = other.is_some(); *other = outputs[0].map(|kind| Item { kind, active: None }); *this = outputs[1].map(|kind| Item { kind, active: None }); produce( this_had_item, other_had_item, this, this_loc, other, other_loc, score_changed, packet_out, ); } else { packet_out.push_back(PacketC::SetProgress { item: this_loc, position: active.position, speed: active.speed, warn: active.warn, }); } return; } } } } } if !edge { return; } if interactable { for (ri, recipe) in data.recipes() { if !recipe.supports_tile(tile) { continue; } match recipe { Recipe::Active { input, speed, .. } => { if other.is_none() { if let Some(item) = this { if item.kind == *input && item.active.is_none() { info!("start active recipe {ri:?}"); item.active = Some(Involvement { recipe: ri, speed: *speed, position: 0., warn: false, }); } } } if this.is_none() { if let Some(item) = &other { if item.kind == *input { let mut item = other.take().unwrap(); if item.active.is_some() { warn!("interact recipe tested when already active") } else { info!("start active recipe {ri:?}"); item.active = Some(Involvement { recipe: ri, speed: *speed, position: 0., warn: false, }); } *this = Some(item); score.active_recipes += 1; packet_out.push_back(PacketC::MoveItem { from: other_loc, to: this_loc, }); packet_out.push_back(PacketC::SetProgress { item: this_loc, position: 0., speed: *speed, warn: false, }); return; } } } } Recipe::Instant { inputs, outputs, points: pd, .. } => { let on_tile = this.as_ref().map(|i| i.kind); let in_hand = other.as_ref().map(|i| i.kind); let ok = inputs[0] == on_tile && inputs[1] == in_hand; let ok_rev = inputs[1] == on_tile && inputs[0] == in_hand; if ok || ok_rev { info!("instant recipe {ri:?} reversed={ok_rev}"); let ok_rev = ok_rev as usize; let this_had_item = this.is_some(); let other_had_item = other.is_some(); *other = outputs[1 - ok_rev].map(|kind| Item { kind, active: None }); *this = outputs[ok_rev].map(|kind| Item { kind, active: None }); score.points += pd; score.instant_recipes += 1; *score_changed = true; produce( this_had_item, other_had_item, this, this_loc, other, other_loc, score_changed, packet_out, ); return; } } _ => (), } } } if interactable && this.is_none() { if let Some(item) = other.take() { *this = Some(item); packet_out.push_back(PacketC::MoveItem { from: other_loc, to: this_loc, }); return; } } if other.is_none() { if let Some(item) = this.take() { *other = Some(item); packet_out.push_back(PacketC::MoveItem { from: this_loc, to: other_loc, }); return; } } } pub enum TickEffect { Progress { speed: f32, position: f32, warn: bool, }, ClearProgress, Produce, } pub fn tick_slot( dt: f32, data: &Gamedata, tile: Option, slot: &mut Option, slot_loc: ItemLocation, score: &mut Score, score_changed: &mut bool, packet_out: &mut VecDeque, ) { if let Some(item) = slot { if let Some(a) = &mut item.active { let r = &data.recipe(a.recipe); let prev_speed = a.speed; if r.supports_tile(tile) { if a.speed <= 0. { if let Recipe::Passive { speed, .. } = &data.recipe(a.recipe) { a.speed = *speed; } } } else if let Some(revert_speed) = r.revert_speed() { a.speed = -revert_speed } else { a.speed = 0.; } if a.position < 0. { item.active = None; packet_out.push_back(PacketC::ClearProgress { item: slot_loc }); return; } if a.position >= 1. { if let Recipe::Passive { output, .. } = &data.recipe(a.recipe) { *slot = output.map(|kind| Item { kind, active: None }); score.passive_recipes += 1; *score_changed = true; packet_out.push_back(PacketC::SetProgress { warn: false, item: slot_loc, position: 1., speed: 0., }); packet_out.push_back(PacketC::SetItem { location: slot_loc, item: slot.as_ref().map(|i| i.kind), }); return; }; } a.position += dt * a.speed; a.position = a.position.min(1.); if a.speed != prev_speed { packet_out.push_back(PacketC::SetProgress { position: a.position, speed: a.speed, warn: a.warn, item: slot_loc, }); return; } } else { for (ri, recipe) in data.recipes() { if recipe.supports_tile(tile) { if let Recipe::Passive { input, warn, speed, .. } = recipe { if *input == item.kind { item.active = Some(Involvement { recipe: ri, position: 0., warn: *warn, speed: *speed, }); packet_out.push_back(PacketC::SetProgress { position: 0., speed: *speed, warn: *warn, item: slot_loc, }); return; } } } } } } } fn produce( this_had_item: bool, other_had_item: bool, this: &Option, this_loc: ItemLocation, other: &Option, other_loc: ItemLocation, score_changed: &mut bool, packet_out: &mut VecDeque, ) { info!("produce {this_loc} <~ {other_loc}"); *score_changed = true; if this_had_item { packet_out.push_back(PacketC::SetProgress { item: this_loc, position: 1., speed: 0., warn: false, }); packet_out.push_back(PacketC::SetItem { location: this_loc, item: None, }); } if other_had_item { packet_out.push_back(PacketC::MoveItem { from: other_loc, to: this_loc, }); packet_out.push_back(PacketC::SetItem { location: this_loc, item: None, }); } if let Some(i) = &other { packet_out.push_back(PacketC::SetItem { location: this_loc, item: Some(i.kind), }); packet_out.push_back(PacketC::MoveItem { from: this_loc, to: other_loc, }) } if let Some(i) = &this { packet_out.push_back(PacketC::SetItem { location: this_loc, item: Some(i.kind), }); } }