wip 1
This commit is contained in:
+95
-75
@@ -2,10 +2,11 @@ use crate::action::{apply_push, apply_shift, apply_teleport, Action, BoardAction
|
||||
use crate::board::Board;
|
||||
use crate::log::LogLine;
|
||||
use crate::script::ScriptHost;
|
||||
use crate::utils::{Direction, ObjectId};
|
||||
use crate::utils::{Direction, ObjectId, Point, Pushable};
|
||||
use crate::world::World;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::collections::{BTreeSet, HashSet, VecDeque};
|
||||
use std::fmt::format;
|
||||
use std::hash::Hash;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -295,7 +296,8 @@ impl GameState {
|
||||
apply_teleport(&mut self.board_mut(), target, x, y).unwrap_or_else(|e| log_sink.error(e))
|
||||
}
|
||||
Action::Push { x, y, dir } => {
|
||||
apply_push(&mut self.board_mut(), x, y, dir).unwrap_or_else(|e| log_sink.error(e))
|
||||
self.resolve_move((x as usize, y as usize), dir);
|
||||
//apply_push(&mut self.board_mut(), x, y, dir).unwrap_or_else(|e| log_sink.error(e))
|
||||
}
|
||||
Action::Shift(cells) => {
|
||||
apply_shift(&mut self.board_mut(), &cells).unwrap_or_else(|e| log_sink.error(e))
|
||||
@@ -385,6 +387,96 @@ impl GameState {
|
||||
self.run_init();
|
||||
}
|
||||
|
||||
fn resolve_move(&mut self, from: (usize, usize), dir: Direction) -> bool {
|
||||
// Get the target coords, if they're out of bounds then the move fails.
|
||||
let target: Point = dir.from_point(from.0 as i64, from.1 as i64).into();
|
||||
if !self.board().in_bounds((target.x, target.y)) { return false; }
|
||||
|
||||
if self.board().is_empty(target) {
|
||||
// If it's empty, we can move in there; do so and return true:
|
||||
self.board_mut().move_cell(from.into(), target);
|
||||
} else if self.board().is_player(target) {
|
||||
// Target cell contains player, who's pushable, so we'll recurse and see what happens:
|
||||
if self.resolve_move((target.x as usize, target.y as usize), dir) {
|
||||
self.board_mut().move_cell(from.into(), target);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, what enter response does the thing there have? We need to be able to call
|
||||
// hooks on objects, so we can't hold a mut reference to the board going into this match
|
||||
let id = {
|
||||
if let Some(Tile::Object(b)) = self.board_mut().get(target.x as usize, target.y as usize) && let def = b.as_ref() {
|
||||
def.scripting.id
|
||||
} else {
|
||||
unreachable!("moving into the player was handled above")
|
||||
}
|
||||
};
|
||||
|
||||
// Whether the player is what initiated the move directly. Matters for grab / swap
|
||||
// let from_player = matches!(self.board().get(from.0, from.1), Some(Tile::Player));
|
||||
|
||||
// Call the target's bump hook and resolve whatever it did
|
||||
let actions = self.scripts.run_bump(id, dir.opposite());
|
||||
self.apply_actions(actions);
|
||||
|
||||
// First do things to try and call any hooks relevant:
|
||||
// let actions = match resp {
|
||||
// // We block all moves, return false
|
||||
// EnterResponse::Block => { self.scripts.run_bump(id, dir.opposite()) }
|
||||
//
|
||||
// // The player can grab things directly, so let's do that
|
||||
// EnterResponse::Grab if from_player => {
|
||||
// let a = self.scripts.run_grab(id);
|
||||
// self.board_mut().get_mut(target.0, target.1).take();
|
||||
// a
|
||||
// }
|
||||
//
|
||||
// // Grabbables not from the player act as pushable, we need to recurse
|
||||
// EnterResponse::Grab => { self.resolve_move(target, dir); vec![] }
|
||||
//
|
||||
// // Pushable if we're allowed to push that way recurses
|
||||
// EnterResponse::Push(p) if p.allows(dir) => { self.resolve_move(target, dir); vec![] }
|
||||
//
|
||||
// // Pushable in a disallowed direction blocks
|
||||
// EnterResponse::Push(p) => { self.scripts.run_bump(id, dir.opposite()) }
|
||||
//
|
||||
// // Swaps let the player swap places, but we should return false afterward because we
|
||||
// // haven't left the source cell empty. In practice this won't matter because right
|
||||
// // now we never recurse _into_ a player-source-cell, all moves are initiated by the
|
||||
// // player, but still.
|
||||
// EnterResponse::Swap if from_player => {
|
||||
// let mut board = self.board_mut();
|
||||
// let mover = board.get_mut(from.0, from.1).take();
|
||||
// let swapper = board.get_mut(target.0, target.1).take();
|
||||
// board.get_mut(target.0, target.1).replace(mover.unwrap());
|
||||
// board.get_mut(from.0, from.1).replace(swapper.unwrap());
|
||||
// vec![]
|
||||
// }
|
||||
//
|
||||
// // Swaps in a chain are just normal pushable, recurse:
|
||||
// EnterResponse::Swap => { self.resolve_move(target, dir); vec![] }
|
||||
//
|
||||
// // Squish just lets the mover overwrite, and always leaves the source cell empty:
|
||||
// EnterResponse::Squish => { self.board_mut().get_mut(from.0, from.1).take(); vec![] }
|
||||
//
|
||||
// // Finally, hook, we need to call a hook and let it do its thing:
|
||||
// EnterResponse::Hook => { self.scripts.run_bump(id, dir.opposite()) }
|
||||
// };
|
||||
|
||||
// If there were actions performed by the hooks, do them.
|
||||
// self.apply_actions(actions);
|
||||
|
||||
// Now, check again if the target cell is empty. If it is, put the mover there. Also
|
||||
// check that the source cell still contains something! It may have been teleported away.
|
||||
if !self.board().is_empty(from.into()) && self.board().is_empty(target) {
|
||||
self.board_mut().move_cell(from.into(), target);
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, return whether the source cell is now empty, so calls up the chain
|
||||
// can move into it (push chains)
|
||||
self.board().is_empty(from.into())
|
||||
}
|
||||
|
||||
/// Attempts to move the player one cell in `dir`.
|
||||
///
|
||||
/// The move is ignored if the target cell is out of bounds, or it is neither
|
||||
@@ -399,80 +491,8 @@ impl GameState {
|
||||
if !self.board().in_bounds(target) {
|
||||
return;
|
||||
}
|
||||
let (nx, ny) = (target.0 as usize, target.1 as usize);
|
||||
|
||||
// We need to be able to call hooks on objects, so we can't hold a mut reference to the
|
||||
// board going into this match
|
||||
let obj_data = {
|
||||
if let Some(Tile::Object(b)) = self.board_mut().get(nx, ny) && let def = b.as_ref() {
|
||||
Some((def.scripting.id, def.enter_response))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some((id, enter_response)) = obj_data {
|
||||
let actions = match enter_response {
|
||||
EnterResponse::Block => {
|
||||
// Call the bump hook
|
||||
self.scripts.run_bump(id, dir.opposite())
|
||||
}
|
||||
EnterResponse::Grab => {
|
||||
// Call the hook to get the actions and then stamp the player on top
|
||||
let a = self.scripts.run_grab(id);
|
||||
{
|
||||
let mut board = self.board_mut();
|
||||
board.get_mut(player_loc.0, player_loc.1).take();
|
||||
board.get_mut(nx, ny).replace(Tile::Player);
|
||||
}
|
||||
a
|
||||
}
|
||||
EnterResponse::Push(pushable) => {
|
||||
// First, can we push?
|
||||
if self.board().can_push(nx, ny, dir) {
|
||||
// The player is pushable, so, this amounts to the same thing and saves a
|
||||
// couple replace()s
|
||||
self.board_mut().push(player_loc.0, player_loc.1, dir);
|
||||
vec![] // There's no push hook, just pushing doesn't call scripts
|
||||
} else {
|
||||
// This is actually not pushable in this way, so we're gonna bump instead:
|
||||
self.scripts.run_bump(id, dir.opposite())
|
||||
}
|
||||
}
|
||||
EnterResponse::Hook => {
|
||||
vec![] // TODO this hook needs to exist and work. It's documented in tile.rb. Has implications for push as well
|
||||
// plan: get rid of can_push in board. Make a board::pushes_into_hook or something, find the hook-enter
|
||||
// object at the end of this chain. Trying to push calls that, if there's no hook object then it pushes, if
|
||||
// that returns false then it bumps. If there is a hook object, call the hook, run the actions. If it _leaves
|
||||
// the cell empty,_ then call push. Otherwise bump.
|
||||
// Maybe have a pushresult enum or something that board::push returns, "hook(id, bumpid)" or "bump(id)" or "moved".
|
||||
// If the situation is: `@b++h` then moving to the east, the bumpid would be b (the thing you actually touched),
|
||||
// hook id would be h (the thing with the hook enterresponse). Board can identify object chains but not actually
|
||||
// call hooks.
|
||||
}
|
||||
EnterResponse::Swap => {
|
||||
let mut board = self.board_mut();
|
||||
// Swap the two
|
||||
let tgt = board.get_mut(nx, ny).replace(Tile::Player);
|
||||
*board.get_mut(player_loc.0, player_loc.1) = tgt;
|
||||
vec![]
|
||||
}
|
||||
EnterResponse::Squish => {
|
||||
let mut board = self.board_mut();
|
||||
// Stamp over it, squishing it
|
||||
board.get_mut(player_loc.0, player_loc.1).take();
|
||||
board.get_mut(nx, ny).replace(Tile::Player);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
self.apply_actions(actions);
|
||||
} else {
|
||||
// There's not an object there, we can just move the player
|
||||
let mut board = self.board_mut();
|
||||
board.get_mut(player_loc.0, player_loc.1).take();
|
||||
board.get_mut(nx, ny).replace(Tile::Player);
|
||||
}
|
||||
self.resolve_move(player_loc, dir);
|
||||
|
||||
// Check if we actually moved
|
||||
let new_loc = self.board().player_pos();
|
||||
|
||||
Reference in New Issue
Block a user