Staff patrol area 2 from NSF (#15600)

* Port over changes from NSF for staff

* Fix import and export

* Fix crash on export

* Fix import issue

* Fix patrol import issue

* Remove staff id field

* Increment network version

* Actually remove all instances of staff id

* Update replays
This commit is contained in:
Duncan 2021-10-27 21:19:10 +01:00 committed by GitHub
parent 1cfa4f8a9e
commit a8d2d5fd44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 273 additions and 231 deletions

View File

@ -65,9 +65,9 @@ set(OBJECTS_VERSION "1.0.21")
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip")
set(OBJECTS_SHA1 "c38af45d51a6e440386180feacf76c64720b6ac5")
set(REPLAYS_VERSION "0.0.56")
set(REPLAYS_VERSION "0.0.57")
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip")
set(REPLAYS_SHA1 "C47048B71A95A95428A08035C94AD10E7A020D4D")
set(REPLAYS_SHA1 "DF9C3B48755B19FDD4D0EC721007B98CD5B6F420")
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
option(WITH_TESTS "Build tests")

View File

@ -48,8 +48,8 @@
<TitleSequencesSha1>304d13a126c15bf2c86ff13b81a2f2cc1856ac8d</TitleSequencesSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0.21/objects.zip</ObjectsUrl>
<ObjectsSha1>c38af45d51a6e440386180feacf76c64720b6ac5</ObjectsSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.56/replays.zip</ReplaysUrl>
<ReplaysSha1>C47048B71A95A95428A08035C94AD10E7A020D4D</ReplaysSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.57/replays.zip</ReplaysUrl>
<ReplaysSha1>DF9C3B48755B19FDD4D0EC721007B98CD5B6F420</ReplaysSha1>
</PropertyGroup>
<ItemGroup>

View File

@ -25,6 +25,7 @@
#include <openrct2/actions/SmallSceneryRemoveAction.h>
#include <openrct2/actions/WallRemoveAction.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/peep/Staff.h>
#include <openrct2/ride/Ride.h>
#include <openrct2/ride/RideData.h>
#include <openrct2/ride/Track.h>

View File

@ -25,6 +25,7 @@
#include <openrct2/actions/SurfaceSetStyleAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/peep/Staff.h>
#include <openrct2/ride/RideData.h>
#include <openrct2/ride/Track.h>
#include <openrct2/ride/TrainManager.h>

View File

@ -14,6 +14,7 @@
#include <openrct2/drawing/Drawing.h>
#include <openrct2/interface/Colour.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/peep/Staff.h>
#include <openrct2/world/Entity.h>
static constexpr const rct_string_id WINDOW_TITLE = STR_SACK_STAFF;

View File

@ -11,6 +11,7 @@
#include "core/CircularBuffer.h"
#include "peep/Peep.h"
#include "peep/Staff.h"
#include "ride/Vehicle.h"
#include "world/Balloon.h"
#include "world/Duck.h"
@ -296,7 +297,6 @@ struct GameStateSnapshots final : public IGameStateSnapshots
COMPARE_FIELD(Staff, AssignedStaffType);
COMPARE_FIELD(Staff, MechanicTimeSinceCall);
COMPARE_FIELD(Staff, HireDate);
COMPARE_FIELD(Staff, StaffId);
COMPARE_FIELD(Staff, StaffOrders);
COMPARE_FIELD(Staff, StaffMowingTimeout);
COMPARE_FIELD(Staff, StaffRidesFixed);

View File

@ -18,6 +18,7 @@
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../network/network.h"
#include "../peep/Staff.h"
#include "../ride/Ride.h"
#include "../ride/Vehicle.h"
#include "../scenario/Scenario.h"

View File

@ -10,7 +10,7 @@
#include "StaffFireAction.h"
#include "../interface/Window.h"
#include "../peep/Peep.h"
#include "../peep/Staff.h"
#include "../world/Entity.h"
StaffFireAction::StaffFireAction(uint16_t spriteId)

View File

@ -111,15 +111,8 @@ GameActions::Result::Ptr StaffHireNewAction::QueryExecute(bool execute) const
}
}
// Look for a free slot in the staff modes.
int32_t staffIndex;
for (staffIndex = 0; staffIndex < STAFF_MAX_COUNT; ++staffIndex)
{
if (gStaffModes[staffIndex] == StaffMode::None)
break;
}
if (staffIndex == STAFF_MAX_COUNT)
auto numStaff = GetEntityListCount(EntityType::Staff);
if (numStaff == STAFF_MAX_COUNT)
{
// Too many staff members exist already.
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_HIRE_NEW_STAFF, STR_TOO_MANY_STAFF_IN_GAME);
@ -222,14 +215,7 @@ GameActions::Result::Ptr StaffHireNewAction::QueryExecute(bool execute) const
newPeep->EnergyTarget = 0x60;
newPeep->StaffMowingTimeout = 0;
newPeep->StaffId = staffIndex;
gStaffModes[staffIndex] = StaffMode::Walk;
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
{
gStaffPatrolAreas[staffIndex * STAFF_PATROL_AREA_SIZE + i] = 0;
}
newPeep->PatrolInfo = nullptr;
res->SetData(StaffHireNewActionResult{ newPeep->sprite_index });
}

