#include "behaviour_system.h" #include #include #include #include #include "game.h" #include "character_system.h" #include "direction.h" #include "transform_system.h" bool pathfindingOptionsContains(PathfindingOptions options, PathfindingOptions value) { return (static_cast(options) & static_cast(value)) != 0; } void BehaviourSystem::tick(double dt) { if (game_.isGameplayPaused()) return; timer_.accumulate(dt); while (timer_.step()) { for (int spriteId : game_.getSprites()) { Sprite& sprite = game_.getSprite(spriteId); if (!sprite.paused) { if (sprite.behaviourType == BehaviourType::Wander) { // 75% chance of changing what's happening if (std::bernoulli_distribution(0.75)(game_.getRng())) { // 50% chance of choosing a direction or stopping if (std::bernoulli_distribution(0.5)(game_.getRng())) { Direction dir; switch (std::uniform_int_distribution(0,3)(game_.getRng())) { case 0: dir = Direction::left; break; case 1: dir = Direction::up; break; case 2: dir = Direction::right; break; default: dir = Direction::down; break; } game_.getSystem().moveInDirection(spriteId, dir); } else { game_.getSystem().stopDirecting(spriteId); } } } else if (sprite.behaviourType == BehaviourType::Follow) { Sprite& target = game_.getSprite(sprite.followSpriteId); game_.getSystem().moveInDirection(spriteId, directionFacingPoint(target.loc - sprite.loc)); } } } } overcorrectionTimer_.accumulate(dt); while (overcorrectionTimer_.step()) { for (int spriteId : game_.getSprites()) { Sprite& sprite = game_.getSprite(spriteId); if (!sprite.paused && sprite.behaviourType == BehaviourType::Path && !sprite.path.empty() && sprite.loc != sprite.path.front().endpoint) { if (directionFacingPoint(sprite.path.front().endpoint - sprite.loc) != sprite.path.front().dir) { game_.getSystem().moveSprite(spriteId, sprite.path.front().endpoint); } } } } for (int spriteId : game_.getSprites()) { Sprite& sprite = game_.getSprite(spriteId); if (!sprite.paused && sprite.behaviourType == BehaviourType::Path) { while (!sprite.path.empty() && sprite.path.front().endpoint == sprite.loc) { sprite.path.pop_front(); } if (sprite.path.empty()) { game_.getSystem().stopDirecting(spriteId); } else { if (sprite.characterState == CharacterState::Still || sprite.movementDir != sprite.path.front().dir) { game_.getSystem().moveInDirection(spriteId, sprite.path.front().dir); } } } } } void BehaviourSystem::directSpriteToLocation(int spriteId, vec2i pos, PathfindingOptions options) { Sprite& sprite = game_.getSprite(spriteId); sprite.orientable = true; sprite.behaviourType = BehaviourType::Path; sprite.pathfindingDestination = pos; sprite.cardinalDirectionsOnly = pathfindingOptionsContains(options, PathfindingOptions::CardinalDirectionsOnly); createPath(spriteId); } bool BehaviourSystem::isFollowingPath(int spriteId) { Sprite& sprite = game_.getSprite(spriteId); return sprite.behaviourType == BehaviourType::Path && !sprite.path.empty(); } struct PathNodeInfo { int cheapestPathCost = INT_MAX; int estimatedRemainingCost = INT_MAX; vec2i previousPoint; Direction dirFromPreviousPoint; }; struct SearchNode { vec2i point; int estimatedCost = INT_MAX; int tiebreaker; // this actually counts downward. wow SearchNode(vec2i point, int estimatedCost) : point(point), estimatedCost(estimatedCost) { static int tiebreakerCounter = 0; tiebreaker = tiebreakerCounter--; } bool operator>(const SearchNode& rhs) const { return std::tie(estimatedCost, tiebreaker) > std::tie(rhs.estimatedCost, rhs.tiebreaker); } }; int estimateRemainingCost(vec2i current, vec2i dest, int movementSpeed, bool cardinalDirectionsOnly) { vec2i difference = dest - current; if (cardinalDirectionsOnly) { return (std::abs(difference.x()) + std::abs(difference.y())) / movementSpeed; } else { return std::max(std::abs(difference.x()), std::abs(difference.y())) / movementSpeed; } } void BehaviourSystem::createPath(int spriteId) { Sprite& sprite = game_.getSprite(spriteId); sprite.path.clear(); const Map& map = game_.getMap(); vec2i mapBounds = map.getMapSize() * map.getTileSize(); // If it is not possible to reach the destination because of the parity (if // the movement speed is above 1), then adjust the destination. if (sprite.movementSpeed > 1) { if ((sprite.loc
name: "X Plus"
panels {
  name: "ROSE"
  path: "Panels/panel_16"
  clue: "rose"
  answer: "thorn"
  symbols: BOXES
}
paintings {
  name: "UNLESS"
  path: "Components/Paintings/unless"
  gravity: X_PLUS
  orientation: "south"
  display_name: "M Side Left Painting"
}
paintings {
  name: "UNDONE"
  path: "Components/Paintings/undone"
  gravity: X_PLUS
  orientation: "south"
  display_name: "M Side Right Painting"
}
keyholders {
  name: "M"
  path: "Components/KeyHolders/keyHolderM"
}