mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-23 14:54:30 +01:00
Merge pull request #16740 from IntelOrca/precise-patrol-areas
Allow patrol areas to be single tiles.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "EntityRegistry.h"
|
||||
|
||||
#include "../Game.h"
|
||||
#include "../core/Algorithm.hpp"
|
||||
#include "../core/ChecksumStream.h"
|
||||
#include "../core/Crypt.h"
|
||||
#include "../core/DataSerialiser.h"
|
||||
@@ -291,8 +292,8 @@ static void AddToFreeList(EntityId index)
|
||||
static void RemoveFromEntityList(EntityBase* entity)
|
||||
{
|
||||
auto& list = gEntityLists[EnumValue(entity->Type)];
|
||||
auto ptr = std::lower_bound(std::begin(list), std::end(list), entity->sprite_index);
|
||||
if (ptr != std::end(list) && *ptr == entity->sprite_index)
|
||||
auto ptr = binary_find(std::begin(list), std::end(list), entity->sprite_index);
|
||||
if (ptr != std::end(list))
|
||||
{
|
||||
list.erase(ptr);
|
||||
}
|
||||
@@ -364,8 +365,8 @@ EntityBase* CreateEntity(EntityType type)
|
||||
|
||||
EntityBase* CreateEntityAt(const EntityId index, const EntityType type)
|
||||
{
|
||||
auto id = std::lower_bound(std::rbegin(_freeIdList), std::rend(_freeIdList), index);
|
||||
if (id == std::rend(_freeIdList) || *id != index)
|
||||
auto id = binary_find(std::rbegin(_freeIdList), std::rend(_freeIdList), index);
|
||||
if (id == std::rend(_freeIdList))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -421,8 +422,8 @@ static void EntitySpatialRemove(EntityBase* entity)
|
||||
{
|
||||
size_t currentIndex = GetSpatialIndexOffset({ entity->x, entity->y });
|
||||
auto& spatialVector = gEntitySpatialIndex[currentIndex];
|
||||
auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), entity->sprite_index);
|
||||
if (index != std::end(spatialVector) && *index == entity->sprite_index)
|
||||
auto index = binary_find(std::begin(spatialVector), std::end(spatialVector), entity->sprite_index);
|
||||
if (index != std::end(spatialVector))
|
||||
{
|
||||
spatialVector.erase(index, index + 1);
|
||||
}
|
||||
|
||||
178
src/openrct2/entity/PatrolArea.cpp
Normal file
178
src/openrct2/entity/PatrolArea.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "PatrolArea.h"
|
||||
|
||||
#include "../core/Algorithm.hpp"
|
||||
#include "EntityList.h"
|
||||
#include "Staff.h"
|
||||
|
||||
static PatrolArea _consolidatedPatrolArea[EnumValue(StaffType::Count)];
|
||||
static std::variant<StaffType, EntityId> _patrolAreaToRender;
|
||||
|
||||
static bool CompareTileCoordsXY(const TileCoordsXY& lhs, const TileCoordsXY& rhs)
|
||||
{
|
||||
if (lhs.y == rhs.y)
|
||||
return lhs.x < rhs.x;
|
||||
return lhs.y < rhs.y;
|
||||
}
|
||||
|
||||
const PatrolArea::Cell* PatrolArea::GetCell(const TileCoordsXY& pos) const
|
||||
{
|
||||
return const_cast<PatrolArea*>(this)->GetCell(pos);
|
||||
}
|
||||
|
||||
PatrolArea::Cell* PatrolArea::GetCell(const TileCoordsXY& pos)
|
||||
{
|
||||
auto areaPos = TileCoordsXY(pos.x / Cell::Width, pos.y / Cell::Height);
|
||||
if (areaPos.x < 0 || areaPos.x >= CellColumns || areaPos.y < 0 || areaPos.y >= CellRows)
|
||||
return nullptr;
|
||||
|
||||
auto& area = Areas[(areaPos.y * CellColumns) + areaPos.x];
|
||||
return &area;
|
||||
}
|
||||
|
||||
bool PatrolArea::IsEmpty() const
|
||||
{
|
||||
return TileCount == 0;
|
||||
}
|
||||
|
||||
void PatrolArea::Clear()
|
||||
{
|
||||
for (auto& area : Areas)
|
||||
{
|
||||
area.SortedTiles.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool PatrolArea::Get(const TileCoordsXY& pos) const
|
||||
{
|
||||
auto* area = GetCell(pos);
|
||||
if (area == nullptr)
|
||||
return false;
|
||||
|
||||
auto it = binary_find(area->SortedTiles.begin(), area->SortedTiles.end(), pos, CompareTileCoordsXY);
|
||||
auto found = it != area->SortedTiles.end();
|
||||
return found;
|
||||
}
|
||||
|
||||
bool PatrolArea::Get(const CoordsXY& pos) const
|
||||
{
|
||||
return Get(TileCoordsXY(pos));
|
||||
}
|
||||
|
||||
void PatrolArea::Set(const TileCoordsXY& pos, bool value)
|
||||
{
|
||||
auto* area = GetCell(pos);
|
||||
if (area == nullptr)
|
||||
return;
|
||||
|
||||
auto it = std::lower_bound(area->SortedTiles.begin(), area->SortedTiles.end(), pos, CompareTileCoordsXY);
|
||||
auto found = it != area->SortedTiles.end() && *it == pos;
|
||||
|
||||
if (!found && value)
|
||||
{
|
||||
area->SortedTiles.insert(it, pos);
|
||||
TileCount++;
|
||||
}
|
||||
else if (found && !value)
|
||||
{
|
||||
area->SortedTiles.erase(it);
|
||||
assert(TileCount != 0);
|
||||
TileCount--;
|
||||
}
|
||||
}
|
||||
|
||||
void PatrolArea::Set(const CoordsXY& pos, bool value)
|
||||
{
|
||||
Set(TileCoordsXY(pos), value);
|
||||
}
|
||||
|
||||
void PatrolArea::Union(const PatrolArea& other)
|
||||
{
|
||||
for (size_t i = 0; i < Areas.size(); i++)
|
||||
{
|
||||
for (const auto& pos : other.Areas[i].SortedTiles)
|
||||
{
|
||||
Set(pos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PatrolArea::Union(const std::vector<TileCoordsXY>& other)
|
||||
{
|
||||
for (const auto& pos : other)
|
||||
{
|
||||
Set(pos, true);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TileCoordsXY> PatrolArea::ToVector() const
|
||||
{
|
||||
std::vector<TileCoordsXY> result;
|
||||
for (const auto& area : Areas)
|
||||
{
|
||||
for (const auto& pos : area.SortedTiles)
|
||||
{
|
||||
result.push_back(pos);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const PatrolArea& GetMergedPatrolArea(const StaffType type)
|
||||
{
|
||||
return _consolidatedPatrolArea[EnumValue(type)];
|
||||
}
|
||||
|
||||
void UpdateConsolidatedPatrolAreas()
|
||||
{
|
||||
for (int32_t staffType = 0; staffType < EnumValue(StaffType::Count); ++staffType)
|
||||
{
|
||||
// Reset all of the merged data for the type.
|
||||
auto& mergedArea = _consolidatedPatrolArea[staffType];
|
||||
mergedArea.Clear();
|
||||
|
||||
for (auto staff : EntityList<Staff>())
|
||||
{
|
||||
if (EnumValue(staff->AssignedStaffType) != staffType)
|
||||
continue;
|
||||
|
||||
if (staff->PatrolInfo == nullptr)
|
||||
continue;
|
||||
|
||||
mergedArea.Union(*staff->PatrolInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPatrolAreaSetForStaffType(StaffType type, const CoordsXY& coords)
|
||||
{
|
||||
return _consolidatedPatrolArea[EnumValue(type)].Get(coords);
|
||||
}
|
||||
|
||||
std::variant<StaffType, EntityId> GetPatrolAreaToRender()
|
||||
{
|
||||
return _patrolAreaToRender;
|
||||
}
|
||||
|
||||
void ClearPatrolAreaToRender()
|
||||
{
|
||||
SetPatrolAreaToRender(EntityId::GetNull());
|
||||
}
|
||||
|
||||
void SetPatrolAreaToRender(EntityId staffId)
|
||||
{
|
||||
_patrolAreaToRender = staffId;
|
||||
}
|
||||
|
||||
void SetPatrolAreaToRender(StaffType staffType)
|
||||
{
|
||||
_patrolAreaToRender = staffType;
|
||||
}
|
||||
61
src/openrct2/entity/PatrolArea.h
Normal file
61
src/openrct2/entity/PatrolArea.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../world/Map.h"
|
||||
#include "Peep.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
// The number of elements in the gStaffPatrolAreas array per staff member. Every bit in the array represents a 4x4 square.
|
||||
// Right now, it's a 32-bit array like in RCT2. 32 * 128 = 4096 bits, which is also the number of 4x4 squares on a 256x256 map.
|
||||
constexpr size_t STAFF_PATROL_AREA_BLOCKS_PER_LINE = MAXIMUM_MAP_SIZE_TECHNICAL / 4;
|
||||
constexpr size_t STAFF_PATROL_AREA_SIZE = (STAFF_PATROL_AREA_BLOCKS_PER_LINE * STAFF_PATROL_AREA_BLOCKS_PER_LINE) / 32;
|
||||
|
||||
class PatrolArea
|
||||
{
|
||||
private:
|
||||
struct Cell
|
||||
{
|
||||
static constexpr auto Width = 64;
|
||||
static constexpr auto Height = 64;
|
||||
static constexpr auto NumTiles = Width * Height;
|
||||
|
||||
std::vector<TileCoordsXY> SortedTiles;
|
||||
};
|
||||
|
||||
static constexpr auto CellColumns = (MAXIMUM_MAP_SIZE_TECHNICAL + (Cell::Width - 1)) / Cell::Width;
|
||||
static constexpr auto CellRows = (MAXIMUM_MAP_SIZE_TECHNICAL + (Cell::Height - 1)) / Cell::Height;
|
||||
static constexpr auto NumCells = CellColumns * CellRows;
|
||||
|
||||
std::array<Cell, NumCells> Areas;
|
||||
size_t TileCount{};
|
||||
|
||||
const Cell* GetCell(const TileCoordsXY& pos) const;
|
||||
Cell* GetCell(const TileCoordsXY& pos);
|
||||
|
||||
public:
|
||||
bool IsEmpty() const;
|
||||
void Clear();
|
||||
bool Get(const TileCoordsXY& pos) const;
|
||||
bool Get(const CoordsXY& pos) const;
|
||||
void Set(const TileCoordsXY& pos, bool value);
|
||||
void Set(const CoordsXY& pos, bool value);
|
||||
void Union(const PatrolArea& other);
|
||||
void Union(const std::vector<TileCoordsXY>& other);
|
||||
std::vector<TileCoordsXY> ToVector() const;
|
||||
};
|
||||
|
||||
void UpdateConsolidatedPatrolAreas();
|
||||
bool IsPatrolAreaSetForStaffType(StaffType type, const CoordsXY& coords);
|
||||
std::variant<StaffType, EntityId> GetPatrolAreaToRender();
|
||||
void ClearPatrolAreaToRender();
|
||||
void SetPatrolAreaToRender(EntityId staffId);
|
||||
void SetPatrolAreaToRender(StaffType staffType);
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/SmallScenery.h"
|
||||
#include "../world/Surface.h"
|
||||
#include "PatrolArea.h"
|
||||
#include "Staff.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -667,7 +668,7 @@ void peep_sprite_remove(Peep* peep)
|
||||
else
|
||||
{
|
||||
staff->ClearPatrolArea();
|
||||
staff_update_greyed_patrol_areas();
|
||||
UpdateConsolidatedPatrolAreas();
|
||||
|
||||
News::DisableNewsItems(News::ItemType::Peep, staff->sprite_index.ToUnderlying());
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/SmallScenery.h"
|
||||
#include "../world/Surface.h"
|
||||
#include "PatrolArea.h"
|
||||
#include "Peep.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -61,18 +62,10 @@ const rct_string_id StaffCostumeNames[] = {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
uint16_t gStaffDrawPatrolAreas;
|
||||
colour_t gStaffHandymanColour;
|
||||
colour_t gStaffMechanicColour;
|
||||
colour_t gStaffSecurityColour;
|
||||
|
||||
static PatrolArea _mergedPatrolAreas[EnumValue(StaffType::Count)];
|
||||
|
||||
const PatrolArea& GetMergedPatrolArea(const StaffType type)
|
||||
{
|
||||
return _mergedPatrolAreas[EnumValue(type)];
|
||||
}
|
||||
|
||||
// Maximum manhattan distance that litter can be for a handyman to seek to it
|
||||
const uint16_t MAX_LITTER_DISTANCE = 3 * COORDS_XY_STEP;
|
||||
|
||||
@@ -81,47 +74,6 @@ template<> bool EntityBase::Is<Staff>() const
|
||||
return Type == EntityType::Staff;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BD3A4
|
||||
*/
|
||||
void staff_reset_modes()
|
||||
{
|
||||
staff_update_greyed_patrol_areas();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006C0C3F
|
||||
*/
|
||||
void staff_update_greyed_patrol_areas()
|
||||
{
|
||||
for (int32_t staffType = 0; staffType < EnumValue(StaffType::Count); ++staffType)
|
||||
{
|
||||
// Reset all of the merged data for the type.
|
||||
auto& mergedData = _mergedPatrolAreas[staffType].Data;
|
||||
std::fill(std::begin(mergedData), std::end(mergedData), 0);
|
||||
|
||||
for (auto staff : EntityList<Staff>())
|
||||
{
|
||||
if (EnumValue(staff->AssignedStaffType) != staffType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!staff->HasPatrolArea())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto staffData = staff->PatrolInfo->Data;
|
||||
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
|
||||
{
|
||||
mergedData[i] |= staffData[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006C0905
|
||||
@@ -321,33 +273,15 @@ void Staff::ResetStats()
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int32_t, int32_t> getPatrolAreaOffsetIndex(const CoordsXY& coords)
|
||||
{
|
||||
auto tilePos = TileCoordsXY(coords);
|
||||
auto x = tilePos.x / 4;
|
||||
auto y = tilePos.y / 4;
|
||||
auto bitIndex = (y * STAFF_PATROL_AREA_BLOCKS_PER_LINE) + x;
|
||||
auto byteIndex = int32_t(bitIndex / 32);
|
||||
auto byteBitIndex = int32_t(bitIndex % 32);
|
||||
return { byteIndex, byteBitIndex };
|
||||
}
|
||||
|
||||
bool Staff::IsPatrolAreaSet(const CoordsXY& coords) const
|
||||
{
|
||||
if (PatrolInfo != nullptr)
|
||||
{
|
||||
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
|
||||
return PatrolInfo->Data[offset] & (1UL << bitIndex);
|
||||
return PatrolInfo->Get(coords);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool staff_is_patrol_area_set_for_type(StaffType type, const CoordsXY& coords)
|
||||
{
|
||||
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
|
||||
return _mergedPatrolAreas[EnumValue(type)].Data[offset] & (1UL << bitIndex);
|
||||
}
|
||||
|
||||
void Staff::SetPatrolArea(const CoordsXY& coords, bool value)
|
||||
{
|
||||
if (PatrolInfo == nullptr)
|
||||
@@ -361,15 +295,18 @@ void Staff::SetPatrolArea(const CoordsXY& coords, bool value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
|
||||
auto* addr = &PatrolInfo->Data[offset];
|
||||
if (value)
|
||||
|
||||
PatrolInfo->Set(coords, value);
|
||||
}
|
||||
|
||||
void Staff::SetPatrolArea(const MapRange& range, bool value)
|
||||
{
|
||||
for (int32_t yy = range.GetTop(); yy <= range.GetBottom(); yy += COORDS_XY_STEP)
|
||||
{
|
||||
*addr |= (1 << bitIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
*addr &= ~(1 << bitIndex);
|
||||
for (int32_t xx = range.GetLeft(); xx <= range.GetRight(); xx += COORDS_XY_STEP)
|
||||
{
|
||||
SetPatrolArea({ xx, yy }, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,11 +318,7 @@ void Staff::ClearPatrolArea()
|
||||
|
||||
bool Staff::HasPatrolArea() const
|
||||
{
|
||||
if (PatrolInfo == nullptr)
|
||||
return false;
|
||||
|
||||
constexpr auto hasData = [](const auto& datapoint) { return datapoint != 0; };
|
||||
return std::any_of(std::begin(PatrolInfo->Data), std::end(PatrolInfo->Data), hasData);
|
||||
return PatrolInfo == nullptr ? false : !PatrolInfo->IsEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,16 +14,7 @@
|
||||
#include "Peep.h"
|
||||
|
||||
class DataSerialiser;
|
||||
|
||||
// The number of elements in the gStaffPatrolAreas array per staff member. Every bit in the array represents a 4x4 square.
|
||||
// Right now, it's a 32-bit array like in RCT2. 32 * 128 = 4096 bits, which is also the number of 4x4 squares on a 256x256 map.
|
||||
constexpr size_t STAFF_PATROL_AREA_BLOCKS_PER_LINE = MAXIMUM_MAP_SIZE_TECHNICAL / 4;
|
||||
constexpr size_t STAFF_PATROL_AREA_SIZE = (STAFF_PATROL_AREA_BLOCKS_PER_LINE * STAFF_PATROL_AREA_BLOCKS_PER_LINE) / 32;
|
||||
|
||||
struct PatrolArea
|
||||
{
|
||||
uint32_t Data[STAFF_PATROL_AREA_SIZE];
|
||||
};
|
||||
class PatrolArea;
|
||||
|
||||
struct Staff : Peep
|
||||
{
|
||||
@@ -72,7 +63,10 @@ public:
|
||||
|
||||
void ClearPatrolArea();
|
||||
void SetPatrolArea(const CoordsXY& coords, bool value);
|
||||
void SetPatrolArea(const MapRange& range, bool value);
|
||||
bool HasPatrolArea() const;
|
||||
std::vector<TileCoordsXY> GetPatrolArea();
|
||||
void SetPatrolArea(const std::vector<TileCoordsXY>& area);
|
||||
|
||||
private:
|
||||
void UpdatePatrolling();
|
||||
@@ -150,14 +144,10 @@ enum class EntertainerCostume : uint8_t
|
||||
|
||||
extern const rct_string_id StaffCostumeNames[static_cast<uint8_t>(EntertainerCostume::Count)];
|
||||
|
||||
extern uint16_t gStaffDrawPatrolAreas;
|
||||
extern colour_t gStaffHandymanColour;
|
||||
extern colour_t gStaffMechanicColour;
|
||||
extern colour_t gStaffSecurityColour;
|
||||
|
||||
void staff_reset_modes();
|
||||
void staff_update_greyed_patrol_areas();
|
||||
bool staff_is_patrol_area_set_for_type(StaffType type, const CoordsXY& coords);
|
||||
colour_t staff_get_colour(StaffType staffType);
|
||||
bool staff_set_colour(StaffType staffType, colour_t value);
|
||||
uint32_t staff_get_available_entertainer_costumes();
|
||||
|
||||
Reference in New Issue
Block a user