View File

@ -55,29 +55,6 @@ GameActions::Result::Ptr StaffSetPatrolAreaAction::Query() const
return MakeResult();
}
static void UpdateStaffMode(const Staff& staff)
{
bool isPatrolling = false;
const auto peepOffset = staff.StaffId * STAFF_PATROL_AREA_SIZE;
for (size_t i = peepOffset; i < peepOffset + STAFF_PATROL_AREA_SIZE; i++)
{
if (gStaffPatrolAreas[i] != 0)
{
isPatrolling = true;
break;
}
}
if (isPatrolling)
{
gStaffModes[staff.StaffId] = StaffMode::Patrol;
}
else if (gStaffModes[staff.StaffId] == StaffMode::Patrol)
{
gStaffModes[staff.StaffId] = StaffMode::Walk;
}
}
static void InvalidatePatrolTile(const CoordsXY& loc)
{
// Align the location to the top left of the patrol square
@ -104,12 +81,14 @@ GameActions::Result::Ptr StaffSetPatrolAreaAction::Execute() const
{
case StaffSetPatrolAreaMode::Set:
staff->SetPatrolArea(_loc, true);
UpdateStaffMode(*staff);
InvalidatePatrolTile(_loc);
break;
case StaffSetPatrolAreaMode::Unset:
staff->SetPatrolArea(_loc, false);
UpdateStaffMode(*staff);
if (!staff->HasPatrolArea())
{
staff->ClearPatrolArea();
}
InvalidatePatrolTile(_loc);
break;
case StaffSetPatrolAreaMode::ClearAll:

View File

@ -38,7 +38,7 @@
// This string specifies which version of network stream current build uses.
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
#define NETWORK_STREAM_VERSION "15"
#define NETWORK_STREAM_VERSION "16"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;

View File

@ -655,7 +655,6 @@ void peep_sprite_remove(Peep* peep)
else
{
staff->ClearPatrolArea();
gStaffModes[staff->StaffId] = StaffMode::None;
staff_update_greyed_patrol_areas();
News::DisableNewsItems(News::ItemType::Peep, staff->sprite_index);

View File

@ -838,104 +838,7 @@ private:
void GoToRideEntrance(Ride* ride);
};
struct Staff : Peep
{
static constexpr auto cEntityType = EntityType::Staff;
public:
StaffType AssignedStaffType;
uint16_t MechanicTimeSinceCall; // time getting to ride to fix
int32_t HireDate;
uint8_t StaffId;
uint8_t StaffOrders;
uint8_t StaffMowingTimeout;
union
{
uint16_t StaffLawnsMown;
uint16_t StaffRidesFixed;
};
union
{
uint16_t StaffGardensWatered;
uint16_t StaffRidesInspected;
};
union
{
uint16_t StaffLitterSwept;
uint16_t StaffVandalsStopped;
};
uint16_t StaffBinsEmptied;
void UpdateStaff(uint32_t stepsToTake);
void Tick128UpdateStaff();
bool IsMechanic() const;
bool IsPatrolAreaSet(const CoordsXY& coords) const;
bool IsLocationInPatrol(const CoordsXY& loc) const;
bool IsLocationOnPatrolEdge(const CoordsXY& loc) const;
bool DoPathFinding();
uint8_t GetCostume() const;
void SetCostume(uint8_t value);
void SetHireDate(int32_t hireDate);
int32_t GetHireDate() const;
bool CanIgnoreWideFlag(const CoordsXYZ& staffPos, TileElement* path) const;
static void ResetStats();
void Serialise(DataSerialiser& stream);
void ClearPatrolArea();
void TogglePatrolArea(const CoordsXY& coords);
void SetPatrolArea(const CoordsXY& coords, bool value);
bool HasPatrolArea() const;
private:
void UpdatePatrolling();
void UpdateMowing();
void UpdateSweeping();
void UpdateEmptyingBin();
void UpdateWatering();
void UpdateAnswering();
void UpdateFixing(int32_t steps);
bool UpdateFixingEnterStation(Ride* ride) const;
bool UpdateFixingMoveToBrokenDownVehicle(bool firstRun, const Ride* ride);
bool UpdateFixingFixVehicle(bool firstRun, const Ride* ride);
bool UpdateFixingFixVehicleMalfunction(bool firstRun, const Ride* ride);
bool UpdateFixingMoveToStationEnd(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationEnd(bool firstRun);
bool UpdateFixingMoveToStationStart(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationStart(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationBrakes(bool firstRun, Ride* ride);
bool UpdateFixingMoveToStationExit(bool firstRun, const Ride* ride);
bool UpdateFixingFinishFixOrInspect(bool firstRun, int32_t steps, Ride* ride);
bool UpdateFixingLeaveByEntranceExit(bool firstRun, const Ride* ride);
void UpdateRideInspected(ride_id_t rideIndex);
void UpdateHeadingToInspect();
bool DoHandymanPathFinding();
bool DoMechanicPathFinding();
bool DoEntertainerPathFinding();
bool DoMiscPathFinding();
Direction HandymanDirectionRandSurface(uint8_t validDirections) const;
void EntertainerUpdateNearbyPeeps() const;
uint8_t GetValidPatrolDirections(const CoordsXY& loc) const;
Direction HandymanDirectionToNearestLitter() const;
uint8_t HandymanDirectionToUncutGrass(uint8_t valid_directions) const;
Direction DirectionSurface(Direction initialDirection) const;
Direction DirectionPath(uint8_t validDirections, PathElement* pathElement) const;
Direction MechanicDirectionSurface() const;
Direction MechanicDirectionPathRand(uint8_t pathDirections) const;
Direction MechanicDirectionPath(uint8_t validDirections, PathElement* pathElement);
bool UpdatePatrollingFindWatering();
bool UpdatePatrollingFindBin();
bool UpdatePatrollingFindSweeping();
bool UpdatePatrollingFindGrass();
};
static_assert(sizeof(Guest) <= 512);
static_assert(sizeof(Staff) <= 512);
struct rct_sprite_bounds
{

View File

@ -61,15 +61,18 @@ const rct_string_id StaffCostumeNames[] = {
};
// clang-format on
// Every staff member has STAFF_PATROL_AREA_SIZE elements assigned to in this array, indexed by their StaffId
// Additionally there is a patrol area for each staff type, which is the union of the patrols of all staff members of that type
uint32_t gStaffPatrolAreas[(STAFF_MAX_COUNT + static_cast<uint8_t>(StaffType::Count)) * STAFF_PATROL_AREA_SIZE];
StaffMode gStaffModes[STAFF_MAX_COUNT + static_cast<uint8_t>(StaffType::Count)];
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;
@ -84,12 +87,6 @@ template<> bool EntityBase::Is<Staff>() const
*/
void staff_reset_modes()
{
for (int32_t i = 0; i < STAFF_MAX_COUNT; i++)
gStaffModes[i] = StaffMode::None;
for (int32_t i = STAFF_MAX_COUNT; i < (STAFF_MAX_COUNT + static_cast<uint8_t>(StaffType::Count)); i++)
gStaffModes[i] = StaffMode::Walk;
staff_update_greyed_patrol_areas();
}
@ -142,27 +139,27 @@ bool staff_hire_new_member(StaffType staffType, EntertainerCostume entertainerTy
*/
void staff_update_greyed_patrol_areas()
{
for (int32_t staff_type = 0; staff_type < static_cast<uint8_t>(StaffType::Count); ++staff_type)
for (int32_t staffType = 0; staffType < EnumValue(StaffType::Count); ++staffType)
{
const size_t staffPatrolOffset = (staff_type + STAFF_MAX_COUNT) * STAFF_PATROL_AREA_SIZE;
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
{
gStaffPatrolAreas[staffPatrolOffset + i] = 0;
}
// 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 peep : EntityList<Staff>())
for (auto staff : EntityList<Staff>())
{
if (!peep->HasPatrolArea())
if (EnumValue(staff->AssignedStaffType) != staffType)
{
continue;
}
if (static_cast<uint8_t>(peep->AssignedStaffType) == staff_type)
if (!staff->HasPatrolArea())
{
const size_t peepPatrolOffset = peep->StaffId * STAFF_PATROL_AREA_SIZE;
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
{
gStaffPatrolAreas[staffPatrolOffset + i] |= gStaffPatrolAreas[peepPatrolOffset + i];
}
continue;
}
auto staffData = staff->PatrolInfo->Data;
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
{
mergedData[i] |= staffData[i];
}
}
}
@ -369,7 +366,6 @@ void Staff::ResetStats()
static std::pair<int32_t, int32_t> getPatrolAreaOffsetIndex(const CoordsXY& coords)
{
// Patrol areas are 4 * 4 tiles (32 * 4) = 128 = 2^^7
auto tilePos = TileCoordsXY(coords);
auto x = tilePos.x / 4;
auto y = tilePos.y / 4;
@ -379,34 +375,37 @@ static std::pair<int32_t, int32_t> getPatrolAreaOffsetIndex(const CoordsXY& coor
return { byteIndex, byteBitIndex };
}
static bool staff_is_patrol_area_set(int32_t staffIndex, const CoordsXY& coords)
{
// Patrol quads are stored in a bit map (8 patrol quads per byte).
// Each patrol quad is 4x4.
// Therefore there are in total 64 x 64 patrol quads in the 256 x 256 map.
// At the end of the array (after the slots for individual staff members),
// there are slots that save the combined patrol area for every staff type.
int32_t peepOffset = staffIndex * STAFF_PATROL_AREA_SIZE;
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
return gStaffPatrolAreas[peepOffset + offset] & (1UL << bitIndex);
}
bool Staff::IsPatrolAreaSet(const CoordsXY& coords) const
{
return staff_is_patrol_area_set(StaffId, coords);
if (PatrolInfo != nullptr)
{
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
return PatrolInfo->Data[offset] & (1UL << bitIndex);
}
return false;
}
bool staff_is_patrol_area_set_for_type(StaffType type, const CoordsXY& coords)
{
return staff_is_patrol_area_set(STAFF_MAX_COUNT + static_cast<uint8_t>(type), coords);
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
return _mergedPatrolAreas[EnumValue(type)].Data[offset] & (1UL << bitIndex);
}
void Staff::SetPatrolArea(const CoordsXY& coords, bool value)
{
int32_t peepOffset = StaffId * STAFF_PATROL_AREA_SIZE;
if (PatrolInfo == nullptr)
{
if (value)
{
PatrolInfo = new PatrolArea();
}
else
{
return;
}
}
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
uint32_t* addr = &gStaffPatrolAreas[peepOffset + offset];
auto* addr = &PatrolInfo->Data[offset];
if (value)
{
*addr |= (1 << bitIndex);
@ -419,21 +418,17 @@ void Staff::SetPatrolArea(const CoordsXY& coords, bool value)
void Staff::ClearPatrolArea()
{
const auto peepOffset = StaffId * STAFF_PATROL_AREA_SIZE;
std::fill_n(&gStaffPatrolAreas[peepOffset], STAFF_PATROL_AREA_SIZE, 0);
gStaffModes[StaffId] = StaffMode::Walk;
}
void Staff::TogglePatrolArea(const CoordsXY& coords)
{
int32_t peepOffset = StaffId * STAFF_PATROL_AREA_SIZE;
auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords);
gStaffPatrolAreas[peepOffset + offset] ^= (1 << bitIndex);
delete PatrolInfo;
PatrolInfo = nullptr;
}
bool Staff::HasPatrolArea() const
{
return gStaffModes[StaffId] == StaffMode::Patrol;
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);
}
/**

View File

@ -18,13 +18,107 @@
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;
enum class StaffMode : uint8_t
struct PatrolArea
{
None,
Walk,
Patrol = 3
uint32_t Data[STAFF_PATROL_AREA_SIZE];
};
struct Staff : Peep
{
static constexpr auto cEntityType = EntityType::Staff;
public:
PatrolArea* PatrolInfo;
StaffType AssignedStaffType;
uint16_t MechanicTimeSinceCall; // time getting to ride to fix
int32_t HireDate;
uint8_t StaffOrders;
uint8_t StaffMowingTimeout;
union
{
uint16_t StaffLawnsMown;
uint16_t StaffRidesFixed;
};
union
{
uint16_t StaffGardensWatered;
uint16_t StaffRidesInspected;
};
union
{
uint16_t StaffLitterSwept;
uint16_t StaffVandalsStopped;
};
uint16_t StaffBinsEmptied;
void UpdateStaff(uint32_t stepsToTake);
void Tick128UpdateStaff();
bool IsMechanic() const;
bool IsPatrolAreaSet(const CoordsXY& coords) const;
bool IsLocationInPatrol(const CoordsXY& loc) const;
bool IsLocationOnPatrolEdge(const CoordsXY& loc) const;
bool DoPathFinding();
uint8_t GetCostume() const;
void SetCostume(uint8_t value);
void SetHireDate(int32_t hireDate);
int32_t GetHireDate() const;
bool CanIgnoreWideFlag(const CoordsXYZ& staffPos, TileElement* path) const;
static void ResetStats();
void Serialise(DataSerialiser& stream);
void ClearPatrolArea();
void SetPatrolArea(const CoordsXY& coords, bool value);
bool HasPatrolArea() const;
private:
void UpdatePatrolling();
void UpdateMowing();
void UpdateSweeping();
void UpdateEmptyingBin();
void UpdateWatering();
void UpdateAnswering();
void UpdateFixing(int32_t steps);
bool UpdateFixingEnterStation(Ride* ride) const;
bool UpdateFixingMoveToBrokenDownVehicle(bool firstRun, const Ride* ride);
bool UpdateFixingFixVehicle(bool firstRun, const Ride* ride);
bool UpdateFixingFixVehicleMalfunction(bool firstRun, const Ride* ride);
bool UpdateFixingMoveToStationEnd(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationEnd(bool firstRun);
bool UpdateFixingMoveToStationStart(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationStart(bool firstRun, const Ride* ride);
bool UpdateFixingFixStationBrakes(bool firstRun, Ride* ride);
bool UpdateFixingMoveToStationExit(bool firstRun, const Ride* ride);
bool UpdateFixingFinishFixOrInspect(bool firstRun, int32_t steps, Ride* ride);
bool UpdateFixingLeaveByEntranceExit(bool firstRun, const Ride* ride);
void UpdateRideInspected(ride_id_t rideIndex);
void UpdateHeadingToInspect();
bool DoHandymanPathFinding();
bool DoMechanicPathFinding();
bool DoEntertainerPathFinding();
bool DoMiscPathFinding();
Direction HandymanDirectionRandSurface(uint8_t validDirections) const;
void EntertainerUpdateNearbyPeeps() const;
uint8_t GetValidPatrolDirections(const CoordsXY& loc) const;
Direction HandymanDirectionToNearestLitter() const;
uint8_t HandymanDirectionToUncutGrass(uint8_t valid_directions) const;
Direction DirectionSurface(Direction initialDirection) const;
Direction DirectionPath(uint8_t validDirections, PathElement* pathElement) const;
Direction MechanicDirectionSurface() const;
Direction MechanicDirectionPathRand(uint8_t pathDirections) const;
Direction MechanicDirectionPath(uint8_t validDirections, PathElement* pathElement);
bool UpdatePatrollingFindWatering();
bool UpdatePatrollingFindBin();
bool UpdatePatrollingFindSweeping();
bool UpdatePatrollingFindGrass();
};
static_assert(sizeof(Staff) <= 512);
enum STAFF_ORDERS
{
STAFF_ORDERS_SWEEPING = (1 << 0),
@ -54,8 +148,6 @@ enum class EntertainerCostume : uint8_t
extern const rct_string_id StaffCostumeNames[static_cast<uint8_t>(EntertainerCostume::Count)];
extern uint32_t gStaffPatrolAreas[(STAFF_MAX_COUNT + static_cast<uint8_t>(StaffType::Count)) * STAFF_PATROL_AREA_SIZE];
extern StaffMode gStaffModes[STAFF_MAX_COUNT + static_cast<uint8_t>(StaffType::Count)];
extern uint16_t gStaffDrawPatrolAreas;
extern colour_t gStaffHandymanColour;
extern colour_t gStaffMechanicColour;
@ -73,3 +165,5 @@ int32_t staff_get_available_entertainer_costume_list(EntertainerCostume* costume
money32 GetStaffWage(StaffType type);
PeepSpriteType EntertainerCostumeToSprite(EntertainerCostume entertainerType);
const PatrolArea& GetMergedPatrolArea(const StaffType type);

View File

@ -1183,19 +1183,6 @@ namespace RCT1
void FixImportStaff()
{
// The RCT2/OpenRCT2 structures are bigger than in RCT1, so initialise them to zero
std::fill(std::begin(gStaffModes), std::end(gStaffModes), StaffMode::None);
std::fill(std::begin(gStaffPatrolAreas), std::end(gStaffPatrolAreas), 0);
for (int32_t i = 0; i < RCT1_MAX_STAFF; i++)
{
gStaffModes[i] = static_cast<StaffMode>(_s4.staff_modes[i]);
}
for (auto peep : EntityList<Staff>())
{
ImportStaffPatrolArea(peep);
}
// Only the individual patrol areas have been converted, so generate the combined patrol areas of each staff type
staff_update_greyed_patrol_areas();
}
@ -1258,8 +1245,11 @@ namespace RCT1
dst->PathfindGoal.direction = INVALID_DIRECTION;
}
void ImportStaffPatrolArea(Staff* staffmember)
void ImportStaffPatrolArea(Staff* staffmember, uint8_t staffId)
{
// TODO: It is likely that S4 files should have a staffmode check before setting
// patrol areas. See S6 importer.
// The patrol areas in RCT1 are encoded as follows, for coordinates x and y, separately for every staff member:
// - Chop off the 7 lowest bits of the x and y coordinates, which leaves 5 bits per coordinate.
// This step also "produces" the 4x4 patrol squares.
@ -1270,7 +1260,7 @@ namespace RCT1
// index in the array ----^ ^--- bit position in the 8-bit value
// We do the opposite in this function to recover the x and y values.
int32_t peepOffset = staffmember->StaffId * RCT12_PATROL_AREA_SIZE;
int32_t peepOffset = staffId * RCT12_PATROL_AREA_SIZE;
for (int32_t i = 0; i < RCT12_PATROL_AREA_SIZE; i++)
{
if (_s4.patrol_areas[peepOffset + i] == 0)
@ -2886,13 +2876,14 @@ namespace RCT1
dst->AssignedStaffType = StaffType(src->staff_type);
dst->MechanicTimeSinceCall = src->mechanic_time_since_call;
dst->HireDate = src->park_entry_time;
dst->StaffId = src->staff_id;
dst->StaffOrders = src->staff_orders;
dst->StaffMowingTimeout = src->staff_mowing_timeout;
dst->StaffLawnsMown = src->paid_to_enter;
dst->StaffGardensWatered = src->paid_on_rides;
dst->StaffLitterSwept = src->paid_on_food;
dst->StaffBinsEmptied = src->paid_on_souvenirs;
ImportStaffPatrolArea(dst, src->staff_id);
}
template<> void S4Importer::ImportEntity<Litter>(const RCT12SpriteBase& srcBase)

View File

@ -747,6 +747,13 @@ struct RCT2SpritePeep : RCT12SpriteBase
};
assert_struct_size(RCT2SpritePeep, 0x100);
enum class RCT2StaffMode : uint8_t
{
None,
Walk,
Patrol = 3
};
union RCT2Sprite
{
private:
@ -1025,7 +1032,7 @@ struct rct_s6_data
uint32_t next_guest_index;
uint16_t grass_and_scenery_tilepos;
uint32_t patrol_areas[(RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT) * RCT12_PATROL_AREA_SIZE];
uint8_t staff_modes[RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT];
RCT2StaffMode staff_modes[RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT];
uint8_t pad_13CA73E;
uint8_t pad_13CA73F;
uint8_t byte_13CA740;

View File

@ -523,8 +523,7 @@ void S6Exporter::Export()
ExportRideMeasurements();
_s6.next_guest_index = gNextGuestNumber;
_s6.grass_and_scenery_tilepos = gGrassSceneryTileLoopPosition;
std::memcpy(_s6.patrol_areas, gStaffPatrolAreas, sizeof(_s6.patrol_areas));
std::memcpy(_s6.staff_modes, gStaffModes, sizeof(_s6.staff_modes));
ExportStaffPatrolAreas();
// unk_13CA73E
// pad_13CA73F
// unk_13CA740
@ -1107,6 +1106,48 @@ void S6Exporter::RebuildEntitySpatialLocation(const TileCoordsXY& loc)
}
}
void S6Exporter::ExportStaffPatrolAreas()
{
std::fill(std::begin(_s6.staff_modes), std::end(_s6.staff_modes), RCT2StaffMode::None);
std::fill(std::begin(_s6.patrol_areas), std::end(_s6.patrol_areas), 0);
auto staffId = 0;
for (auto* staff : EntityList<Staff>())
{
if (staff->HasPatrolArea())
{
const size_t staffPatrolOffset = staffId * STAFF_PATROL_AREA_SIZE;
std::copy(
std::begin(staff->PatrolInfo->Data), std::end(staff->PatrolInfo->Data), &_s6.patrol_areas[staffPatrolOffset]);
_s6.staff_modes[staffId] = RCT2StaffMode::Patrol;
}
else
{
_s6.staff_modes[staffId] = RCT2StaffMode::Walk;
}
_s6.sprites[staff->sprite_index].peep.staff_id = staffId;
staffId++;
}
constexpr auto hasData = [](const auto& datapoint) { return datapoint != 0; };
for (const auto type : { StaffType::Handyman, StaffType::Mechanic, StaffType::Security, StaffType::Entertainer })
{
const size_t staffPatrolOffset = (EnumValue(type) + STAFF_MAX_COUNT) * STAFF_PATROL_AREA_SIZE;
const auto& area = GetMergedPatrolArea(type);
std::copy(std::begin(area.Data), std::end(area.Data), &_s6.patrol_areas[staffPatrolOffset]);
if (std::any_of(std::begin(area.Data), std::end(area.Data), hasData))
{
_s6.staff_modes[EnumValue(type) + STAFF_MAX_COUNT] = RCT2StaffMode::Patrol;
}
else
{
_s6.staff_modes[EnumValue(type) + STAFF_MAX_COUNT] = RCT2StaffMode::Walk;
}
}
}
void S6Exporter::RebuildEntityLinks()
{
// Rebuild next/previous linked list entity indexs
@ -1422,7 +1463,6 @@ template<> void S6Exporter::ExportEntity(RCT2SpritePeep* dst, const Staff* src)
dst->staff_type = static_cast<uint8_t>(src->AssignedStaffType);
dst->mechanic_time_since_call = src->MechanicTimeSinceCall;
dst->park_entry_time = src->HireDate;
dst->staff_id = src->StaffId;
dst->staff_orders = src->StaffOrders;
dst->staff_mowing_timeout = src->StaffMowingTimeout;
dst->paid_to_enter = src->StaffLawnsMown;

View File

@ -80,4 +80,5 @@ private:
void ExportUserStrings();
void RebuildEntityLinks();
void RebuildEntitySpatialLocation(const TileCoordsXY& loc);
void ExportStaffPatrolAreas();
};

View File

@ -421,8 +421,6 @@ public:
ImportRideMeasurements();
gNextGuestNumber = _s6.next_guest_index;
gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos;
std::memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas));
std::memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes));
// unk_13CA73E
// pad_13CA73F
// unk_13CA740
@ -485,6 +483,7 @@ public:
FixLandOwnership();
research_determine_first_of_type();
staff_update_greyed_patrol_areas();
}
void FixLandOwnership() const
@ -1444,6 +1443,43 @@ public:
}
}
void ImportStaffPatrolArea(Staff* staffmember, uint8_t staffId)
{
// First check staff mode as vanilla did not clean up patrol areas when switching from patrol to walk
// without doing this we could accidentally add a patrol when it didn't exist.
if (_s6.staff_modes[staffId] != RCT2StaffMode::Patrol)
{
return;
}
int32_t peepOffset = staffId * RCT12_PATROL_AREA_SIZE;
for (int32_t i = 0; i < RCT12_PATROL_AREA_SIZE; i++)
{
if (_s6.patrol_areas[peepOffset + i] == 0)
{
// No patrol for this area
continue;
}
// Loop over the bits of the uint32_t
for (int32_t j = 0; j < 32; j++)
{
int8_t bit = (_s6.patrol_areas[peepOffset + i] >> j) & 1;
if (bit == 0)
{
// No patrol for this area
continue;
}
// val contains the 6 highest bits of both the x and y coordinates
int32_t val = j | (i << 5);
int32_t x = val & 0x03F;
x <<= 7;
int32_t y = val & 0xFC0;
y <<= 1;
staffmember->SetPatrolArea({ x, y }, true);
}
}
}
void ImportEntities()
{
for (int32_t i = 0; i < RCT2_MAX_SPRITES; i++)
@ -1866,13 +1902,14 @@ template<> void S6Importer::ImportEntity<Staff>(const RCT12SpriteBase& baseSrc)
dst->MechanicTimeSinceCall = src->mechanic_time_since_call;
dst->HireDate = src->park_entry_time;
dst->StaffId = src->staff_id;
dst->StaffOrders = src->staff_orders;
dst->StaffMowingTimeout = src->staff_mowing_timeout;
dst->StaffLawnsMown = src->paid_to_enter;
dst->StaffGardensWatered = src->paid_on_rides;
dst->StaffLitterSwept = src->paid_on_food;
dst->StaffBinsEmptied = src->paid_on_souvenirs;
ImportStaffPatrolArea(dst, src->staff_id);
}
template<> void S6Importer::ImportEntity<SteamParticle>(const RCT12SpriteBase& baseSrc)

View File

@ -20,6 +20,7 @@
#include "../localisation/Localisation.h"
#include "../network/network.h"
#include "../paint/VirtualFloor.h"
#include "../peep/Staff.h"
#include "../ui/UiContext.h"
#include "../ui/WindowManager.h"
#include "../util/Util.h"

View File

@ -11,6 +11,8 @@
# include "ScStaff.hpp"
# include "../../../peep/Staff.h"
namespace OpenRCT2::Scripting
{
ScStaff::ScStaff(uint16_t Id)

View File

@ -12,6 +12,7 @@
# include "ScMap.hpp"
# include "../../../common.h"
# include "../../../peep/Staff.h"
# include "../../../ride/Ride.h"
# include "../../../ride/TrainManager.h"
# include "../../../world/Balloon.h"

View File

@ -11,6 +11,7 @@
#include "../core/DataSerialiser.h"
#include "../peep/Peep.h"
#include "../peep/Staff.h"
#include "../ride/Vehicle.h"
#include "Balloon.h"
#include "Duck.h"
@ -223,7 +224,6 @@ void Staff::Serialise(DataSerialiser& stream)
stream << AssignedStaffType;
stream << MechanicTimeSinceCall;
stream << HireDate;
stream << StaffId;
stream << StaffOrders;
stream << StaffMowingTimeout;
stream << StaffLawnsMown;

View File

@ -9,12 +9,12 @@
#include "EntityTweener.h"
#include "../peep/Peep.h"
#include "../peep/Staff.h"
#include "../ride/Vehicle.h"
#include "EntityList.h"
#include "Sprite.h"
#include <cmath>
void EntityTweener::PopulateEntities()
{
for (auto ent : EntityList<Guest>())

View File

@ -18,6 +18,7 @@
#include "../interface/Viewport.h"
#include "../peep/Peep.h"
#include "../peep/RideUseSystem.h"
#include "../peep/Staff.h"
#include "../ride/Vehicle.h"
#include "../scenario/Scenario.h"
#include "Balloon.h"
@ -555,6 +556,7 @@ static void FreeEntity(EntityBase& entity)
if (staff != nullptr)
{
staff->SetName({});
staff->ClearPatrolArea();
}
else if (guest != nullptr)
{