mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 15:52:55 +01:00
Merge pull request #8849 'ga-staff-hire'
This commit is contained in:
@@ -206,13 +206,13 @@ static void window_staff_list_mouseup(rct_window* w, rct_widgetindex widgetIndex
|
|||||||
break;
|
break;
|
||||||
case WIDX_STAFF_LIST_HIRE_BUTTON:
|
case WIDX_STAFF_LIST_HIRE_BUTTON:
|
||||||
{
|
{
|
||||||
int32_t staffType = _windowStaffListSelectedTab;
|
STAFF_TYPE staffType = static_cast<STAFF_TYPE>(_windowStaffListSelectedTab);
|
||||||
|
ENTERTAINER_COSTUME costume = ENTERTAINER_COSTUME_COUNT;
|
||||||
if (staffType == STAFF_TYPE_ENTERTAINER)
|
if (staffType == STAFF_TYPE_ENTERTAINER)
|
||||||
{
|
{
|
||||||
uint8_t costume = window_staff_list_get_random_entertainer_costume();
|
costume = static_cast<ENTERTAINER_COSTUME>(window_staff_list_get_random_entertainer_costume());
|
||||||
staffType += costume;
|
|
||||||
}
|
}
|
||||||
hire_new_staff_member(staffType);
|
staff_hire_new_member(staffType, costume);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON:
|
case WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON:
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ static GAME_COMMAND_CALLBACK_POINTER * const game_command_callback_table[] = {
|
|||||||
nullptr,
|
nullptr,
|
||||||
game_command_callback_place_banner,
|
game_command_callback_place_banner,
|
||||||
nullptr,
|
nullptr,
|
||||||
game_command_callback_hire_new_staff_member,
|
nullptr,
|
||||||
game_command_callback_pickup_guest,
|
game_command_callback_pickup_guest,
|
||||||
game_command_callback_pickup_staff
|
game_command_callback_pickup_staff
|
||||||
};
|
};
|
||||||
@@ -1290,7 +1290,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
|||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
game_command_hire_new_staff_member,
|
nullptr,
|
||||||
game_command_set_staff_patrol,
|
game_command_set_staff_patrol,
|
||||||
game_command_fire_staff_member,
|
game_command_fire_staff_member,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ enum GAME_COMMAND
|
|||||||
GAME_COMMAND_RAISE_WATER, // GA
|
GAME_COMMAND_RAISE_WATER, // GA
|
||||||
GAME_COMMAND_LOWER_WATER, // GA
|
GAME_COMMAND_LOWER_WATER, // GA
|
||||||
GAME_COMMAND_SET_BRAKES_SPEED, // GA
|
GAME_COMMAND_SET_BRAKES_SPEED, // GA
|
||||||
GAME_COMMAND_HIRE_NEW_STAFF_MEMBER,
|
GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, // GA
|
||||||
GAME_COMMAND_SET_STAFF_PATROL,
|
GAME_COMMAND_SET_STAFF_PATROL,
|
||||||
GAME_COMMAND_FIRE_STAFF_MEMBER,
|
GAME_COMMAND_FIRE_STAFF_MEMBER,
|
||||||
GAME_COMMAND_SET_STAFF_ORDERS, // GA
|
GAME_COMMAND_SET_STAFF_ORDERS, // GA
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "SignSetStyleAction.hpp"
|
#include "SignSetStyleAction.hpp"
|
||||||
#include "SmallSceneryPlaceAction.hpp"
|
#include "SmallSceneryPlaceAction.hpp"
|
||||||
#include "SmallSceneryRemoveAction.hpp"
|
#include "SmallSceneryRemoveAction.hpp"
|
||||||
|
#include "StaffHireNewAction.hpp"
|
||||||
#include "StaffSetColourAction.hpp"
|
#include "StaffSetColourAction.hpp"
|
||||||
#include "StaffSetCostumeAction.hpp"
|
#include "StaffSetCostumeAction.hpp"
|
||||||
#include "StaffSetNameAction.hpp"
|
#include "StaffSetNameAction.hpp"
|
||||||
@@ -91,6 +92,7 @@ namespace GameActions
|
|||||||
Register<SetParkEntranceFeeAction>();
|
Register<SetParkEntranceFeeAction>();
|
||||||
Register<SignSetNameAction>();
|
Register<SignSetNameAction>();
|
||||||
Register<SignSetStyleAction>();
|
Register<SignSetStyleAction>();
|
||||||
|
Register<StaffHireNewAction>();
|
||||||
Register<StaffSetColourAction>();
|
Register<StaffSetColourAction>();
|
||||||
Register<StaffSetNameAction>();
|
Register<StaffSetNameAction>();
|
||||||
Register<StaffSetOrdersAction>();
|
Register<StaffSetOrdersAction>();
|
||||||
|
|||||||
347
src/openrct2/actions/StaffHireNewAction.hpp
Normal file
347
src/openrct2/actions/StaffHireNewAction.hpp
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (c) 2014-2019 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 "../Cheats.h"
|
||||||
|
#include "../Context.h"
|
||||||
|
#include "../core/MemoryStream.h"
|
||||||
|
#include "../drawing/Drawing.h"
|
||||||
|
#include "../interface/Window.h"
|
||||||
|
#include "../localisation/Localisation.h"
|
||||||
|
#include "../localisation/StringIds.h"
|
||||||
|
#include "../management/Finance.h"
|
||||||
|
#include "../ride/Ride.h"
|
||||||
|
#include "../scenario/Scenario.h"
|
||||||
|
#include "../ui/UiContext.h"
|
||||||
|
#include "../ui/WindowManager.h"
|
||||||
|
#include "../world/Entrance.h"
|
||||||
|
#include "../world/Park.h"
|
||||||
|
#include "../world/Sprite.h"
|
||||||
|
#include "GameAction.h"
|
||||||
|
|
||||||
|
static constexpr const rct_string_id staffNames[] = {
|
||||||
|
STR_HANDYMAN_X,
|
||||||
|
STR_MECHANIC_X,
|
||||||
|
STR_SECURITY_GUARD_X,
|
||||||
|
STR_ENTERTAINER_X,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* rct2: 0x009929FC */
|
||||||
|
static constexpr const PeepSpriteType spriteTypes[] = {
|
||||||
|
PEEP_SPRITE_TYPE_HANDYMAN,
|
||||||
|
PEEP_SPRITE_TYPE_MECHANIC,
|
||||||
|
PEEP_SPRITE_TYPE_SECURITY,
|
||||||
|
PEEP_SPRITE_TYPE_ENTERTAINER_PANDA,
|
||||||
|
};
|
||||||
|
|
||||||
|
class StaffHireNewActionResult final : public GameActionResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StaffHireNewActionResult()
|
||||||
|
: GameActionResult(GA_ERROR::OK, STR_CANT_HIRE_NEW_STAFF)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
StaffHireNewActionResult(GA_ERROR error, rct_string_id message)
|
||||||
|
: GameActionResult(error, STR_CANT_HIRE_NEW_STAFF, message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t peepSriteIndex = SPRITE_INDEX_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_GAME_ACTION(StaffHireNewAction, GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, StaffHireNewActionResult)
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool _autoPosition = false;
|
||||||
|
uint8_t _staffType = STAFF_TYPE::STAFF_TYPE_COUNT;
|
||||||
|
uint8_t _entertainerType = ENTERTAINER_COSTUME::ENTERTAINER_COSTUME_COUNT;
|
||||||
|
uint32_t _staffOrders = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StaffHireNewAction() = default;
|
||||||
|
StaffHireNewAction(bool autoPosition, STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType, uint32_t staffOrders)
|
||||||
|
: _autoPosition(autoPosition)
|
||||||
|
, _staffType(staffType)
|
||||||
|
, _entertainerType(entertainerType)
|
||||||
|
, _staffOrders(staffOrders)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t GetActionFlags() const override
|
||||||
|
{
|
||||||
|
return GameAction::GetActionFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serialise(DataSerialiser & stream) override
|
||||||
|
{
|
||||||
|
GameAction::Serialise(stream);
|
||||||
|
|
||||||
|
stream << DS_TAG(_autoPosition) << DS_TAG(_staffType) << DS_TAG(_entertainerType) << DS_TAG(_staffOrders);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameActionResult::Ptr Query() const override
|
||||||
|
{
|
||||||
|
return QueryExecute(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameActionResult::Ptr Execute() const override
|
||||||
|
{
|
||||||
|
return QueryExecute(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameActionResult::Ptr QueryExecute(bool execute) const
|
||||||
|
{
|
||||||
|
auto res = std::make_unique<StaffHireNewActionResult>();
|
||||||
|
|
||||||
|
res->ExpenditureType = RCT_EXPENDITURE_TYPE_WAGES;
|
||||||
|
|
||||||
|
if (_staffType >= STAFF_TYPE_COUNT)
|
||||||
|
{
|
||||||
|
// Invalid staff type.
|
||||||
|
log_error("Tried to use invalid staff type: %u", (uint32_t)_staffType);
|
||||||
|
|
||||||
|
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gSpriteListCount[SPRITE_LIST_NULL] < 400)
|
||||||
|
{
|
||||||
|
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_PEOPLE_IN_GAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_staffType == STAFF_TYPE_ENTERTAINER)
|
||||||
|
{
|
||||||
|
if (_entertainerType >= ENTERTAINER_COSTUME_COUNT)
|
||||||
|
{
|
||||||
|
// Invalid entertainer costume
|
||||||
|
log_error("Tried to use invalid entertainer type: %u", (uint32_t)_entertainerType);
|
||||||
|
|
||||||
|
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t availableCostumes = staff_get_available_entertainer_costumes();
|
||||||
|
if (!(availableCostumes & (1 << _entertainerType)))
|
||||||
|
{
|
||||||
|
// Entertainer costume unavailable
|
||||||
|
log_error("Tried to use unavailable entertainer type: %u", (uint32_t)_entertainerType);
|
||||||
|
|
||||||
|
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a free slot in the staff modes.
|
||||||
|
int32_t staffIndex;
|
||||||
|
for (staffIndex = 0; staffIndex < STAFF_MAX_COUNT; ++staffIndex)
|
||||||
|
{
|
||||||
|
if (!(gStaffModes[staffIndex] & 1))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (staffIndex == STAFF_MAX_COUNT)
|
||||||
|
{
|
||||||
|
// Too many staff members exist already.
|
||||||
|
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_STAFF_IN_GAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
Peep* newPeep = &(create_sprite(GetFlags())->peep);
|
||||||
|
if (newPeep == nullptr)
|
||||||
|
{
|
||||||
|
// Too many peeps exist already.
|
||||||
|
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_PEOPLE_IN_GAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (execute == false)
|
||||||
|
{
|
||||||
|
// In query we just want to see if we can obtain a sprite slot.
|
||||||
|
sprite_remove((rct_sprite*)newPeep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
move_sprite_to_list((rct_sprite*)newPeep, SPRITE_LIST_PEEP * 2);
|
||||||
|
|
||||||
|
newPeep->sprite_identifier = 1;
|
||||||
|
newPeep->window_invalidate_flags = 0;
|
||||||
|
newPeep->action = PEEP_ACTION_NONE_2;
|
||||||
|
newPeep->special_sprite = 0;
|
||||||
|
newPeep->action_sprite_image_offset = 0;
|
||||||
|
newPeep->no_action_frame_num = 0;
|
||||||
|
newPeep->action_sprite_type = PEEP_ACTION_SPRITE_TYPE_NONE;
|
||||||
|
newPeep->path_check_optimisation = 0;
|
||||||
|
newPeep->type = PEEP_TYPE_STAFF;
|
||||||
|
newPeep->outside_of_park = 0;
|
||||||
|
newPeep->peep_flags = 0;
|
||||||
|
newPeep->paid_to_enter = 0;
|
||||||
|
newPeep->paid_on_rides = 0;
|
||||||
|
newPeep->paid_on_food = 0;
|
||||||
|
newPeep->paid_on_souvenirs = 0;
|
||||||
|
newPeep->staff_orders = _staffOrders;
|
||||||
|
|
||||||
|
uint16_t idSearchSpriteIndex;
|
||||||
|
Peep* idSearchPeep;
|
||||||
|
|
||||||
|
// We search for the first available id for a given staff type
|
||||||
|
uint32_t newStaffId = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
++newStaffId;
|
||||||
|
|
||||||
|
FOR_ALL_STAFF (idSearchSpriteIndex, idSearchPeep)
|
||||||
|
{
|
||||||
|
if (idSearchPeep->staff_type != _staffType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (idSearchPeep->id == newStaffId)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newPeep->id = newStaffId;
|
||||||
|
newPeep->staff_type = _staffType;
|
||||||
|
|
||||||
|
PeepSpriteType spriteType = spriteTypes[_staffType];
|
||||||
|
if (_staffType == STAFF_TYPE_ENTERTAINER)
|
||||||
|
{
|
||||||
|
spriteType = static_cast<PeepSpriteType>(PEEP_SPRITE_TYPE_ENTERTAINER_PANDA + _entertainerType);
|
||||||
|
}
|
||||||
|
newPeep->name_string_idx = staffNames[_staffType];
|
||||||
|
newPeep->sprite_type = spriteType;
|
||||||
|
|
||||||
|
const rct_sprite_bounds* spriteBounds = g_peep_animation_entries[spriteType].sprite_bounds;
|
||||||
|
newPeep->sprite_width = spriteBounds->sprite_width;
|
||||||
|
newPeep->sprite_height_negative = spriteBounds->sprite_height_negative;
|
||||||
|
newPeep->sprite_height_positive = spriteBounds->sprite_height_positive;
|
||||||
|
|
||||||
|
if (_autoPosition == true)
|
||||||
|
{
|
||||||
|
AutoPositionNewStaff(newPeep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NOTE: This state is required for the window to act.
|
||||||
|
newPeep->state = PEEP_STATE_PICKED;
|
||||||
|
|
||||||
|
sprite_move(newPeep->x, newPeep->y, newPeep->z, (rct_sprite*)newPeep);
|
||||||
|
invalidate_sprite_2((rct_sprite*)newPeep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Staff uses this
|
||||||
|
newPeep->time_in_park = gDateMonthsElapsed;
|
||||||
|
newPeep->pathfind_goal.x = 0xFF;
|
||||||
|
newPeep->pathfind_goal.y = 0xFF;
|
||||||
|
newPeep->pathfind_goal.z = 0xFF;
|
||||||
|
newPeep->pathfind_goal.direction = 0xFF;
|
||||||
|
|
||||||
|
uint8_t colour = staff_get_colour(_staffType);
|
||||||
|
newPeep->tshirt_colour = colour;
|
||||||
|
newPeep->trousers_colour = colour;
|
||||||
|
|
||||||
|
// Staff energy determines their walking speed
|
||||||
|
newPeep->energy = 0x60;
|
||||||
|
newPeep->energy_target = 0x60;
|
||||||
|
newPeep->staff_mowing_timeout = 0;
|
||||||
|
|
||||||
|
peep_update_name_sort(newPeep);
|
||||||
|
|
||||||
|
newPeep->staff_id = newStaffId;
|
||||||
|
|
||||||
|
gStaffModes[staffIndex] = STAFF_MODE_WALK;
|
||||||
|
|
||||||
|
for (int32_t newStaffIndex = 0; newStaffIndex < STAFF_PATROL_AREA_SIZE; newStaffIndex++)
|
||||||
|
{
|
||||||
|
gStaffPatrolAreas[newStaffIndex * STAFF_PATROL_AREA_SIZE + newStaffId] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
res->peepSriteIndex = newPeep->sprite_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoPositionNewStaff(Peep * newPeep) const
|
||||||
|
{
|
||||||
|
// Find a location to place new staff member
|
||||||
|
|
||||||
|
newPeep->state = PEEP_STATE_FALLING;
|
||||||
|
|
||||||
|
int16_t x, y, z;
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint16_t sprite_index;
|
||||||
|
Peep* guest = nullptr;
|
||||||
|
TileElement* guest_tile = nullptr;
|
||||||
|
|
||||||
|
// Count number of walking guests
|
||||||
|
FOR_ALL_GUESTS (sprite_index, guest)
|
||||||
|
{
|
||||||
|
if (guest->state == PEEP_STATE_WALKING)
|
||||||
|
{
|
||||||
|
// Check the walking guest's tile. Only count them if they're on a path tile.
|
||||||
|
guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z);
|
||||||
|
if (guest_tile != nullptr)
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
// Place staff at a random guest
|
||||||
|
uint32_t rand = scenario_rand_max(count);
|
||||||
|
FOR_ALL_GUESTS (sprite_index, guest)
|
||||||
|
{
|
||||||
|
if (guest->state == PEEP_STATE_WALKING)
|
||||||
|
{
|
||||||
|
guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z);
|
||||||
|
if (guest_tile != nullptr)
|
||||||
|
{
|
||||||
|
if (rand == 0)
|
||||||
|
break;
|
||||||
|
--rand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x = guest->x;
|
||||||
|
y = guest->y;
|
||||||
|
z = guest->z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No walking guests; pick random park entrance
|
||||||
|
if (gParkEntrances.size() > 0)
|
||||||
|
{
|
||||||
|
auto rand = scenario_rand_max((uint32_t)gParkEntrances.size());
|
||||||
|
const auto& entrance = gParkEntrances[rand];
|
||||||
|
auto dir = entrance.direction;
|
||||||
|
x = entrance.x;
|
||||||
|
y = entrance.y;
|
||||||
|
z = entrance.z;
|
||||||
|
x += 16 + ((dir & 1) == 0 ? ((dir & 2) ? 32 : -32) : 0);
|
||||||
|
y += 16 + ((dir & 1) == 1 ? ((dir & 2) ? -32 : 32) : 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// User must pick a location
|
||||||
|
newPeep->state = PEEP_STATE_PICKED;
|
||||||
|
x = newPeep->x;
|
||||||
|
y = newPeep->y;
|
||||||
|
z = newPeep->z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sprite_move(x, y, z + 16, (rct_sprite*)newPeep);
|
||||||
|
invalidate_sprite_2((rct_sprite*)newPeep);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "../Context.h"
|
#include "../Context.h"
|
||||||
#include "../Game.h"
|
#include "../Game.h"
|
||||||
#include "../Input.h"
|
#include "../Input.h"
|
||||||
|
#include "../actions/StaffHireNewAction.hpp"
|
||||||
#include "../actions/StaffSetOrdersAction.hpp"
|
#include "../actions/StaffSetOrdersAction.hpp"
|
||||||
#include "../audio/audio.h"
|
#include "../audio/audio.h"
|
||||||
#include "../config/Config.h"
|
#include "../config/Config.h"
|
||||||
@@ -81,286 +82,6 @@ void staff_reset_modes()
|
|||||||
staff_update_greyed_patrol_areas();
|
staff_update_greyed_patrol_areas();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void staff_autoposition_new_staff_member(Peep* newPeep)
|
|
||||||
{
|
|
||||||
// Find a location to place new staff member
|
|
||||||
|
|
||||||
newPeep->state = PEEP_STATE_FALLING;
|
|
||||||
|
|
||||||
int16_t x, y, z;
|
|
||||||
uint32_t count = 0;
|
|
||||||
uint16_t sprite_index;
|
|
||||||
Peep* guest = nullptr;
|
|
||||||
TileElement* guest_tile = nullptr;
|
|
||||||
|
|
||||||
// Count number of walking guests
|
|
||||||
FOR_ALL_GUESTS (sprite_index, guest)
|
|
||||||
{
|
|
||||||
if (guest->state == PEEP_STATE_WALKING)
|
|
||||||
{
|
|
||||||
// Check the walking guest's tile. Only count them if they're on a path tile.
|
|
||||||
guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z);
|
|
||||||
if (guest_tile != nullptr)
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
// Place staff at a random guest
|
|
||||||
uint32_t rand = scenario_rand_max(count);
|
|
||||||
FOR_ALL_GUESTS (sprite_index, guest)
|
|
||||||
{
|
|
||||||
if (guest->state == PEEP_STATE_WALKING)
|
|
||||||
{
|
|
||||||
guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z);
|
|
||||||
if (guest_tile != nullptr)
|
|
||||||
{
|
|
||||||
if (rand == 0)
|
|
||||||
break;
|
|
||||||
--rand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x = guest->x;
|
|
||||||
y = guest->y;
|
|
||||||
z = guest->z;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No walking guests; pick random park entrance
|
|
||||||
if (gParkEntrances.size() > 0)
|
|
||||||
{
|
|
||||||
auto rand = scenario_rand_max((uint32_t)gParkEntrances.size());
|
|
||||||
const auto& entrance = gParkEntrances[rand];
|
|
||||||
auto dir = entrance.direction;
|
|
||||||
x = entrance.x;
|
|
||||||
y = entrance.y;
|
|
||||||
z = entrance.z;
|
|
||||||
x += 16 + ((dir & 1) == 0 ? ((dir & 2) ? 32 : -32) : 0);
|
|
||||||
y += 16 + ((dir & 1) == 1 ? ((dir & 2) ? -32 : 32) : 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// User must pick a location
|
|
||||||
newPeep->state = PEEP_STATE_PICKED;
|
|
||||||
x = newPeep->x;
|
|
||||||
y = newPeep->y;
|
|
||||||
z = newPeep->z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite_move(x, y, z + 16, (rct_sprite*)newPeep);
|
|
||||||
invalidate_sprite_2((rct_sprite*)newPeep);
|
|
||||||
}
|
|
||||||
|
|
||||||
static money32 staff_hire_new_staff_member(
|
|
||||||
uint8_t staff_type, uint8_t flags, int16_t command_x, int16_t command_y, int16_t command_z, int32_t autoposition,
|
|
||||||
int32_t* newPeep_sprite_index)
|
|
||||||
{
|
|
||||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_WAGES;
|
|
||||||
gCommandPosition.x = command_x;
|
|
||||||
gCommandPosition.y = command_y;
|
|
||||||
gCommandPosition.z = command_z;
|
|
||||||
|
|
||||||
if (gSpriteListCount[SPRITE_LIST_NULL] < 400)
|
|
||||||
{
|
|
||||||
gGameCommandErrorText = STR_TOO_MANY_PEOPLE_IN_GAME;
|
|
||||||
return MONEY32_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Staff type matches STAFF_TYPE enum, but ENTERTAINER onwards will match
|
|
||||||
// the ENTERTAINER_COSTUME enum
|
|
||||||
uint8_t entertainerType = ENTERTAINER_COSTUME_PANDA;
|
|
||||||
if (staff_type >= STAFF_TYPE_ENTERTAINER)
|
|
||||||
{
|
|
||||||
entertainerType = staff_type - STAFF_TYPE_ENTERTAINER;
|
|
||||||
if (entertainerType >= ENTERTAINER_COSTUME_COUNT)
|
|
||||||
{
|
|
||||||
// Invalid entertainer costume
|
|
||||||
return MONEY32_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t availableCostumes = staff_get_available_entertainer_costumes();
|
|
||||||
if (!(availableCostumes & (1 << entertainerType)))
|
|
||||||
{
|
|
||||||
// Entertainer costume unavailable
|
|
||||||
return MONEY32_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
staff_type = STAFF_TYPE_ENTERTAINER;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t i;
|
|
||||||
for (i = 0; i < STAFF_MAX_COUNT; ++i)
|
|
||||||
{
|
|
||||||
if (!(gStaffModes[i] & 1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == STAFF_MAX_COUNT)
|
|
||||||
{
|
|
||||||
gGameCommandErrorText = STR_TOO_MANY_STAFF_IN_GAME;
|
|
||||||
return MONEY32_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
|
||||||
{
|
|
||||||
int32_t newStaffId = i;
|
|
||||||
const rct_sprite_bounds* spriteBounds;
|
|
||||||
Peep* newPeep = &(create_sprite(flags)->peep);
|
|
||||||
|
|
||||||
if (newPeep == nullptr)
|
|
||||||
{
|
|
||||||
gGameCommandErrorText = STR_TOO_MANY_PEOPLE_IN_GAME;
|
|
||||||
return MONEY32_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags == 0)
|
|
||||||
{
|
|
||||||
sprite_remove((rct_sprite*)newPeep);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
move_sprite_to_list((rct_sprite*)newPeep, SPRITE_LIST_PEEP * 2);
|
|
||||||
|
|
||||||
newPeep->sprite_identifier = 1;
|
|
||||||
newPeep->window_invalidate_flags = 0;
|
|
||||||
newPeep->action = PEEP_ACTION_NONE_2;
|
|
||||||
newPeep->special_sprite = 0;
|
|
||||||
newPeep->action_sprite_image_offset = 0;
|
|
||||||
newPeep->no_action_frame_num = 0;
|
|
||||||
newPeep->action_sprite_type = PEEP_ACTION_SPRITE_TYPE_NONE;
|
|
||||||
newPeep->path_check_optimisation = 0;
|
|
||||||
newPeep->type = PEEP_TYPE_STAFF;
|
|
||||||
newPeep->outside_of_park = 0;
|
|
||||||
newPeep->peep_flags = 0;
|
|
||||||
newPeep->paid_to_enter = 0;
|
|
||||||
newPeep->paid_on_rides = 0;
|
|
||||||
newPeep->paid_on_food = 0;
|
|
||||||
newPeep->paid_on_souvenirs = 0;
|
|
||||||
|
|
||||||
if (staff_type == STAFF_TYPE_HANDYMAN)
|
|
||||||
newPeep->staff_orders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS;
|
|
||||||
else if (staff_type == STAFF_TYPE_MECHANIC)
|
|
||||||
newPeep->staff_orders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES;
|
|
||||||
else
|
|
||||||
newPeep->staff_orders = 0;
|
|
||||||
|
|
||||||
uint16_t idSearchSpriteIndex;
|
|
||||||
Peep* idSearchPeep;
|
|
||||||
|
|
||||||
// We search for the first available id for a given staff type
|
|
||||||
uint32_t newStaffIndex = 0;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
++newStaffIndex;
|
|
||||||
|
|
||||||
FOR_ALL_STAFF (idSearchSpriteIndex, idSearchPeep)
|
|
||||||
{
|
|
||||||
if (idSearchPeep->staff_type != staff_type)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (idSearchPeep->id == newStaffIndex)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
newPeep->id = newStaffIndex;
|
|
||||||
newPeep->staff_type = staff_type;
|
|
||||||
|
|
||||||
static constexpr const rct_string_id staffNames[] = {
|
|
||||||
STR_HANDYMAN_X,
|
|
||||||
STR_MECHANIC_X,
|
|
||||||
STR_SECURITY_GUARD_X,
|
|
||||||
STR_ENTERTAINER_X,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* rct2: 0x009929FC */
|
|
||||||
static constexpr const PeepSpriteType spriteTypes[] = {
|
|
||||||
PEEP_SPRITE_TYPE_HANDYMAN,
|
|
||||||
PEEP_SPRITE_TYPE_MECHANIC,
|
|
||||||
PEEP_SPRITE_TYPE_SECURITY,
|
|
||||||
PEEP_SPRITE_TYPE_ENTERTAINER_PANDA,
|
|
||||||
};
|
|
||||||
|
|
||||||
PeepSpriteType sprite_type = spriteTypes[staff_type];
|
|
||||||
if (staff_type == STAFF_TYPE_ENTERTAINER)
|
|
||||||
{
|
|
||||||
sprite_type = static_cast<PeepSpriteType>(PEEP_SPRITE_TYPE_ENTERTAINER_PANDA + entertainerType);
|
|
||||||
}
|
|
||||||
newPeep->name_string_idx = staffNames[staff_type];
|
|
||||||
newPeep->sprite_type = sprite_type;
|
|
||||||
|
|
||||||
spriteBounds = g_peep_animation_entries[sprite_type].sprite_bounds;
|
|
||||||
newPeep->sprite_width = spriteBounds->sprite_width;
|
|
||||||
newPeep->sprite_height_negative = spriteBounds->sprite_height_negative;
|
|
||||||
newPeep->sprite_height_positive = spriteBounds->sprite_height_positive;
|
|
||||||
|
|
||||||
if (autoposition)
|
|
||||||
{
|
|
||||||
staff_autoposition_new_staff_member(newPeep);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newPeep->state = PEEP_STATE_PICKED;
|
|
||||||
|
|
||||||
sprite_move(newPeep->x, newPeep->y, newPeep->z, (rct_sprite*)newPeep);
|
|
||||||
invalidate_sprite_2((rct_sprite*)newPeep);
|
|
||||||
}
|
|
||||||
|
|
||||||
newPeep->time_in_park = gDateMonthsElapsed;
|
|
||||||
newPeep->pathfind_goal.x = 0xFF;
|
|
||||||
newPeep->pathfind_goal.y = 0xFF;
|
|
||||||
newPeep->pathfind_goal.z = 0xFF;
|
|
||||||
newPeep->pathfind_goal.direction = 0xFF;
|
|
||||||
|
|
||||||
uint8_t colour = staff_get_colour(staff_type);
|
|
||||||
newPeep->tshirt_colour = colour;
|
|
||||||
newPeep->trousers_colour = colour;
|
|
||||||
|
|
||||||
// Staff energy determines their walking speed
|
|
||||||
newPeep->energy = 0x60;
|
|
||||||
newPeep->energy_target = 0x60;
|
|
||||||
newPeep->staff_mowing_timeout = 0;
|
|
||||||
|
|
||||||
peep_update_name_sort(newPeep);
|
|
||||||
|
|
||||||
newPeep->staff_id = newStaffId;
|
|
||||||
|
|
||||||
gStaffModes[newStaffId] = STAFF_MODE_WALK;
|
|
||||||
|
|
||||||
for (i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
|
|
||||||
{
|
|
||||||
gStaffPatrolAreas[newStaffId * STAFF_PATROL_AREA_SIZE + i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*newPeep_sprite_index = newPeep->sprite_index;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* rct2: 0x006BEFA1
|
|
||||||
*/
|
|
||||||
void game_command_hire_new_staff_member(
|
|
||||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi,
|
|
||||||
[[maybe_unused]] int32_t* ebp)
|
|
||||||
{
|
|
||||||
*ebx = staff_hire_new_staff_member(
|
|
||||||
(*ebx & 0xFF00) >> 8, *ebx & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFFFF, (*ebx & 0xFF0000) >> 16, edi);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* rct2: 0x006C09D1
|
* rct2: 0x006C09D1
|
||||||
@@ -449,42 +170,45 @@ void game_command_fire_staff_member(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hires a new staff member of the given type. If the hire cannot be completed (eg. the maximum number of staff is reached or
|
* Hires a new staff member of the given type.
|
||||||
* there are too many peeps) it returns SPRITE_INDEX_NULL.
|
|
||||||
*/
|
*/
|
||||||
uint16_t hire_new_staff_member(uint8_t staffType)
|
bool staff_hire_new_member(STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType)
|
||||||
{
|
{
|
||||||
gGameCommandErrorTitle = STR_CANT_HIRE_NEW_STAFF;
|
bool autoPosition = gConfigGeneral.auto_staff_placement;
|
||||||
|
|
||||||
int32_t command_x, ebx, command_y, command_z, esi, new_sprite_index, ebp;
|
|
||||||
command_y = command_z = esi = new_sprite_index = ebp = 0;
|
|
||||||
command_x = 0x8000;
|
|
||||||
|
|
||||||
int32_t autoposition = gConfigGeneral.auto_staff_placement;
|
|
||||||
if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z)
|
if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z)
|
||||||
{
|
{
|
||||||
autoposition = autoposition ^ 1;
|
autoPosition = autoPosition ^ 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ebx = autoposition << 16 | staffType << 8 | GAME_COMMAND_FLAG_APPLY;
|
uint32_t staffOrders = 0;
|
||||||
|
|
||||||
game_command_callback = game_command_callback_hire_new_staff_member;
|
if (staffType == STAFF_TYPE_HANDYMAN)
|
||||||
int32_t result = game_do_command_p(
|
|
||||||
GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, &command_x, &ebx, &command_y, &command_z, &esi, &new_sprite_index, &ebp);
|
|
||||||
|
|
||||||
if (result == MONEY32_UNDEFINED)
|
|
||||||
return SPRITE_INDEX_NULL;
|
|
||||||
|
|
||||||
if ((staffType == STAFF_TYPE_HANDYMAN) && gConfigGeneral.handymen_mow_default)
|
|
||||||
{
|
{
|
||||||
Peep* newPeep = GET_PEEP(new_sprite_index);
|
staffOrders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS;
|
||||||
uint8_t newOrders = newPeep->staff_orders | STAFF_ORDERS_MOWING;
|
if (gConfigGeneral.handymen_mow_default)
|
||||||
|
{
|
||||||
auto staffSetOrdersAction = StaffSetOrdersAction(new_sprite_index, newOrders);
|
staffOrders |= STAFF_ORDERS_MOWING;
|
||||||
GameActions::Execute(&staffSetOrdersAction);
|
}
|
||||||
|
}
|
||||||
|
else if (staffType == STAFF_TYPE_MECHANIC)
|
||||||
|
{
|
||||||
|
staffOrders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_sprite_index;
|
auto hireStaffAction = StaffHireNewAction(autoPosition, staffType, entertainerType, staffOrders);
|
||||||
|
hireStaffAction.SetCallback([=](const GameAction*, const StaffHireNewActionResult* res) -> void {
|
||||||
|
if (res->Error != GA_ERROR::OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Open window for new staff.
|
||||||
|
Peep* peep = &get_sprite(res->peepSriteIndex)->peep;
|
||||||
|
auto intent = Intent(WC_PEEP);
|
||||||
|
intent.putExtra(INTENT_EXTRA_PEEP, peep);
|
||||||
|
context_open_intent(&intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto res = GameActions::Execute(&hireStaffAction);
|
||||||
|
return res->Error == GA_ERROR::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ enum STAFF_MODE
|
|||||||
STAFF_MODE_PATROL = 3
|
STAFF_MODE_PATROL = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum STAFF_TYPE
|
enum STAFF_TYPE : uint8_t
|
||||||
{
|
{
|
||||||
STAFF_TYPE_HANDYMAN,
|
STAFF_TYPE_HANDYMAN,
|
||||||
STAFF_TYPE_MECHANIC,
|
STAFF_TYPE_MECHANIC,
|
||||||
@@ -45,7 +45,7 @@ enum STAFF_ORDERS
|
|||||||
STAFF_ORDERS_FIX_RIDES = (1 << 1)
|
STAFF_ORDERS_FIX_RIDES = (1 << 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ENTERTAINER_COSTUME
|
enum ENTERTAINER_COSTUME : uint8_t
|
||||||
{
|
{
|
||||||
ENTERTAINER_COSTUME_PANDA,
|
ENTERTAINER_COSTUME_PANDA,
|
||||||
ENTERTAINER_COSTUME_TIGER,
|
ENTERTAINER_COSTUME_TIGER,
|
||||||
@@ -86,7 +86,7 @@ void game_command_pickup_staff(
|
|||||||
|
|
||||||
void staff_reset_modes();
|
void staff_reset_modes();
|
||||||
void staff_set_name(uint16_t spriteIndex, const char* name);
|
void staff_set_name(uint16_t spriteIndex, const char* name);
|
||||||
uint16_t hire_new_staff_member(uint8_t staffType);
|
bool staff_hire_new_member(STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType);
|
||||||
void staff_update_greyed_patrol_areas();
|
void staff_update_greyed_patrol_areas();
|
||||||
bool staff_is_location_in_patrol(Peep* mechanic, int32_t x, int32_t y);
|
bool staff_is_location_in_patrol(Peep* mechanic, int32_t x, int32_t y);
|
||||||
bool staff_is_location_on_patrol_edge(Peep* mechanic, int32_t x, int32_t y);
|
bool staff_is_location_on_patrol_edge(Peep* mechanic, int32_t x, int32_t y);
|
||||||
|
|||||||
@@ -55,25 +55,6 @@ void game_command_callback_pickup_guest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_command_callback_hire_new_staff_member(
|
|
||||||
[[maybe_unused]] int32_t eax, [[maybe_unused]] int32_t ebx, [[maybe_unused]] int32_t ecx, [[maybe_unused]] int32_t edx,
|
|
||||||
[[maybe_unused]] int32_t esi, int32_t edi, [[maybe_unused]] int32_t ebp)
|
|
||||||
{
|
|
||||||
int32_t sprite_index = edi;
|
|
||||||
if (sprite_index == SPRITE_INDEX_NULL)
|
|
||||||
{
|
|
||||||
rct_window* window = window_find_by_class(WC_STAFF_LIST);
|
|
||||||
window_invalidate(window);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Peep* peep = &get_sprite(sprite_index)->peep;
|
|
||||||
auto intent = Intent(WC_PEEP);
|
|
||||||
intent.putExtra(INTENT_EXTRA_PEEP, peep);
|
|
||||||
context_open_intent(&intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_command_callback_pickup_staff(
|
void game_command_callback_pickup_staff(
|
||||||
int32_t eax, int32_t ebx, int32_t ecx, [[maybe_unused]] int32_t edx, [[maybe_unused]] int32_t esi,
|
int32_t eax, int32_t ebx, int32_t ecx, [[maybe_unused]] int32_t edx, [[maybe_unused]] int32_t esi,
|
||||||
[[maybe_unused]] int32_t edi, [[maybe_unused]] int32_t ebp)
|
[[maybe_unused]] int32_t edi, [[maybe_unused]] int32_t ebp)
|
||||||
|
|||||||
Reference in New Issue
Block a user