diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp index 4ad444d416..4af848da5a 100644 --- a/src/openrct2-ui/windows/Staff.cpp +++ b/src/openrct2-ui/windows/Staff.cpp @@ -530,7 +530,7 @@ void window_staff_overview_mousedown(rct_window* w, rct_widgetindex widgetIndex, } // Disable clear patrol area if no area is set. - if (gStaffModes[peep->StaffId] != StaffMode::Patrol) + if (!peep->HasPatrolArea()) { Dropdown::SetDisabled(1, true); } @@ -550,20 +550,13 @@ void window_staff_overview_dropdown(rct_window* w, rct_widgetindex widgetIndex, // Clear patrol if (dropdownIndex == 1) { - const auto peep = GetStaff(w); - if (peep == nullptr) + const auto staff = GetStaff(w); + if (staff != nullptr) { - return; + staff->ClearPatrolArea(); + gfx_invalidate_screen(); + staff_update_greyed_patrol_areas(); } - for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) - { - gStaffPatrolAreas[peep->StaffId * STAFF_PATROL_AREA_SIZE + i] = 0; - } - assert(gStaffModes[peep->StaffId] == StaffMode::Patrol); - gStaffModes[peep->StaffId] = StaffMode::Walk; - - gfx_invalidate_screen(); - staff_update_greyed_patrol_areas(); } else { diff --git a/src/openrct2-ui/windows/StaffList.cpp b/src/openrct2-ui/windows/StaffList.cpp index cfed859a0b..589df6caac 100644 --- a/src/openrct2-ui/windows/StaffList.cpp +++ b/src/openrct2-ui/windows/StaffList.cpp @@ -414,7 +414,7 @@ public: DrawTextEllipsised(&dpi, { actionOffset, y }, actionColumnSize, format, ft); // True if a patrol path is set for the worker - if (gStaffModes[peep->StaffId] == StaffMode::Patrol) + if (peep->HasPatrolArea()) { gfx_draw_sprite(&dpi, ImageId(SPR_STAFF_PATROL_PATH), { nameColumnSize + 5, y }); } @@ -566,11 +566,7 @@ private: if (isPatrolAreaSet) { - if (gStaffModes[peep->StaffId] != StaffMode::Patrol) - { - continue; - } - if (!peep->IsLocationInPatrol(footpathCoords)) + if (!peep->HasPatrolArea() || peep->IsLocationInPatrol(footpathCoords)) { continue; } diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 5c59d1885e..a70e6b2e86 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -515,6 +515,8 @@ void game_fix_save_vars() // Fix gParkEntrance locations for which the tile_element no longer exists fix_park_entrance_locations(); + + staff_update_greyed_patrol_areas(); } void game_load_init() diff --git a/src/openrct2/GameStateSnapshots.cpp b/src/openrct2/GameStateSnapshots.cpp index 69c065d405..14c6cebb02 100644 --- a/src/openrct2/GameStateSnapshots.cpp +++ b/src/openrct2/GameStateSnapshots.cpp @@ -261,7 +261,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); diff --git a/src/openrct2/ParkFile.cpp b/src/openrct2/ParkFile.cpp index ae0f0bd892..0ed6a9906b 100644 --- a/src/openrct2/ParkFile.cpp +++ b/src/openrct2/ParkFile.cpp @@ -1440,29 +1440,27 @@ namespace OpenRCT2 ReadWritePeep(cs, entity); } - static std::vector GetPatrolArea(uint32_t staffId) + static std::vector GetPatrolArea(Staff& staff) { std::vector area; - auto hasPatrol = gStaffModes[staffId] == StaffMode::Patrol; - if (hasPatrol) + if (staff.PatrolInfo != nullptr) { - auto offset = staffId * STAFF_PATROL_AREA_SIZE; - for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) + for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) { // 32 blocks per array item (32 bits) - auto arrayItem = gStaffPatrolAreas[offset + i]; - for (int32_t j = 0; j < 32; j++) + auto arrayItem = staff.PatrolInfo->Data[i]; + for (size_t j = 0; j < 32; j++) { - int32_t blockIndex = (i * 32) + j; + auto blockIndex = (i * 32) + j; if (arrayItem & (1 << j)) { - auto sx = (blockIndex % 64) * 4; - auto sy = (blockIndex / 64) * 4; - for (int32_t y = 0; y < 4; y++) + auto sx = (blockIndex % STAFF_PATROL_AREA_BLOCKS_PER_LINE) * 4; + auto sy = (blockIndex / STAFF_PATROL_AREA_BLOCKS_PER_LINE) * 4; + for (size_t y = 0; y < 4; y++) { - for (int32_t x = 0; x < 4; x++) + for (size_t x = 0; x < 4; x++) { - area.push_back({ sx + x, sy + y }); + area.push_back({ static_cast(sx + x), static_cast(sy + y) }); } } } @@ -1472,18 +1470,17 @@ namespace OpenRCT2 return area; } - static void SetPatrolArea(uint32_t staffId, const std::vector& area) + static void SetPatrolArea(Staff& staff, const std::vector& area) { if (area.empty()) { - gStaffModes[staffId] = StaffMode::Walk; + staff.ClearPatrolArea(); } else { - gStaffModes[staffId] = StaffMode::Patrol; for (const auto& coord : area) { - staff_set_patrol_area(staffId, coord.ToCoordsXY(), true); + staff.SetPatrolArea(coord.ToCoordsXY(), true); } } } @@ -1495,12 +1492,12 @@ namespace OpenRCT2 std::vector patrolArea; if (cs.GetMode() == OrcaStream::Mode::WRITING) { - patrolArea = GetPatrolArea(entity.StaffId); + patrolArea = GetPatrolArea(entity); } cs.ReadWriteVector(patrolArea, [&cs](TileCoordsXY& value) { cs.ReadWrite(value); }); if (cs.GetMode() == OrcaStream::Mode::READING) { - SetPatrolArea(entity.StaffId, patrolArea); + SetPatrolArea(entity, patrolArea); } } @@ -1648,11 +1645,6 @@ namespace OpenRCT2 Vehicle, Guest, Staff, Litter, SteamParticle, MoneyEffect, VehicleCrashParticle, ExplosionCloud, CrashSplashParticle, ExplosionFlare, JumpingFountain, Balloon, Duck>(cs); } - - if (cs.GetMode() == OrcaStream::Mode::READING) - { - staff_update_greyed_patrol_areas(); - } }); } } // namespace OpenRCT2 diff --git a/src/openrct2/actions/StaffHireNewAction.cpp b/src/openrct2/actions/StaffHireNewAction.cpp index 49692d4648..a0a826326c 100644 --- a/src/openrct2/actions/StaffHireNewAction.cpp +++ b/src/openrct2/actions/StaffHireNewAction.cpp @@ -114,20 +114,6 @@ 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) - { - // Too many staff members exist already. - return MakeResult(GameActions::Status::NoFreeElements, STR_TOO_MANY_STAFF_IN_GAME); - } - Staff* newPeep = CreateEntity(); if (newPeep == nullptr) { @@ -222,14 +208,7 @@ GameActions::Result::Ptr StaffHireNewAction::QueryExecute(bool execute) const newPeep->EnergyTarget = 0x60; newPeep->StaffMowingTimeout = 0; - newPeep->StaffId = staffIndex; - - gStaffModes[staffIndex] = StaffMode::Walk; - - for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) - { - gStaffPatrolAreas[staffIndex * STAFF_PATROL_AREA_SIZE + i] = 0; - } + newPeep->PatrolInfo = nullptr; res->peepSriteIndex = newPeep->sprite_index; } diff --git a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp index 8f981a9468..ce5008ad39 100644 --- a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp +++ b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp @@ -63,27 +63,11 @@ GameActions::Result::Ptr StaffSetPatrolAreaAction::Execute() const return MakeResult(GameActions::Status::InvalidParameters, STR_NONE); } - int32_t patrolOffset = staff->StaffId * STAFF_PATROL_AREA_SIZE; - - staff_toggle_patrol_area(staff->StaffId, _loc); - - bool isPatrolling = false; - for (int32_t i = 0; i < 128; i++) + staff->TogglePatrolArea(_loc); + if (!staff->HasPatrolArea()) { - if (gStaffPatrolAreas[patrolOffset + i]) - { - isPatrolling = true; - break; - } - } - - if (isPatrolling) - { - gStaffModes[staff->StaffId] = StaffMode::Patrol; - } - else if (gStaffModes[staff->StaffId] == StaffMode::Patrol) - { - gStaffModes[staff->StaffId] = StaffMode::Walk; + // This frees the data if there is no patrol area + staff->ClearPatrolArea(); } for (int32_t y = 0; y < 4 * COORDS_XY_STEP; y += COORDS_XY_STEP) diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 4bcdab0380..fcc014f384 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1248,15 +1248,12 @@ static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] cons spriteCount += GetEntityListCount(EntityType(i)); } - int32_t staffCount = GetEntityListCount(EntityType::Staff); - auto bannerCount = GetNumBanners(); console.WriteFormatLine("Sprites: %d/%d", spriteCount, MAX_ENTITIES); console.WriteFormatLine("Map Elements: %zu/%d", tileElementCount, MAX_TILE_ELEMENTS); console.WriteFormatLine("Banners: %d/%zu", bannerCount, MAX_BANNERS); console.WriteFormatLine("Rides: %d/%d", rideCount, MAX_RIDES); - console.WriteFormatLine("Staff: %d/%d", staffCount, STAFF_MAX_COUNT); console.WriteFormatLine("Images: %zu/%zu", ImageListGetUsedCount(), ImageListGetMaximum()); return 0; } diff --git a/src/openrct2/peep/Peep.cpp b/src/openrct2/peep/Peep.cpp index daf4e082cc..5ceb7b7440 100644 --- a/src/openrct2/peep/Peep.cpp +++ b/src/openrct2/peep/Peep.cpp @@ -652,7 +652,6 @@ void peep_sprite_remove(Peep* peep) } else { - gStaffModes[staff->StaffId] = StaffMode::None; staff_update_greyed_patrol_areas(); News::DisableNewsItems(News::ItemType::Peep, staff->sprite_index); diff --git a/src/openrct2/peep/Peep.h b/src/openrct2/peep/Peep.h index 094831c7c6..840e87e380 100644 --- a/src/openrct2/peep/Peep.h +++ b/src/openrct2/peep/Peep.h @@ -653,7 +653,6 @@ struct Peep : SpriteBase uint8_t PathCheckOptimisation; // see peep.checkForPath union { - uint8_t StaffId; ride_id_t GuestHeadingToRideId; }; union @@ -865,11 +864,23 @@ private: void GoToRideEntrance(Ride* ride); }; +// The number of elements in the PatrolArea.Data 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 * 2048 = 65536 bits, which is also the number of 4x4 squares on a 1024x1024 map. +constexpr size_t STAFF_PATROL_AREA_BLOCKS_PER_LINE = 1024 / 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]; +}; + struct Staff : Peep { static constexpr auto cEntityType = EntityType::Staff; public: + PatrolArea* PatrolInfo; + void UpdateStaff(uint32_t stepsToTake); void Tick128UpdateStaff(); bool IsMechanic() const; @@ -886,6 +897,11 @@ public: static void ResetStats(); + void ClearPatrolArea(); + void TogglePatrolArea(const CoordsXY& coords); + void SetPatrolArea(const CoordsXY& coords, bool value); + bool HasPatrolArea() const; + private: void UpdatePatrolling(); void UpdateMowing(); diff --git a/src/openrct2/peep/Staff.cpp b/src/openrct2/peep/Staff.cpp index f4d0018d75..2882335661 100644 --- a/src/openrct2/peep/Staff.cpp +++ b/src/openrct2/peep/Staff.cpp @@ -60,15 +60,13 @@ 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(StaffType::Count)) * STAFF_PATROL_AREA_SIZE]; -StaffMode gStaffModes[STAFF_MAX_COUNT + static_cast(StaffType::Count)]; uint16_t gStaffDrawPatrolAreas; colour_t gStaffHandymanColour; colour_t gStaffMechanicColour; colour_t gStaffSecurityColour; +static PatrolArea _mergedPatrolAreas[EnumValue(StaffType::Count)]; + // Maximum manhattan distance that litter can be for a handyman to seek to it const uint16_t MAX_LITTER_DISTANCE = 3 * COORDS_XY_STEP; @@ -83,12 +81,6 @@ template<> bool SpriteBase::Is() 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(StaffType::Count)); i++) - gStaffModes[i] = StaffMode::Walk; - staff_update_greyed_patrol_areas(); } @@ -140,22 +132,22 @@ 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(StaffType::Count); ++staff_type) + for (int32_t staff_type = 0; staff_type < EnumValue(StaffType::Count); staff_type++) { - int32_t staffPatrolOffset = (staff_type + STAFF_MAX_COUNT) * STAFF_PATROL_AREA_SIZE; - for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) - { - gStaffPatrolAreas[staffPatrolOffset + i] = 0; - } + auto mergedData = _mergedPatrolAreas[staff_type].Data; + std::memset(mergedData, 0, sizeof(PatrolArea)); - for (auto peep : EntityList()) + for (auto staff : EntityList()) { - if (static_cast(peep->AssignedStaffType) == staff_type) + if (EnumValue(staff->AssignedStaffType) == staff_type) { - int32_t peepPatrolOffset = peep->StaffId * STAFF_PATROL_AREA_SIZE; - for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) + if (staff->PatrolInfo != nullptr) { - gStaffPatrolAreas[staffPatrolOffset + i] |= gStaffPatrolAreas[peepPatrolOffset + i]; + auto staffData = staff->PatrolInfo->Data; + for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) + { + mergedData[i] |= staffData[i]; + } } } } @@ -173,7 +165,7 @@ bool Staff::IsLocationInPatrol(const CoordsXY& loc) const return false; // Check if staff has patrol area - if (gStaffModes[StaffId] != StaffMode::Patrol) + if (!HasPatrolArea()) return true; return IsPatrolAreaSet(loc); @@ -363,39 +355,48 @@ void Staff::ResetStats() static std::pair getPatrolAreaOffsetIndex(const CoordsXY& coords) { - // Patrol areas are 4 * 4 tiles (32 * 4) = 128 = 2^^7 - auto hash = ((coords.x & 0x1F80) >> 7) | ((coords.y & 0x1F80) >> 1); - return { hash >> 5, hash & 0x1F }; + 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 = bitIndex / 32; + auto byteBitIndex = bitIndex % 32; + return { byteIndex, byteBitIndex }; } -static bool staff_is_patrol_area_set(int32_t staffIndex, const CoordsXY& coords) +void Staff::ClearPatrolArea() { - // 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. + delete PatrolInfo; + PatrolInfo = nullptr; +} + +void Staff::TogglePatrolArea(const CoordsXY& coords) +{ + if (PatrolInfo == nullptr) + { + PatrolInfo = new PatrolArea(); + } - int32_t peepOffset = staffIndex * STAFF_PATROL_AREA_SIZE; auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords); - return gStaffPatrolAreas[peepOffset + offset] & (1UL << bitIndex); + PatrolInfo->Data[offset] ^= (1 << bitIndex); } -bool Staff::IsPatrolAreaSet(const CoordsXY& coords) const +void Staff::SetPatrolArea(const CoordsXY& coords, bool value) { - return staff_is_patrol_area_set(StaffId, coords); -} + if (PatrolInfo == nullptr) + { + if (value) + { + PatrolInfo = new PatrolArea(); + } + else + { + return; + } + } -bool staff_is_patrol_area_set_for_type(StaffType type, const CoordsXY& coords) -{ - return staff_is_patrol_area_set(STAFF_MAX_COUNT + static_cast(type), coords); -} - -void staff_set_patrol_area(int32_t staffIndex, const CoordsXY& coords, bool value) -{ - int32_t peepOffset = staffIndex * STAFF_PATROL_AREA_SIZE; auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords); - uint32_t* addr = &gStaffPatrolAreas[peepOffset + offset]; + auto* addr = &PatrolInfo->Data[offset]; if (value) { *addr |= (1 << bitIndex); @@ -406,11 +407,36 @@ void staff_set_patrol_area(int32_t staffIndex, const CoordsXY& coords, bool valu } } -void staff_toggle_patrol_area(int32_t staffIndex, const CoordsXY& coords) +bool Staff::HasPatrolArea() const +{ + if (PatrolInfo == nullptr) + return false; + + for (auto datapoint : PatrolInfo->Data) + { + if (datapoint != 0) + { + return true; + } + } + + return false; +} + +bool Staff::IsPatrolAreaSet(const CoordsXY& coords) const +{ + 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) { - int32_t peepOffset = staffIndex * STAFF_PATROL_AREA_SIZE; auto [offset, bitIndex] = getPatrolAreaOffsetIndex(coords); - gStaffPatrolAreas[peepOffset + offset] ^= (1 << bitIndex); + return _mergedPatrolAreas[EnumValue(type)].Data[offset] & (1UL << bitIndex); } /** diff --git a/src/openrct2/peep/Staff.h b/src/openrct2/peep/Staff.h index 245af03882..80d0bbbae2 100644 --- a/src/openrct2/peep/Staff.h +++ b/src/openrct2/peep/Staff.h @@ -13,18 +13,6 @@ #include "../common.h" #include "Peep.h" -#define STAFF_MAX_COUNT 200 -// 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. -#define STAFF_PATROL_AREA_SIZE 128 - -enum class StaffMode : uint8_t -{ - None, - Walk, - Patrol = 3 -}; - enum STAFF_ORDERS { STAFF_ORDERS_SWEEPING = (1 << 0), @@ -54,8 +42,6 @@ enum class EntertainerCostume : uint8_t extern const rct_string_id StaffCostumeNames[static_cast(EntertainerCostume::Count)]; -extern uint32_t gStaffPatrolAreas[(STAFF_MAX_COUNT + static_cast(StaffType::Count)) * STAFF_PATROL_AREA_SIZE]; -extern StaffMode gStaffModes[STAFF_MAX_COUNT + static_cast(StaffType::Count)]; extern uint16_t gStaffDrawPatrolAreas; extern colour_t gStaffHandymanColour; extern colour_t gStaffMechanicColour; @@ -66,8 +52,6 @@ void staff_set_name(uint16_t spriteIndex, const char* name); bool staff_hire_new_member(StaffType staffType, EntertainerCostume entertainerType); void staff_update_greyed_patrol_areas(); bool staff_is_patrol_area_set_for_type(StaffType type, const CoordsXY& coords); -void staff_set_patrol_area(int32_t staffIndex, const CoordsXY& coords, bool value); -void staff_toggle_patrol_area(int32_t staffIndex, 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(); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 4714db2b2c..0dd49e697e 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1218,7 +1218,6 @@ private: { ImportEntity(_s4.sprites[i].unknown); } - FixImportStaff(); } void SetVehicleColours(Vehicle* dst, const rct1_vehicle* src) @@ -1269,25 +1268,6 @@ private: } } - 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(_s4.staff_modes[i]); - } - - for (auto peep : EntityList()) - { - 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(); - } - void ImportPeep(Peep* dst, const rct1_peep* src) { // Peep vs. staff (including which kind) @@ -1346,7 +1326,7 @@ private: dst->PathfindGoal.direction = INVALID_DIRECTION; } - void ImportStaffPatrolArea(Staff* staffmember) + void ImportStaffPatrolArea(Staff* staffmember, size_t staffId) { // 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. @@ -1358,9 +1338,11 @@ private: // 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++) { + staffmember->ClearPatrolArea(); + if (_s4.patrol_areas[peepOffset + i] == 0) { // No patrol for this area @@ -1382,7 +1364,7 @@ private: x <<= 7; int32_t y = val & 0x3E0; y <<= 2; - staff_set_patrol_area(staffmember->StaffId, { x, y }, true); + staffmember->SetPatrolArea({ x, y }, true); } } } @@ -2926,13 +2908,14 @@ template<> void S4Importer::ImportEntity(const RCT12SpriteBase& srcBase) 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(const RCT12SpriteBase& srcBase) diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index a5a5b59f73..2c312a024d 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -400,8 +400,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 @@ -1553,6 +1551,37 @@ public: RCT12AddDefaultObjects(objectList); return objectList; } + + std::vector GetPatrolArea(uint8_t staffIndex) + { + std::vector area; + if (staffIndex < RCT2_MAX_STAFF && _s6.staff_modes[staffIndex] == 3) + { + auto offset = staffIndex * RCT12_PATROL_AREA_SIZE; + for (size_t i = 0; i < RCT12_PATROL_AREA_SIZE; i++) + { + // 32 blocks per array item (32 bits) + auto arrayItem = _s6.patrol_areas[offset + i]; + for (size_t j = 0; j < 32; j++) + { + auto blockIndex = (i * 32) + j; + if (arrayItem & (1 << j)) + { + auto sx = (blockIndex % 64) * 4; + auto sy = (blockIndex / 64) * 4; + for (size_t y = 0; y < 4; y++) + { + for (size_t x = 0; x < 4; x++) + { + area.push_back({ static_cast(sx + x), static_cast(sy + y) }); + } + } + } + } + } + } + return area; + } }; template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) @@ -1748,13 +1777,19 @@ template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) 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; + + dst->ClearPatrolArea(); + auto patrolArea = GetPatrolArea(src->staff_id); + for (const auto& coord : patrolArea) + { + dst->SetPatrolArea(coord.ToCoordsXY(), true); + } } template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) diff --git a/src/openrct2/world/Sprite.cpp b/src/openrct2/world/Sprite.cpp index 2f5c123196..19ba3631c9 100644 --- a/src/openrct2/world/Sprite.cpp +++ b/src/openrct2/world/Sprite.cpp @@ -223,6 +223,24 @@ const std::list& GetEntityList(const EntityType id) return gEntityLists[EnumValue(id)]; } +/** + * Frees any dynamically attached memory to the entity, such as peep name. + */ +static void FreeEntity(SpriteBase& entity) +{ + auto peep = entity.As(); + if (peep != nullptr) + { + peep->SetName({}); + + auto staff = peep->As(); + if (staff != nullptr) + { + staff->ClearPatrolArea(); + } + } +} + /** * * rct2: 0x0069EB13 @@ -230,6 +248,16 @@ const std::list& GetEntityList(const EntityType id) void reset_sprite_list() { gSavedAge = 0; + + for (int32_t i = 0; i < MAX_ENTITIES; ++i) + { + auto* entity = GetEntity(i); + if (entity != nullptr) + { + FreeEntity(*entity); + } + } + std::memset(static_cast(_spriteList), 0, sizeof(_spriteList)); for (int32_t i = 0; i < MAX_ENTITIES; ++i) { @@ -687,11 +715,7 @@ void sprite_set_coordinates(const CoordsXYZ& spritePos, SpriteBase* sprite) */ void sprite_remove(SpriteBase* sprite) { - auto peep = sprite->As(); - if (peep != nullptr) - { - peep->SetName({}); - } + FreeEntity(*sprite); EntityTweener::Get().RemoveEntity(sprite); RemoveFromEntityList(sprite); // remove from existing list