1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-28 17:24:47 +01:00
Files
OpenRCT2/src/peep/peep.c

12216 lines
337 KiB
C

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "../audio/audio.h"
#include "../audio/mixer.h"
#include "../cheats.h"
#include "../config.h"
#include "../game.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../management/finance.h"
#include "../management/marketing.h"
#include "../management/news_item.h"
#include "../openrct2.h"
#include "../ride/ride.h"
#include "../ride/ride_data.h"
#include "../ride/track.h"
#include "../scenario.h"
#include "../sprites.h"
#include "../util/util.h"
#include "../world/climate.h"
#include "../world/footpath.h"
#include "../world/map.h"
#include "../world/scenery.h"
#include "../world/sprite.h"
#include "peep.h"
#include "staff.h"
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
bool gPathFindDebug = false;
utf8 gPathFindDebugPeepName[256];
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
uint8 gGuestChangeModifier;
uint16 gNumGuestsInPark;
uint16 gNumGuestsInParkLastWeek;
uint16 gNumGuestsHeadingForPark;
money16 gGuestInitialCash;
uint8 gGuestInitialHappiness;
uint8 gGuestInitialHunger;
uint8 gGuestInitialThirst;
uint32 gNextGuestNumber;
uint8 gPeepWarningThrottle[16];
rct_xyz16 gPeepPathFindGoalPosition;
bool gPeepPathFindIgnoreForeignQueues;
uint8 gPeepPathFindQueueRideIndex;
bool gPeepPathFindSingleChoiceSection;
// uint32 gPeepPathFindAltStationNum;
static bool _peepPathFindIsStaff;
static sint8 _peepPathFindNumJunctions;
static sint8 _peepPathFindMaxJunctions;
static sint32 _peepPathFindTilesChecked;
static uint8 _peepPathFindFewestNumSteps;
/* A junction history for the peep pathfinding heuristic search
* The magic number 16 is the largest value returned by
* peep_pathfind_get_max_number_junctions() which should eventually
* be declared properly. */
static struct {
rct_xyz8 location;
uint8 direction;
} _peepPathFindHistory[16];
static uint8 _unk_F1AEF0;
static uint8 _unk_F1AEF1;
static uint16 _unk_F1EE18;
static rct_map_element * _peepRideEntranceExitElement;
static uint32 _peepRideConsideration[8];
static uint8 _peepPotentialRides[256];
enum {
PATH_SEARCH_DEAD_END,
PATH_SEARCH_WIDE,
PATH_SEARCH_THIN,
PATH_SEARCH_JUNCTION,
PATH_SEARCH_RIDE_QUEUE,
PATH_SEARCH_RIDE_ENTRANCE,
PATH_SEARCH_RIDE_EXIT,
PATH_SEARCH_PARK_EXIT,
PATH_SEARCH_SHOP_ENTRANCE,
PATH_SEARCH_LIMIT_REACHED,
PATH_SEARCH_LOOP,
PATH_SEARCH_OTHER,
PATH_SEARCH_FAILED
};
enum {
F1EE18_DESTINATION_REACHED = 1 << 0,
F1EE18_OUTSIDE_PARK = 1 << 1,
F1EE18_RIDE_EXIT = 1 << 2,
F1EE18_RIDE_ENTRANCE = 1 << 3,
};
static void sub_68F41A(rct_peep *peep, int index);
static void peep_update(rct_peep *peep);
static int peep_has_empty_container(rct_peep* peep);
static int peep_has_drink(rct_peep* peep);
static int peep_has_food_standard_flag(rct_peep* peep);
static int peep_has_food_extra_flag(rct_peep* peep);
static int peep_empty_container_standard_flag(rct_peep* peep);
static int peep_empty_container_extra_flag(rct_peep* peep);
static int peep_should_find_bench(rct_peep* peep);
static void peep_stop_purchase_thought(rct_peep* peep, uint8 ride_type);
static void sub_693BAB(rct_peep* peep);
static int sub_693C9E(rct_peep *peep);
static void peep_spend_money(rct_peep *peep, money16 *peep_expend_type, money32 amount);
static void peep_set_has_ridden(rct_peep *peep, int rideIndex);
static bool peep_has_ridden(rct_peep *peep, int rideIndex);
static void peep_set_has_ridden_ride_type(rct_peep *peep, int rideType);
static bool peep_has_ridden_ride_type(rct_peep *peep, int rideType);
static void peep_on_enter_or_exit_ride(rct_peep *peep, int rideIndex, int flags);
static void peep_update_favourite_ride(rct_peep *peep, rct_ride *ride);
static sint16 peep_calculate_ride_satisfaction(rct_peep *peep, rct_ride *ride);
static void peep_update_ride_nausea_growth(rct_peep *peep, rct_ride *ride);
static bool sub_69AF1E(rct_peep *peep, int rideIndex, int shopItem, money32 price);
static bool peep_should_use_cash_machine(rct_peep *peep, int rideIndex);
static bool peep_should_go_on_ride(rct_peep *peep, int rideIndex, int entranceNum, int flags);
static void peep_ride_is_too_intense(rct_peep *peep, int rideIndex, bool peepAtRide);
static void peep_chose_not_to_go_on_ride(rct_peep *peep, int rideIndex, bool peepAtRide, bool updateLastRide);
static void peep_tried_to_enter_full_queue(rct_peep *peep, int rideIndex);
static bool peep_should_go_to_shop(rct_peep *peep, int rideIndex, bool peepAtShop);
static void peep_reset_pathfind_goal(rct_peep *peep);
static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToView, uint8 *rideSeatToView);
static void peep_easter_egg_peep_interactions(rct_peep *peep);
static int peep_get_height_on_slope(rct_peep *peep, int x, int y);
static void peep_pick_ride_to_go_on(rct_peep *peep);
static void peep_head_for_nearest_ride_type(rct_peep *peep, int rideType);
static void peep_head_for_nearest_ride_with_flags(rct_peep *peep, int rideTypeFlags);
static void peep_give_real_name(rct_peep *peep);
static int guest_surface_path_finding(rct_peep* peep);
static void peep_read_map(rct_peep *peep);
static bool peep_heading_for_ride_or_park_exit(rct_peep *peep);
static bool peep_update_fixing_sub_state_0(rct_ride *ride);
static bool peep_update_fixing_sub_state_1(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_2345(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_6(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_7(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_8(bool firstRun, rct_peep *peep);
static bool peep_update_fixing_sub_state_9(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_10(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_11(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_12(bool firstRun, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_13(bool firstRun, int steps, rct_peep *peep, rct_ride *ride);
static bool peep_update_fixing_sub_state_14(bool firstRun, rct_peep *peep, rct_ride *ride);
static void sub_6B7588(int rideIndex);
bool loc_690FD0(rct_peep *peep, uint8 *rideToView, uint8 *rideSeatToView, rct_map_element *esi);
const char *gPeepEasterEggNames[] = {
"MICHAEL SCHUMACHER",
"JACQUES VILLENEUVE",
"DAMON HILL",
"MR BEAN",
"CHRIS SAWYER",
"KATIE BRAYSHAW",
"MELANIE WARN",
"SIMON FOSTER",
"JOHN WARDLEY",
"LISA STIRLING",
"DONALD MACRAE",
"KATHERINE MCGOWAN",
"FRANCES MCGOWAN",
"CORINA MASSOURA",
"CAROL YOUNG",
"MIA SHERIDAN",
"KATIE RODGER",
"EMMA GARRELL",
"JOANNE BARTON",
"FELICITY ANDERSON",
"KATIE SMITH",
"EILIDH BELL",
"NANCY STILLWAGON",
"DAVID ELLIS"
};
/** rct2: 0x00981DB0 */
static struct {
uint8 action;
uint8 flags;
} PeepThoughtToActionMap[] = {
{ PEEP_ACTION_SHAKE_HEAD, 1 },
{ PEEP_ACTION_EMPTY_POCKETS, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_WOW, 1 },
{ PEEP_ACTION_NONE_2, 2 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 4 },
{ PEEP_ACTION_SHAKE_HEAD, 4 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_WAVE, 0 },
{ PEEP_ACTION_JOY, 1 },
{ PEEP_ACTION_CHECK_TIME, 1 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_WAVE, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_WAVE, 0 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_27, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_29, 0 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 1 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_SHAKE_HEAD, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_NONE_2, 0 },
{ PEEP_ACTION_JOY, 0 },
{ PEEP_ACTION_NONE_2, 1 },
};
static uint8 unk_981D8C[] = { 0, 6, 12 };
static uint8 unk_981D8F[] = {
1, 3, 4, 5, 8, 9, 10, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 0, 0
};
static const bool SpriteTypeToSlowWalkMap[] = {
false, false, false, false, false, false, false, false,
false, false, false, true, false, false, true, true,
true, true, true, false, true, false, true, true,
true, false, false, true, true, false, false, true,
true, true, true, true, true, true, false, true,
false, true, true, true, true, true, true, true,
};
// These arrays contain the base minimum and maximum nausea ratings for peeps, based on their nausea tolerance level.
static const ride_rating NauseaMinimumThresholds[] = {
0, 0, 200, 400
};
static const ride_rating NauseaMaximumThresholds[] = {
300, 600, 800, 1000
};
// Has to use signed types
static const rct_xy16 _97e1bc_21[64] = {
{ 56, 8 },
{ 8, 8 },
{ 8, 32 },
{ 32, 32 },
{ 8, 8 },
{ 8, 8 },
{ 8, 32 },
{ 32, 32 },
{ 8, 32 },
{ 8, 32 },
{ 8, 32 },
{ 32, 32 },
{ 8, 56 },
{ 8, 32 },
{ 8, 32 },
{ 32, 32 },
{ 56, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 56, -24 },
{ 56, 24 },
{ 32, 24 },
{ 32, 0 },
{ 8, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 32, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 24, 0 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ 24, -24 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ -24, -24 },
{ 24, -24 },
{ 24, 0 },
{ 0, 0 },
{ 24, 24 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ 24, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ 0, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ -24, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ -24, 56 },
{ -24, 8 },
{ 0, 8 },
{ 0, 32 },
};
static const rct_xy16 * _97e1bc[91] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
_97e1bc_21, // RIDE_TYPE_SPIRAL_SLIDE
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
int peep_get_staff_count()
{
uint16 spriteIndex;
rct_peep *peep;
int count = 0;
FOR_ALL_STAFF(spriteIndex, peep)
count++;
return count;
}
/**
*
* rct2: 0x0068F0A9
*/
void peep_update_all()
{
int i;
uint16 spriteIndex;
rct_peep* peep;
if (gScreenFlags & 0x0E)
return;
spriteIndex = gSpriteListHead[SPRITE_LIST_PEEP];
i = 0;
while (spriteIndex != SPRITE_INDEX_NULL) {
peep = &(get_sprite(spriteIndex)->peep);
spriteIndex = peep->next;
if ((i & 0x7F) != (gCurrentTicks & 0x7F)) {
peep_update(peep);
} else {
sub_68F41A(peep, i);
if (peep->linked_list_type_offset == SPRITE_LIST_PEEP * 2) {
peep_update(peep);
}
}
i++;
}
}
/**
*
* rct2: 0x0069BC9A
*/
static uint8 peep_assess_surroundings(sint16 center_x, sint16 center_y, sint16 center_z){
if ((map_element_height(center_x, center_y) & 0xFFFF) > center_z)
return PEEP_THOUGHT_TYPE_NONE;
uint16 num_scenery = 0;
uint16 num_fountains = 0;
uint16 nearby_music = 0;
uint16 num_rubbish = 0;
sint16 initial_x = max(center_x - 160, 0);
sint16 initial_y = max(center_y - 160, 0);
sint16 final_x = min(center_x + 160, 8192);
sint16 final_y = min(center_y + 160, 8192);
for (sint16 x = initial_x; x < final_x; x += 32){
for (sint16 y = initial_y; y < final_y; y += 32){
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
do{
rct_ride* ride;
rct_scenery_entry* scenery;
switch (map_element_get_type(mapElement)){
case MAP_ELEMENT_TYPE_PATH:
if (!footpath_element_has_path_scenery(mapElement))
break;
scenery = get_footpath_item_entry(footpath_element_get_path_scenery_index(mapElement));
if (scenery == NULL) {
return PEEP_THOUGHT_TYPE_NONE;
}
if (footpath_element_path_scenery_is_ghost(mapElement))
break;
if (scenery->path_bit.flags &
(PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER |
PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW)){
num_fountains++;
break;
}
if (mapElement->flags & MAP_ELEMENT_FLAG_BROKEN){
num_rubbish++;
}
break;
case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE:
case MAP_ELEMENT_TYPE_SCENERY:
num_scenery++;
break;
case MAP_ELEMENT_TYPE_TRACK:
ride = get_ride(mapElement->properties.track.ride_index);
if (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC &&
ride->status != RIDE_STATUS_CLOSED &&
!(ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))){
if (ride->type == RIDE_TYPE_MERRY_GO_ROUND){
nearby_music |= 1;
break;
}
if (ride->music == MUSIC_STYLE_ORGAN){
nearby_music |= 1;
break;
}
if (ride->type == RIDE_TYPE_DODGEMS){
// Dodgems drown out music?
nearby_music |= 2;
}
}
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
}
}
rct_litter* litter;
for (uint16 sprite_idx = gSpriteListHead[SPRITE_LIST_LITTER]; sprite_idx != SPRITE_INDEX_NULL; sprite_idx = litter->next) {
litter = &(get_sprite(sprite_idx)->litter);
sint16 dist_x = abs(litter->x - center_x);
sint16 dist_y = abs(litter->y - center_y);
if (max(dist_x, dist_y) <= 160) {
num_rubbish++;
}
}
if (num_fountains >= 5 && num_rubbish < 20)
return PEEP_THOUGHT_TYPE_FOUNTAINS;
if (num_scenery >= 40 && num_rubbish < 8)
return PEEP_THOUGHT_TYPE_SCENERY;
if (nearby_music == 1 && num_rubbish < 20)
return PEEP_THOUGHT_TYPE_MUSIC;
if (num_rubbish < 2 && !gCheatsDisableLittering)
// if disable littering cheat is enabled, peeps will not have the "clean and tidy park" thought
return PEEP_THOUGHT_TYPE_VERY_CLEAN;
return PEEP_THOUGHT_TYPE_NONE;
}
/**
*
* rct2: 0x0068F9A9
*/
static void peep_update_hunger(rct_peep *peep){
if (peep->hunger >= 3){
peep->hunger -= 2;
peep->energy_growth_rate = min(peep->energy_growth_rate + 2, 255);
peep->bathroom = min(peep->bathroom + 1, 255);
}
}
/**
*
* rct2: 0x0068F93E
*/
static void peep_leave_park(rct_peep* peep){
peep->guest_heading_to_ride_id = 0xFF;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK){
if (peep->peep_is_lost_countdown < 60){
return;
}
}
else{
peep->peep_is_lost_countdown = 254;
peep->peep_flags |= PEEP_FLAGS_LEAVING_PARK;
peep->peep_flags &= ~PEEP_FLAGS_PARK_ENTRANCE_CHOSEN;
}
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_GO_HOME, 0xFF);
rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) window_event_invalidate_call(w);
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
}
/**
*
* rct2: 0x0068F8CD
*/
static void sub_68F8CD(rct_peep *peep)
{
if (peep->energy_growth_rate >= 33) {
peep->energy_growth_rate -= 2;
}
if (gClimateCurrentTemperature >= 21 && peep->thirst >= 5) {
peep->thirst--;
}
if (peep->outside_of_park != 0) {
return;
}
if (!(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)){
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
if (peep->energy >= 70 && peep->happiness >= 60) {
return;
}
} else {
if (
peep->energy >= 55 &&
peep->happiness >= 45 &&
peep->cash_in_pocket >= MONEY(5, 00)
) {
return;
}
}
}
if ((scenario_rand() & 0xFFFF) > 3276) {
return;
}
peep_leave_park(peep);
}
/** rct2: 0x009822F4, 0x00982310 */
static const uint8 byte_9822F4[] = {
0, // SHOP_ITEM_BALLOON
0, // SHOP_ITEM_TOY
0, // SHOP_ITEM_MAP
0, // SHOP_ITEM_PHOTO
0, // SHOP_ITEM_UMBRELLA
100, // SHOP_ITEM_DRINK
150, // SHOP_ITEM_BURGER
120, // SHOP_ITEM_FRIES
60, // SHOP_ITEM_ICE_CREAM
50, // SHOP_ITEM_COTTON_CANDY
0, // SHOP_ITEM_EMPTY_CAN
0, // SHOP_ITEM_RUBBISH
0, // SHOP_ITEM_EMPTY_BURGER_BOX
150, // SHOP_ITEM_PIZZA
0, // SHOP_ITEM_VOUCHER
75, // SHOP_ITEM_POPCORN
133, // SHOP_ITEM_HOT_DOG
110, // SHOP_ITEM_TENTACLE
0, // SHOP_ITEM_HAT
50, // SHOP_ITEM_CANDY_APPLE
0, // SHOP_ITEM_TSHIRT
80, // SHOP_ITEM_DONUT
90, // SHOP_ITEM_COFFEE
0, // SHOP_ITEM_EMPTY_CUP
170, // SHOP_ITEM_CHICKEN
115, // SHOP_ITEM_LEMONADE
0, // SHOP_ITEM_EMPTY_BOX
0, // SHOP_ITEM_EMPTY_BOTTLE
0xFF,
0xFF,
0xFF,
0xFF,
0, // SHOP_ITEM_PHOTO2
0, // SHOP_ITEM_PHOTO3
0, // SHOP_ITEM_PHOTO4
70, // SHOP_ITEM_PRETZEL
85, // SHOP_ITEM_CHOCOLATE
95, // SHOP_ITEM_ICED_TEA
90, // SHOP_ITEM_FUNNEL_CAKE
0, // SHOP_ITEM_SUNGLASSES
130, // SHOP_ITEM_BEEF_NOODLES
120, // SHOP_ITEM_FRIED_RICE_NOODLES
100, // SHOP_ITEM_WONTON_SOUP
110, // SHOP_ITEM_MEATBALL_SOUP
110, // SHOP_ITEM_FRUIT_JUICE
90, // SHOP_ITEM_SOYBEAN_MILK
100, // SHOP_ITEM_SU_JONGKWA
130, // SHOP_ITEM_SUB_SANDWICH
75, // SHOP_ITEM_COOKIE
0, // SHOP_ITEM_EMPTY_BOWL_RED
0, // SHOP_ITEM_EMPTY_DRINK_CARTON
0, // SHOP_ITEM_EMPTY_JUICE_CUP
115, // SHOP_ITEM_ROAST_SAUSAGE
0 // SHOP_ITEM_EMPTY_BOWL_BLUE
};
/** rct2: 009823AC */
static const uint8 crowded_thoughts[] = {
PEEP_THOUGHT_TYPE_LOST,
PEEP_THOUGHT_TYPE_TIRED,
PEEP_THOUGHT_TYPE_BAD_LITTER,
PEEP_THOUGHT_TYPE_HUNGRY,
PEEP_THOUGHT_TYPE_THIRSTY,
PEEP_THOUGHT_TYPE_VERY_CLEAN,
PEEP_THOUGHT_TYPE_CROWDED,
PEEP_THOUGHT_TYPE_SCENERY,
PEEP_THOUGHT_TYPE_VERY_CLEAN,
PEEP_THOUGHT_TYPE_MUSIC,
PEEP_THOUGHT_TYPE_WATCHED,
PEEP_THOUGHT_TYPE_NOT_HUNGRY,
PEEP_THOUGHT_TYPE_NOT_THIRSTY,
PEEP_THOUGHT_TYPE_BATHROOM,
PEEP_THOUGHT_TYPE_NONE,
PEEP_THOUGHT_TYPE_NONE,
};
/** rct2: 0x00982326 */
static const uint8 peep_item_containers[] = {
0xFF, // PEEP_ITEM_BALLOON
0xFF, // PEEP_ITEM_TOY
0xFF, // PEEP_ITEM_MAP
0xFF, // PEEP_ITEM_PHOTO
0xFF, // PEEP_ITEM_UMBRELLA
SHOP_ITEM_EMPTY_CAN, // PEEP_ITEM_DRINK
SHOP_ITEM_EMPTY_BURGER_BOX, // PEEP_ITEM_BURGER
SHOP_ITEM_RUBBISH, // PEEP_ITEM_FRIES
0xFF, // PEEP_ITEM_ICE_CREAM
0xFF, // PEEP_ITEM_COTTON_CANDY
0xFF, // PEEP_ITEM_EMPTY_CAN
0xFF, // PEEP_ITEM_RUBBISH
0xFF, // PEEP_ITEM_EMPTY_BURGER_BOX
SHOP_ITEM_RUBBISH, // PEEP_ITEM_PIZZA
0xFF, // PEEP_ITEM_VOUCHER
SHOP_ITEM_RUBBISH, // PEEP_ITEM_POPCORN
0xFF, // PEEP_ITEM_HOT_DOG
0xFF, // PEEP_ITEM_TENTACLE
0xFF, // PEEP_ITEM_HAT
0xFF, // PEEP_ITEM_CANDY_APPLE
0xFF, // PEEP_ITEM_TSHIRT
0xFF, // PEEP_ITEM_DONUT
SHOP_ITEM_EMPTY_CUP, // PEEP_ITEM_COFFEE
0xFF, // PEEP_ITEM_EMPTY_CUP
SHOP_ITEM_EMPTY_BOX, // PEEP_ITEM_CHICKEN
SHOP_ITEM_EMPTY_BOTTLE, // PEEP_ITEM_LEMONADE
0xFF, // PEEP_ITEM_EMPTY_BOX
0xFF, // PEEP_ITEM_EMPTY_BOTTLE
};
/** rct2: 0x00982342 */
static const uint8 peep_extra_item_containers[] = {
0xFF, // PEEP_ITEM_PHOTO2
0xFF, // PEEP_ITEM_PHOTO3
0xFF, // PEEP_ITEM_PHOTO4
0xFF, // PEEP_ITEM_PRETZEL
SHOP_ITEM_EMPTY_CUP, // PEEP_ITEM_CHOCOLATE
SHOP_ITEM_EMPTY_CUP, // PEEP_ITEM_ICED_TEA
0xFF, // PEEP_ITEM_FUNNEL_CAKE
0xFF, // PEEP_ITEM_SUNGLASSES
SHOP_ITEM_EMPTY_BOWL_BLUE, // PEEP_ITEM_BEEF_NOODLES
SHOP_ITEM_EMPTY_BOWL_BLUE, // PEEP_ITEM_FRIED_RICE_NOODLES
SHOP_ITEM_EMPTY_BOWL_RED, // PEEP_ITEM_WONTON_SOUP
SHOP_ITEM_EMPTY_BOWL_RED, // PEEP_ITEM_MEATBALL_SOUP
SHOP_ITEM_EMPTY_JUICE_CUP, // PEEP_ITEM_FRUIT_JUICE
SHOP_ITEM_EMPTY_DRINK_CARTON, // PEEP_ITEM_SOYBEAN_MILK
SHOP_ITEM_EMPTY_DRINK_CARTON, // PEEP_ITEM_SU_JONGKWA
0xFF, // PEEP_ITEM_SUB_SANDWICH
0xFF, // PEEP_ITEM_COOKIE
0xFF, // PEEP_ITEM_EMPTY_BOWL_RED
0xFF, // PEEP_ITEM_EMPTY_DRINK_CARTON
0xFF, // PEEP_ITEM_EMPTY_JUICE_CUP
0xFF, // PEEP_ITEM_ROAST_SAUSAGE
0xFF, // PEEP_ITEM_EMPTY_BOWL_BLUE
};
/**
*
* rct2: 0x0068F41A
*/
static void sub_68F41A(rct_peep *peep, int index)
{
if (peep->type == PEEP_TYPE_STAFF){
if (peep->staff_type != STAFF_TYPE_SECURITY)
return;
uint8 sprite_type = PEEP_SPRITE_TYPE_23;
if (peep->state != PEEP_STATE_PATROLLING)
sprite_type = PEEP_SPRITE_TYPE_SECURITY;
if (peep->sprite_type == sprite_type)
return;
peep->sprite_type = sprite_type;
peep->action_sprite_image_offset = 0;
peep->no_action_frame_no = 0;
if (peep->action < PEEP_ACTION_NONE_1)
peep->action = PEEP_ACTION_NONE_2;
peep->peep_flags &= ~PEEP_FLAGS_SLOW_WALK;
if (SpriteTypeToSlowWalkMap[sprite_type]) {
peep->peep_flags |= PEEP_FLAGS_SLOW_WALK;
}
peep->action_sprite_type = 0xFF;
sub_693B58(peep);
return;
}
if ((index & 0x1FF) == (gCurrentTicks & 0x1FF)){
if (peep->peep_flags & PEEP_FLAGS_CROWDED){
uint8 thought_type = crowded_thoughts[scenario_rand() & 0xF];
if (thought_type != PEEP_THOUGHT_TYPE_NONE){
peep_insert_new_thought(peep, thought_type, 0xFF);
}
}
if (peep->peep_flags & PEEP_FLAGS_EXPLODE && peep->x != (sint16)0x8000){
audio_play_sound_at_location(SOUND_CRASH, peep->x, peep->y, peep->z);
sprite_misc_explosion_cloud_create(peep->x, peep->y, peep->z + 16);
sprite_misc_explosion_flare_create(peep->x, peep->y, peep->z + 16);
peep_remove(peep);
return;
}
if (peep->peep_flags & PEEP_FLAGS_HUNGER){
if (peep->hunger >= 15)peep->hunger -= 15;
}
if (peep->peep_flags & PEEP_FLAGS_BATHROOM){
if (peep->bathroom <= 180)peep->bathroom += 50;
}
if (peep->peep_flags & PEEP_FLAGS_HAPPINESS){
peep->happiness_growth_rate = 5;
}
if (peep->peep_flags & PEEP_FLAGS_NAUSEA){
peep->nausea_growth_rate = 200;
if (peep->nausea <= 130)peep->nausea = 130;
}
if (peep->angriness != 0)
peep->angriness--;
if (peep->state == PEEP_STATE_WALKING || peep->state == PEEP_STATE_SITTING){
peep->var_F2++;
if (peep->var_F2 >= 18){
peep->var_F2 = 0;
if (peep->x != (sint16)0x8000){
uint8 thought_type = peep_assess_surroundings(peep->x & 0xFFE0, peep->y & 0xFFE0, peep->z);
if (thought_type != PEEP_THOUGHT_TYPE_NONE) {
peep_insert_new_thought(peep, thought_type, 0xFF);
peep->happiness_growth_rate = min(255, peep->happiness_growth_rate + 45);
}
}
}
}
peep_update_sprite_type(peep);
if (peep->state == PEEP_STATE_ON_RIDE || peep->state == PEEP_STATE_ENTERING_RIDE){
peep->time_on_ride = min(255, peep->time_on_ride + 1);
if (peep->peep_flags & PEEP_FLAGS_WOW){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_WOW2, 0xFF);
}
if (peep->time_on_ride > 15){
peep->happiness_growth_rate = min(0, peep->happiness_growth_rate - 5);
if (peep->time_on_ride > 22){
rct_ride* ride = get_ride(peep->current_ride);
uint8 thought_type = ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) ?
PEEP_THOUGHT_TYPE_GET_OUT :
PEEP_THOUGHT_TYPE_GET_OFF;
peep_insert_new_thought(peep, thought_type, peep->current_ride);
}
}
}
if (peep->state == PEEP_STATE_WALKING &&
peep->outside_of_park == 0 &&
!(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) &&
peep->no_of_rides == 0 &&
peep->guest_heading_to_ride_id == 0xFF){
uint32 time_duration = gScenarioTicks - peep->time_in_park;
time_duration /= 2048;
if (time_duration >= 5){
peep_pick_ride_to_go_on(peep);
if (peep->guest_heading_to_ride_id == 0xFF){
peep->happiness_growth_rate = max(peep->happiness_growth_rate - 128, 0);
peep_leave_park(peep);
peep_update_hunger(peep);
goto loc_68F9F3;
}
}
}
if ((scenario_rand() & 0xFFFF) <= (peep->item_standard_flags & PEEP_ITEM_MAP ? 8192U : 2184U)){
peep_pick_ride_to_go_on(peep);
}
if ((index & 0x3FF) == (gCurrentTicks & 0x3FF)){
if (peep->outside_of_park == 0 &&
(peep->state == PEEP_STATE_WALKING || peep->state == PEEP_STATE_SITTING)){
uint8 num_thoughts = 0;
uint8 possible_thoughts[5] = { 0 };
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK){
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_GO_HOME;
}
else{
if (peep->energy <= 70 &&
peep->happiness < 128){
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_TIRED;
}
if (peep->hunger <= 10 &&
!peep_has_food(peep)){
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_HUNGRY;
}
if (peep->thirst <= 25 &&
!peep_has_food(peep)){
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_THIRSTY;
}
if (peep->bathroom >= 160){
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_BATHROOM;
}
if (!(gParkFlags & PARK_FLAGS_NO_MONEY) &&
peep->cash_in_pocket <= MONEY(9, 00) &&
peep->happiness >= 105 &&
peep->energy >= 70){
/* The energy check was originally a second check on happiness.
* This was superfluous so should probably check something else.
* Guessed that this should really be checking energy, since
* the addresses for happiness and energy are quite close,
* 70 is also the threshold for tired thoughts (see above) and
* it makes sense that a tired peep might not think about getting
* more money. */
possible_thoughts[num_thoughts++] = PEEP_THOUGHT_TYPE_RUNNING_OUT;
}
}
if (num_thoughts != 0){
uint8 chosen_thought = possible_thoughts[scenario_rand() % num_thoughts];
peep_insert_new_thought(peep, chosen_thought, 0xFF);
switch (chosen_thought){
case PEEP_THOUGHT_TYPE_HUNGRY:
peep_head_for_nearest_ride_with_flags(peep, RIDE_TYPE_FLAG_SELLS_FOOD);
break;
case PEEP_THOUGHT_TYPE_THIRSTY:
peep_head_for_nearest_ride_with_flags(peep, RIDE_TYPE_FLAG_SELLS_DRINKS);
break;
case PEEP_THOUGHT_TYPE_BATHROOM:
peep_head_for_nearest_ride_with_flags(peep, RIDE_TYPE_FLAG_IS_BATHROOM);
break;
case PEEP_THOUGHT_TYPE_RUNNING_OUT:
peep_head_for_nearest_ride_type(peep, RIDE_TYPE_CASH_MACHINE);
break;
}
}
}
}
else{
if (peep->nausea >= 140){
uint8 thought_type = PEEP_THOUGHT_TYPE_SICK;
if (peep->nausea >= 200){
thought_type = PEEP_THOUGHT_TYPE_VERY_SICK;
peep_head_for_nearest_ride_type(peep, RIDE_TYPE_FIRST_AID);
}
peep_insert_new_thought(peep, thought_type, 0xFF);
}
}
switch (peep->state){
case PEEP_STATE_WALKING:
case PEEP_STATE_LEAVING_PARK:
case PEEP_STATE_ENTERING_PARK:
sub_68F8CD(peep);
peep_update_hunger(peep);
break;
case PEEP_STATE_SITTING:
if (peep->energy_growth_rate <= 135)
peep->energy_growth_rate += 5;
if (peep->thirst >= 5){
peep->thirst -= 4;
peep->bathroom = min(255, peep->bathroom + 3);
}
if (peep->nausea_growth_rate >= 50)
peep->nausea_growth_rate -= 6;
// In the original this branched differently
// but it would mean setting the peep happiness from
// a thought type entry which i think is incorrect.
peep_update_hunger(peep);
break;
case PEEP_STATE_QUEUING:
if (peep->time_in_queue >= 2000){
rct_map_element* mapElement = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
uint8 found = 0;
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (mapElement->base_height != peep->next_z)
continue;
// Check if the footpath has a queue line TV monitor on it
if (footpath_element_has_path_scenery(mapElement) && footpath_element_path_scenery_is_ghost(mapElement)){
uint8 pathSceneryIndex = footpath_element_get_path_scenery_index(mapElement);
rct_scenery_entry *sceneryEntry = get_footpath_item_entry(pathSceneryIndex);
if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN){
found = 1;
}
}
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (found){
if (peep->happiness_growth_rate < 90)
peep->happiness_growth_rate = 90;
if (peep->happiness_growth_rate < 165)
peep->happiness_growth_rate += 2;
}
else{
peep->happiness_growth_rate = max(peep->happiness_growth_rate - 4, 0);
}
}
peep_update_hunger(peep);
break;
case PEEP_STATE_ENTERING_RIDE:
if (peep->sub_state == 17 ||
peep->sub_state == 15){
sub_68F8CD(peep);
}
peep_update_hunger(peep);
break;
}
loc_68F9F3:
if (peep->happiness_growth_rate >= 128)
peep->happiness_growth_rate--;
else
peep->happiness_growth_rate++;
peep->nausea_growth_rate = max(peep->nausea_growth_rate - 2, 0);
if (peep->energy <= 50){
peep->energy = max(peep->energy - 2, 0);
}
if (peep->hunger < 10){
peep->hunger = max(peep->hunger - 1, 0);
}
if (peep->thirst < 10){
peep->thirst = max(peep->thirst - 1, 0);
}
if (peep->bathroom >= 195){
peep->bathroom--;
}
if (peep->state == PEEP_STATE_WALKING &&
peep->nausea_growth_rate >= 128){
if ((scenario_rand() & 0xFF) <= (uint8)((peep->nausea - 128) / 2)){
if (peep->action >= PEEP_ACTION_NONE_1){
peep->action = PEEP_ACTION_THROW_UP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
}
// 68FA89
if (peep->var_42 == 0 &&
peep_has_food(peep)){
peep->var_42 += 3;
}
if (peep->var_42 != 0 &&
peep->state != PEEP_STATE_ON_RIDE){
peep->var_42 = max(peep->var_42 - 3, 0);
if (peep_has_drink(peep)){
peep->thirst = min(peep->thirst + 7, 255);
}
else{
peep->hunger = min(peep->hunger + 7, 255);
peep->thirst = max(peep->thirst - 3, 0);
peep->bathroom = min(peep->bathroom + 2, 255);
}
if (peep->var_42 == 0){
int chosen_food = bitscanforward(peep_has_food_standard_flag(peep));
if (chosen_food != -1){
peep->item_standard_flags &= ~(1 << chosen_food);
uint8 discard_container = peep_item_containers[chosen_food];
if (discard_container != 0xFF){
peep->item_standard_flags |= (1 << discard_container);
}
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
else{
chosen_food = bitscanforward(peep_has_food_extra_flag(peep));
if (chosen_food != -1){
peep->item_extra_flags &= ~(1 << chosen_food);
uint8 discard_container = peep_extra_item_containers[chosen_food];
if (discard_container != 0xFF){
if (discard_container >= 32)
peep->item_extra_flags |= (1 << (discard_container - 32));
else
peep->item_standard_flags |= (1 << discard_container);
}
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
}
}
}
uint8 energy = peep->energy;
uint8 energy_growth = peep->energy_growth_rate;
if (energy >= energy_growth){
energy -= 2;
if (energy < energy_growth)
energy = energy_growth;
}
else{
energy = min(255, energy + 4);
if (energy > energy_growth)
energy = energy_growth;
}
if (energy < 32)
energy = 32;
if (energy > 128)
energy = 128;
if (energy != peep->energy){
peep->energy = energy;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_2;
}
uint8 happiness = peep->happiness;
uint8 happiness_growth = peep->happiness_growth_rate;
if (happiness >= happiness_growth){
happiness = max(happiness - 4, 0);
if (happiness < happiness_growth)
happiness = happiness_growth;
}
else{
happiness = min(255, happiness + 4);
if (happiness > happiness_growth)
happiness = happiness_growth;
}
if (happiness != peep->happiness){
peep->happiness = happiness;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_2;
}
uint8 nausea = peep->nausea;
uint8 nausea_growth = peep->nausea_growth_rate;
if (nausea >= nausea_growth){
nausea = max(nausea - 4, 0);
if (nausea < nausea_growth)
nausea = nausea_growth;
}
else{
nausea = min(255, nausea + 4);
if (nausea > nausea_growth)
nausea = nausea_growth;
}
if (nausea != peep->nausea){
peep->nausea = nausea;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_2;
}
}
/*
* rct2: 0x68F3AE
* Set peep state to falling if path below has gone missing, return 1 if current path is valid, 0 if peep starts falling
*/
static int checkForPath(rct_peep *peep){
peep->var_C4++;
if ((peep->var_C4 & 0xF) != (peep->sprite_index & 0xF)){
// This condition makes the check happen less often so the peeps hover for a short,
// random time when a path below them has been deleted
return 1;
}
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
uint8 map_type = MAP_ELEMENT_TYPE_PATH;
if (peep->next_var_29 & ((1 << 4) | (1 << 3))){
map_type = MAP_ELEMENT_TYPE_SURFACE;
}
int z = peep->next_z;
do {
if (map_element_get_type(map_element) == map_type){
if (z == map_element->base_height) {
// Found a suitable path
return 1;
}
}
} while (!map_element_is_last_for_tile(map_element++));
// Found no suitable path
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return 0;
}
void sub_693B58(rct_peep* peep){
uint8 action_sprite_type;
if (peep->sprite_type >= countof(g_sprite_entries)) {
return;
}
if (peep->action >= PEEP_ACTION_NONE_1){ // PEEP_ACTION_NONE_1 or PEEP_ACTION_NONE_2
action_sprite_type = unk_981D8C[peep->special_sprite];
} else {
action_sprite_type = unk_981D8F[peep->action];
}
if (action_sprite_type == peep->action_sprite_type)return;
invalidate_sprite_2((rct_sprite*)peep);
peep->action_sprite_type = action_sprite_type;
const rct_sprite_bounds* spriteBounds = g_sprite_entries[peep->sprite_type].sprite_bounds;
peep->sprite_width = spriteBounds[action_sprite_type].sprite_width;
peep->sprite_height_negative = spriteBounds[action_sprite_type].sprite_height_negative;
peep->sprite_height_positive = spriteBounds[action_sprite_type].sprite_height_positive;
// This is pointless as nothing will have changed.
invalidate_sprite_2((rct_sprite*)peep);
}
/* 0x00693BE5 */
void sub_693BE5(rct_peep* peep, uint8 al){
if (al == peep->special_sprite)return;
peep->special_sprite = al;
// If NONE_1 or NONE_2
if (peep->action >= PEEP_ACTION_NONE_1){
peep->action_sprite_image_offset = 0;
}
sub_693B58(peep);
}
/**
*
* rct2: 0x0069A512
*/
void remove_peep_from_ride(rct_peep* peep)
{
if (peep->state == PEEP_STATE_QUEUING) {
remove_peep_from_queue(peep);
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
sub_693BE5(peep, 0);
}
static void peep_state_reset(rct_peep* peep){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
sub_693BE5(peep, 0);
}
/**
*
* rct2: 0x69C308
* Check if lost.
*/
static void peep_check_if_lost(rct_peep* peep){
if (!(peep->peep_flags & PEEP_FLAGS_LOST)){
if (gRideCount < 2) return;
peep->peep_flags ^= PEEP_FLAGS_21;
if (!(peep->peep_flags & PEEP_FLAGS_21)) return;
peep->var_F4++;
if (peep->var_F4 != 254)return;
peep->var_F4 = 230;
}
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_LOST, 0xFF);
peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0);
}
/**
*
* rct2: 0x69C26B
* Check if cant find ride.
*/
static void peep_check_cant_find_ride(rct_peep* peep){
if (peep->guest_heading_to_ride_id == 0xFF)
return;
// Peeps will think "I can't find ride X" twice before giving up completely.
if (peep->peep_is_lost_countdown == 30 || peep->peep_is_lost_countdown == 60) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_FIND, peep->guest_heading_to_ride_id);
peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0);
}
peep->peep_is_lost_countdown--;
if (peep->peep_is_lost_countdown != 0)
return;
peep->guest_heading_to_ride_id = 0xFF;
rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w){
window_event_invalidate_call(w);
}
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
}
/**
*
* rct2: 0x69C2D0
* Check if cant find exit.
*/
static void peep_check_cant_find_exit(rct_peep* peep){
if (!(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK))
return;
// Peeps who can't find the park exit will continue to get less happy until they find it.
if (peep->peep_is_lost_countdown == 1) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_FIND_EXIT, 0xFF);
peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0);
}
if (--peep->peep_is_lost_countdown == 0)
peep->peep_is_lost_countdown = 90;
}
/** rct2: 0x00981D7C, 0x00981D7E */
const rct_xy16 word_981D7C[4] = {
{ -2, 0 },
{ 0, 2 },
{ 2, 0 },
{ 0, -2 }
};
/**
*
* rct2: 0x6939EB
* Also used to move peeps to the correct position to
* start an action. Returns 1 if the correct destination
* has not yet been reached. xy_distance is how close the
* peep is to the target.
*
* @param x (ax)
* @param y (cx)
* @param distance (bp)
* @param peep (esi)
*/
static int peep_update_action(sint16* x, sint16* y, sint16* xy_distance, rct_peep* peep){
_unk_F1AEF0 = peep->action_sprite_image_offset;
if (peep->action == 0xFE){
peep->action = 0xFF;
}
*x = peep->x - peep->destination_x;
*y = peep->y - peep->destination_y;
int x_delta = abs(*x);
int y_delta = abs(*y);
*xy_distance = x_delta + y_delta;
if (peep->action >= 0xFE){
if (*xy_distance <= peep->destination_tolerence){
return 0;
}
int direction = 0;
if (x_delta < y_delta){
direction = 8;
if (*y >= 0){
direction = 24;
}
}
else{
direction = 16;
if (*x >= 0){
direction = 0;
}
}
peep->sprite_direction = direction;
*x = peep->x + word_981D7C[direction / 8].x;
*y = peep->y + word_981D7C[direction / 8].y;
peep->no_action_frame_no++;
const rct_sprite_image * edi = g_sprite_entries[peep->sprite_type].sprite_image;
const uint8* _edi = (edi[peep->action_sprite_type]).unkn_04;
if (peep->no_action_frame_no >= *_edi){
peep->no_action_frame_no = 0;
}
peep->action_sprite_image_offset = _edi[peep->no_action_frame_no + 1];
return 1;
}
const rct_sprite_image * edi = g_sprite_entries[peep->sprite_type].sprite_image;
const uint8* _edi = (edi[peep->action_sprite_type]).unkn_04;
peep->action_frame++;
int ebx = _edi[peep->action_frame + 1];
// If last frame of action
if (ebx == 0xFF){
peep->action_sprite_image_offset = 0;
peep->action = 0xFF;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
*x = peep->x;
*y = peep->y;
return 1;
}
peep->action_sprite_image_offset = ebx;
// If not throwing up and not at the frame where sick appears.
if (peep->action != PEEP_ACTION_THROW_UP || peep->action_frame != 15){
invalidate_sprite_2((rct_sprite*)peep);
*x = peep->x;
*y = peep->y;
return 1;
}
// We are throwing up
peep->hunger /= 2;
peep->nausea_growth_rate /= 2;
if (peep->nausea < 30)
peep->nausea = 0;
else
peep->nausea -= 30;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_2;
// Create sick at location
litter_create(peep->x, peep->y, peep->z, peep->sprite_direction, (peep->sprite_index & 1) ? LITTER_TYPE_SICK_ALT: LITTER_TYPE_SICK);
int sound_id = SOUND_COUGH_1 + (scenario_rand() & 3);
audio_play_sound_at_location(sound_id, peep->x, peep->y, peep->z);
invalidate_sprite_2((rct_sprite*)peep);
*x = peep->x;
*y = peep->y;
return 1;
}
/**
* rct2: 0x0069A409
* Decreases rider count if on/entering a ride.
*/
void peep_decrement_num_riders(rct_peep* peep){
if (peep->state == PEEP_STATE_ON_RIDE
|| peep->state == PEEP_STATE_ENTERING_RIDE){
rct_ride* ride = get_ride(peep->current_ride);
ride->num_riders--;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
}
}
/* Part of 0x0069B8CC rct2: 0x0069BC31 */
static void set_sprite_type(rct_peep* peep, uint8 type){
if (peep->sprite_type == type)return;
peep->sprite_type = type;
peep->action_sprite_image_offset = 0;
peep->no_action_frame_no = 0;
if (peep->action >= PEEP_ACTION_NONE_1)
peep->action = PEEP_ACTION_NONE_2;
peep->peep_flags &= ~PEEP_FLAGS_SLOW_WALK;
assert(type < countof(SpriteTypeToSlowWalkMap));
if (SpriteTypeToSlowWalkMap[type]) {
peep->peep_flags |= PEEP_FLAGS_SLOW_WALK;
}
peep->action_sprite_type = 0xFF;
sub_693B58(peep);
if (peep->state == PEEP_STATE_SITTING){
peep->action = PEEP_ACTION_NONE_1;
peep->next_action_sprite_type = 7;
sub_693BAB(peep);
}
if (peep->state == PEEP_STATE_WATCHING){
peep->action = PEEP_ACTION_NONE_1;
peep->next_action_sprite_type = 2;
sub_693BAB(peep);
}
}
typedef struct item_pref {
uint8 type; // 0 for standard, 1 for extra
uint32 item; // And this with the relevant flags
uint8 sprite_type;
} item_pref;
item_pref item_order_preference[] = {
{ 0, PEEP_ITEM_ICE_CREAM, 15 },
{ 0, PEEP_ITEM_FRIES, 16},
{ 0, PEEP_ITEM_PIZZA, 22 },
{ 0, PEEP_ITEM_BURGER, 17 },
{ 0, PEEP_ITEM_DRINK, 18 },
{ 0, PEEP_ITEM_COFFEE, 35 },
{ 0, PEEP_ITEM_CHICKEN, 34 },
{ 0, PEEP_ITEM_LEMONADE, 37 },
{ 0, PEEP_ITEM_COTTON_CANDY, 20 },
{ 0, PEEP_ITEM_POPCORN, 22 },
{ 0, PEEP_ITEM_HOT_DOG, 31 },
{ 0, PEEP_ITEM_TENTACLE, 32 },
{ 0, PEEP_ITEM_CANDY_APPLE, 33 },
{ 0, PEEP_ITEM_DONUT, 34 },
{ 1, PEEP_ITEM_PRETZEL, 39 },
{ 1, PEEP_ITEM_COOKIE, 39 },
{ 1, PEEP_ITEM_CHOCOLATE, 35 },
{ 1, PEEP_ITEM_ICED_TEA, 35 },
{ 1, PEEP_ITEM_FUNNEL_CAKE, 43 },
{ 1, PEEP_ITEM_BEEF_NOODLES, 44 },
{ 1, PEEP_ITEM_FRIED_RICE_NOODLES, 44 },
{ 1, PEEP_ITEM_WONTON_SOUP, 46 },
{ 1, PEEP_ITEM_MEATBALL_SOUP, 46 },
{ 1, PEEP_ITEM_FRUIT_JUICE, 43 },
{ 1, PEEP_ITEM_SOYBEAN_MILK, 41 },
{ 1, PEEP_ITEM_SU_JONGKWA, 41 },
{ 1, PEEP_ITEM_SUB_SANDWICH, 47 },
{ 1, PEEP_ITEM_ROAST_SAUSAGE, 45 },
{ 0, PEEP_ITEM_BALLOON, 19 },
{ 0, PEEP_ITEM_HAT, 30},
{ 1, PEEP_ITEM_SUNGLASSES, 40},
{ 0xFF, 0xFFFFFFFF, 0xFF}
};
/**
*
* rct2: 0x0069B8CC
*/
void peep_update_sprite_type(rct_peep* peep)
{
if (
peep->sprite_type == PEEP_SPRITE_TYPE_19 &&
(scenario_rand() & 0xFFFF) <= 327
) {
uint8 bl = 0;
if (
(scenario_rand() & 0xFFFF) <= 13107 &&
peep->x != SPRITE_LOCATION_NULL
) {
bl = 1;
audio_play_sound_at_location(SOUND_BALLOON_POP, peep->x, peep->y, peep->z);
}
if (peep->x != SPRITE_LOCATION_NULL) {
create_balloon(peep->x, peep->y, peep->z + 9, peep->balloon_colour, bl);
}
peep->item_standard_flags &= ~PEEP_ITEM_BALLOON;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
if (
gClimateCurrentRainLevel != 0 &&
(peep->item_standard_flags & PEEP_ITEM_UMBRELLA) &&
peep->x != SPRITE_LOCATION_NULL
) {
int x = peep->x & 0xFFE0;
int y = peep->y & 0xFFE0;
if (x < 0x1FFF && y < 0x1FFF) {
rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32);
while (1) {
if ((peep->z / 8) < map_element->base_height) break;
if (map_element_is_last_for_tile(map_element)) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_UMBRELLA);
return;
}
map_element++;
}
}
}
for (item_pref* item_pref = item_order_preference; item_pref->type != 0xFF; item_pref++) {
if (item_pref->type == 0){
if (peep->item_standard_flags & item_pref->item) {
set_sprite_type(peep, item_pref->sprite_type);
return;
}
} else {
if (peep->item_extra_flags & item_pref->item) {
set_sprite_type(peep, item_pref->sprite_type);
return;
}
}
}
if (peep->state == PEEP_STATE_WATCHING && peep->standing_flags & (1 << 1)) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_WATCHING);
return;
}
if (peep->nausea > 170) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_VERY_NAUSEOUS);
return;
}
if (peep->nausea > 140) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_NAUSEOUS);
return;
}
if (peep->energy <= 64 && peep->happiness < 128) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_26);
return;
}
if (peep->energy <= 80 && peep->happiness < 128) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_25);
return;
}
if (peep->bathroom > 220) {
set_sprite_type(peep, PEEP_SPRITE_TYPE_REQUIRE_BATHROOM);
return;
}
set_sprite_type(peep, PEEP_SPRITE_TYPE_NORMAL);
}
/**
* Call after changing a peeps state to insure that all relevant windows update.
* Note also increase ride count if on/entering a ride.
* rct2: 0x0069A42F
*/
void peep_window_state_update(rct_peep* peep)
{
rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL)
window_event_invalidate_call(w);
if (peep->type == PEEP_TYPE_GUEST) {
if (peep->state == PEEP_STATE_ON_RIDE || peep->state == PEEP_STATE_ENTERING_RIDE) {
rct_ride* ride = get_ride(peep->current_ride);
ride->num_riders++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
}
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
window_invalidate_by_class(WC_GUEST_LIST);
} else {
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
window_invalidate_by_class(WC_STAFF_LIST);
}
}
/**
*
* rct2: 0x0069A535
*/
void peep_sprite_remove(rct_peep* peep){
remove_peep_from_ride(peep);
invalidate_sprite_2((rct_sprite*)peep);
window_close_by_number(WC_PEEP, peep->sprite_index);
window_close_by_number(WC_FIRE_PROMPT, peep->sprite_identifier);
if (peep->type == PEEP_TYPE_GUEST){
window_invalidate_by_class(WC_GUEST_LIST);
news_item_disable_news(NEWS_ITEM_PEEP_ON_RIDE, peep->sprite_index);
}
else{
window_invalidate_by_class(WC_STAFF_LIST);
gStaffModes[peep->staff_id] = 0;
peep->type = 0xFF;
staff_update_greyed_patrol_areas();
peep->type = PEEP_TYPE_STAFF;
news_item_disable_news(NEWS_ITEM_PEEP, peep->sprite_index);
}
sprite_remove((rct_sprite*)peep);
}
/**
* New function removes peep from park existence. Works with staff.
*/
void peep_remove(rct_peep* peep){
if (peep->type == PEEP_TYPE_GUEST){
if (peep->outside_of_park == 0){
gNumGuestsInPark--;
gToolbarDirtyFlags |= BTM_TB_DIRTY_FLAG_PEEP_COUNT;
}
if (peep->state == PEEP_STATE_ENTERING_PARK){
gNumGuestsHeadingForPark--;
}
}
peep_sprite_remove(peep);
}
/**
* Falling and its subset drowning
* rct2: 0x690028
*/
static void peep_update_falling(rct_peep* peep){
if (peep->action == PEEP_ACTION_DROWNING){
// Check to see if we are ready to drown.
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
if (peep->action == PEEP_ACTION_DROWNING) return;
if (gConfigNotifications.guest_died) {
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
news_item_add_to_queue(NEWS_ITEM_BLANK, STR_NEWS_ITEM_GUEST_DROWNED, peep->x | (peep->y << 16));
}
gParkRatingCasualtyPenalty = min(gParkRatingCasualtyPenalty + 25, 1000);
peep_remove(peep);
return;
}
// If not drowning then falling. Note: peeps 'fall' after leaving a ride/enter the park.
rct_map_element *map_element = map_get_first_element_at(peep->x / 32, peep->y / 32);
rct_map_element *saved_map = NULL;
int saved_height = 0;
if (map_element != NULL) {
do {
// If a path check if we are on it
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH) {
int height = map_height_from_slope(peep->x, peep->y, map_element->properties.surface.slope)
+ map_element->base_height * 8;
if (height < peep->z - 1 || height > peep->z + 4) continue;
saved_height = height;
saved_map = map_element;
break;
} // If a surface get the height and see if we are on it
else if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_SURFACE) {
// If the surface is water check to see if we could be drowning
if (map_element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK) {
int height = (map_element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK) * 16;
if (height - 4 >= peep->z && height < peep->z + 20) {
// Looks like we are drowning!
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(peep->x, peep->y, height, (rct_sprite*)peep);
// Drop balloon if held
if (peep->item_standard_flags & PEEP_ITEM_BALLOON) {
peep->item_standard_flags &= ~PEEP_ITEM_BALLOON;
if (peep->sprite_type == PEEP_SPRITE_TYPE_19 && peep->x != (sint16) 0x8000) {
create_balloon(peep->x, peep->y, height, peep->balloon_colour, 0);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
}
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_DROWNING, -1);
peep->action = PEEP_ACTION_DROWNING;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
peep_window_state_update(peep);
return;
}
}
int map_height = map_element_height(0xFFFF & peep->x, 0xFFFF & peep->y) & 0xFFFF;
if (map_height < peep->z || map_height - 4 > peep->z) continue;
saved_height = map_height;
saved_map = map_element;
} // If not a path or surface go see next element
else continue;
} while (!map_element_is_last_for_tile(map_element++));
}
// This will be null if peep is falling
if (saved_map == NULL){
invalidate_sprite_2((rct_sprite*)peep);
if (peep->z <= 1){
// Remove peep if it has gone to the void
peep_remove(peep);
return;
}
sprite_move(peep->x, peep->y, peep->z - 2, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(peep->x, peep->y, saved_height, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
peep->next_x = peep->x & 0xFFE0;
peep->next_y = peep->y & 0xFFE0;
peep->next_z = saved_map->base_height;
int edx = saved_map->properties.surface.slope & 0x7;
if (map_element_get_type(saved_map) != MAP_ELEMENT_TYPE_PATH){
edx = 8;
}
peep->next_var_29 = edx;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
}
/**
*
* rct2: 0x00691677
*/
static void peep_try_get_up_from_sitting(rct_peep* peep){
// Eats all food first
if (peep_has_food(peep))return;
peep->time_to_sitdown--;
if (peep->time_to_sitdown) return;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
// Set destination to the center of the tile.
peep->destination_x = (peep->x & 0xFFE0) + 16;
peep->destination_y = (peep->y & 0xFFE0) + 16;
peep->destination_tolerence = 5;
sub_693B58(peep);
}
/** rct2: 0x00981F2C, 0x00981F2E */
static const rct_xy16 _981F2C[] = {
{ 7, 12 },
{ 12, 25 },
{ 25, 20 },
{ 20, 7 },
{ 7, 20 },
{ 20, 25 },
{ 25, 12 },
{ 12, 7 },
};
/**
*
* rct2: 0x0069152B
*/
static void peep_update_sitting(rct_peep* peep){
if (peep->sub_state == 0){
if (!checkForPath(peep))return;
//691541
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
int ebx = peep->var_37 & 0x7;
int x = (peep->x & 0xFFE0) + _981F2C[ebx].x;
int y = (peep->y & 0xFFE0) + _981F2C[ebx].y;
int z = peep->z;
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
peep->sprite_direction = ((peep->var_37 + 2) & 3) * 8;
invalidate_sprite_2((rct_sprite*)peep);
peep->action = 254;
peep->next_action_sprite_type = 7;
sub_693BAB(peep);
peep->sub_state++;
// Sets time to sit on seat
peep->time_to_sitdown = (129 - peep->energy) * 16 + 50;
}
else if (peep->sub_state == 1){
if (peep->action < 0xFE){
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
if (peep->action != 0xFF) return;
peep->action = 0xFE;
peep_try_get_up_from_sitting(peep);
return;
}
if ((peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
// Set destination to the center of the tile
peep->destination_x = (peep->x & 0xFFE0) + 16;
peep->destination_y = (peep->y & 0xFFE0) + 16;
peep->destination_tolerence = 5;
sub_693B58(peep);
return;
}
if (peep->sprite_type == PEEP_SPRITE_TYPE_UMBRELLA) {
peep_try_get_up_from_sitting(peep);
return;
}
if (peep_has_food(peep)){
if ((scenario_rand() & 0xFFFF) > 1310){
peep_try_get_up_from_sitting(peep);
return;
}
peep->action = PEEP_ACTION_SITTING_EAT_FOOD;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
int rand = scenario_rand();
if ((rand & 0xFFFF) > 131){
peep_try_get_up_from_sitting(peep);
return;
}
if (peep->sprite_type == PEEP_SPRITE_TYPE_19 || peep->sprite_type == PEEP_SPRITE_TYPE_30) {
peep_try_get_up_from_sitting(peep);
return;
}
peep->action = PEEP_ACTION_SITTING_LOOK_AROUND_LEFT;
if (rand & 0x80000000){
peep->action = PEEP_ACTION_SITTING_LOOK_AROUND_RIGHT;
}
if (rand & 0x40000000){
peep->action = PEEP_ACTION_SITTING_CHECK_WATCH;
}
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
}
/**
*
* rct2: 0x006966A9
*/
void remove_peep_from_queue(rct_peep* peep)
{
rct_ride* ride = get_ride(peep->current_ride);
uint8 cur_station = peep->current_ride_station;
ride->queue_length[cur_station]--;
if (peep->sprite_index == ride->last_peep_in_queue[cur_station])
{
ride->last_peep_in_queue[cur_station] = peep->next_in_queue;
return;
}
uint16 spriteId = ride->last_peep_in_queue[cur_station];
while (spriteId != 0xFFFF) {
rct_peep* other_peep = GET_PEEP(spriteId);
if (peep->sprite_index == other_peep->next_in_queue){
other_peep->next_in_queue = peep->next_in_queue;
return;
}
spriteId = other_peep->next_in_queue;
}
}
/**
*
* rct2: 0x00691C6E
*/
static rct_vehicle* peep_choose_car_from_ride(rct_peep* peep, rct_ride* ride, uint8* car_array, uint8 car_array_size){
uint8 chosen_car = scenario_rand();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES)
&& ((chosen_car & 0xC) != 0xC)){
chosen_car = (scenario_rand() & 1) ? 0 : car_array_size - 1;
}
else{
chosen_car = (chosen_car * (uint16)car_array_size) >> 8;
}
peep->current_car = car_array[chosen_car];
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
for (int i = peep->current_car; i > 0; --i){
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
return vehicle;
}
/**
*
* rct2: 0x00691CD1
*/
static void peep_choose_seat_from_car(rct_peep* peep, rct_ride* ride, rct_vehicle* vehicle){
uint8 chosen_seat = vehicle->next_free_seat;
if (ride->mode == RIDE_MODE_FORWARD_ROTATION ||
ride->mode == RIDE_MODE_BACKWARD_ROTATION){
chosen_seat = (((~vehicle->vehicle_sprite_type + 1) >> 3) & 0xF) * 2;
if (vehicle->next_free_seat & 1){
chosen_seat++;
}
}
peep->current_seat = chosen_seat;
vehicle->next_free_seat++;
vehicle->peep[peep->current_seat] = peep->sprite_index;
vehicle->peep_tshirt_colours[peep->current_seat] = peep->tshirt_colour;
}
/**
*
* rct2: 0x00691D27
*/
static void peep_go_to_ride_entrance(rct_peep* peep, rct_ride* ride){
int x = ride->entrances[peep->current_ride_station] & 0xFF;
int y = ride->entrances[peep->current_ride_station] >> 8;
int z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[direction].x;
sint16 y_shift = word_981D6C[direction].y;
uint8 shift_multiplier = 21;
rct_ride_entry* ride_type = get_ride_entry(ride->subtype);
if (ride_type != NULL) {
if (ride_type->vehicles[ride_type->default_vehicle].flags_a & VEHICLE_ENTRY_FLAG_A_MINI_GOLF ||
ride_type->vehicles[ride_type->default_vehicle].flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x += x_shift;
y += y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_ENTERING_RIDE;
peep->sub_state = 1;
peep_window_state_update(peep);
peep->var_AC = 0;
peep->time_on_ride = 0;
remove_peep_from_queue(peep);
}
/**
*
* rct2: 0x00691A3B
*/
static void peep_update_ride_sub_state_0(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (peep->destination_tolerence != 0){
invalidate_sprite_2((rct_sprite*)peep);
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
sint16 z = peep->z;
if (xy_distance < 16){
z = ride->station_heights[peep->current_ride_station] * 8 + 2;
}
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
}
else{
peep->destination_tolerence = 0;
peep->sprite_direction ^= (1 << 4);
}
}
uint8 car_array_size = 0xFF;
uint8 car_array[255];
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_13)){
if (ride->num_riders >= ride->operation_option)
return;
}
else{
uint8 chosen_train = 0xFF;
if (ride->mode == RIDE_MODE_BUMPERCAR || ride->mode == RIDE_MODE_RACE){
if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
return;
for (int i = 0; i < ride->num_vehicles; ++i){
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[i]);
if (vehicle->next_free_seat >= vehicle->num_seats)
continue;
if (vehicle->status != VEHICLE_STATUS_WAITING_FOR_PASSENGERS)
continue;
chosen_train = i;
break;
}
}
else{
chosen_train = ride->train_at_station[peep->current_ride_station];
}
if (chosen_train == 0xFF){
return;
}
peep->current_train = chosen_train;
uint8* car_array_pointer = car_array;
int i = 0;
uint16 vehicle_id = ride->vehicles[chosen_train];
rct_vehicle* vehicle = GET_VEHICLE(vehicle_id);
for (; vehicle_id != 0xFFFF;
vehicle_id = vehicle->next_vehicle_on_train,
i++){
vehicle = GET_VEHICLE(vehicle_id);
uint8 num_seats = vehicle->num_seats;
if (vehicle_is_used_in_pairs(vehicle)){
num_seats &= VEHICLE_SEAT_NUM_MASK;
if (vehicle->next_free_seat & 1){
peep->current_car = i;
peep_choose_seat_from_car(peep, ride, vehicle);
peep_go_to_ride_entrance(peep, ride);
return;
}
}
if (num_seats == vehicle->next_free_seat)
continue;
if (ride->mode == RIDE_MODE_FORWARD_ROTATION ||
ride->mode == RIDE_MODE_BACKWARD_ROTATION)
{
uint8 position = (((~vehicle->vehicle_sprite_type + 1) >> 3) & 0xF) * 2;
if (vehicle->peep[position] != 0xFFFF)
continue;
}
*car_array_pointer++ = i;
}
car_array_size = (uint8)(car_array_pointer - car_array);
if (car_array_size == 0)return;
}
if (ride->status != RIDE_STATUS_OPEN ||
ride->vehicle_change_timeout != 0){
if (peep->destination_tolerence == 0){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
}
return;
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
return;
money16 ridePrice = ride_get_price(ride);
if (ridePrice != 0) {
if (!(peep->item_standard_flags & PEEP_ITEM_VOUCHER) ||
!(peep->voucher_type == VOUCHER_TYPE_RIDE_FREE) ||
!(peep->voucher_arguments == peep->current_ride)){
if (peep->cash_in_pocket <= 0){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, 0xFF);
if (peep->destination_tolerence == 0){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
}
return;
}
if (ridePrice > peep->cash_in_pocket){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, peep->current_ride);
if (peep->destination_tolerence == 0){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
}
return;
}
uint16 value = ride->value;
if (value != RIDE_VALUE_UNDEFINED){
if (value * 2 < ridePrice) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_VALUE, peep->current_ride);
if (peep->destination_tolerence == 0){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
}
return;
}
}
}
}
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_13)){
rct_vehicle* vehicle = peep_choose_car_from_ride(peep, ride, car_array, car_array_size);
peep_choose_seat_from_car(peep, ride, vehicle);
}
peep_go_to_ride_entrance(peep, ride);
}
/** rct2: 0x00981FD4, 0x00981FD6 */
static const rct_xy16 _981FD4[] = {
{ 8, 8 },
{ 8, 24 },
{ 24, 24 },
{ 24, 8 },
};
/**
*
* rct2: 0x006921D3
*/
static void peep_update_ride_sub_state_1(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
rct_ride_entry* ride_entry = get_ride_entry(ride->subtype);
if (peep_update_action(&x, &y, &xy_distance, peep))
{
uint16 distanceThreshold = 16;
if (ride_entry != NULL) {
uint8 vehicle = ride_entry->default_vehicle;
if (ride_entry->vehicles[vehicle].flags_a & VEHICLE_ENTRY_FLAG_A_MINI_GOLF ||
ride_entry->vehicles[vehicle].flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)) {
distanceThreshold = 28;
}
}
if (peep->sub_state == 1 && xy_distance < distanceThreshold) {
peep->sub_state = 2;
}
invalidate_sprite_2((rct_sprite*)peep);
sint16 z = ride->station_heights[peep->current_ride_station] * 8;
distanceThreshold += 4;
if (xy_distance < distanceThreshold) {
z += RideData5[ride->type].z;
}
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_13))
{
sint16 x, y, z;
x = ride->entrances[peep->current_ride_station] & 0xFF;
y = ride->entrances[peep->current_ride_station] >> 8;
z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 direction_entrance = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
if (ride->type == RIDE_TYPE_MAZE){
peep->maze_last_edge = direction_entrance + 1;
x *= 32;
y *= 32;
x += TileDirectionDelta[direction_entrance].x;
y += TileDirectionDelta[direction_entrance].y;
uint8 direction = direction_entrance * 4 + 11;
if (scenario_rand() & 0x40){
direction += 4;
peep->maze_last_edge += 2;
}
direction &= 0xF;
// Direction is 11, 15, 3, or 7
peep->var_37 = direction;
peep->maze_last_edge &= 3;
x += _981FD4[direction / 4].x;
y += _981FD4[direction / 4].y;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
ride->cur_num_customers++;
peep_on_enter_or_exit_ride(peep, peep->current_ride, 0);
peep->sub_state = 17;
return;
}
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
map_element = ride_get_station_start_track_element(ride, peep->current_ride_station);
uint8 direction_track = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
peep->var_37 = (direction_entrance << 2) | (direction_track << 4);
x *= 32;
y *= 32;
const rct_xy16 edx = _97e1bc[ride->type][peep->var_37];
x += edx.x;
y += edx.y;
peep->destination_x = x;
peep->destination_y = y;
peep->current_car = 0;
ride->cur_num_customers++;
peep_on_enter_or_exit_ride(peep, peep->current_ride, 0);
peep->sub_state = 14;
return;
}
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
for (int i = peep->current_car; i != 0; --i){
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
ride_entry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
if (vehicle_type->flags_b & VEHICLE_ENTRY_FLAG_B_10){
sint16 x, y, z;
x = ride->entrances[peep->current_ride_station] & 0xFF;
y = ride->entrances[peep->current_ride_station] >> 8;
z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 direction_entrance = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
map_element = ride_get_station_start_track_element(ride, peep->current_ride_station);
uint8 direction_track = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
ride_entry = get_ride_entry(vehicle->ride_subtype);
vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
uint8 cl = peep->current_seat;
uint8 ch = peep->current_seat & 0xF8;
if (ride->type != RIDE_TYPE_ENTERPRISE)
direction_track *= 2;
if (*vehicle_type->peep_loading_positions == 0){
direction_track /= 2;
cl = 0;
ch = 0;
}
cl += direction_track;
cl &= 0x7;
cl += ch;
peep->var_37 = (direction_entrance | cl * 4) * 4;
x *= 32;
y *= 32;
x += 16;
y += 16;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
x = vehicle->x;
y = vehicle->y;
}
x += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 1];
y += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 2];
peep->destination_x = x;
peep->destination_y = y;
peep->sub_state = 12;
return;
}
if (vehicle_type->flags_b & VEHICLE_ENTRY_FLAG_B_15){
peep->destination_x = vehicle->x;
peep->destination_y = vehicle->y;
peep->destination_tolerence = 15;
peep->sub_state = 4;
return;
}
sint8 load_position = vehicle_type->peep_loading_positions[peep->current_seat];
switch (vehicle->sprite_direction / 8){
case 0:
peep->destination_x = vehicle->x - load_position;
break;
case 1:
peep->destination_y = vehicle->y + load_position;
break;
case 2:
peep->destination_x = vehicle->x + load_position;
break;
case 3:
peep->destination_y = vehicle->y - load_position;
break;
}
peep->sub_state = 4;
return;
}
/**
*
* rct2: 0x0069321D
*/
static void peep_go_to_ride_exit(rct_peep* peep, rct_ride* ride, sint16 x, sint16 y, sint16 z, uint8 exit_direction){
z += RideData5[ride->type].z;
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry* ride_type = get_ride_entry(ride->subtype);
if (ride_type != NULL) {
rct_ride_entry_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle];
if (vehicle_entry->flags_a & VEHICLE_ENTRY_FLAG_A_MINI_GOLF ||
vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep->sprite_direction = exit_direction * 8;
peep->sub_state = 8;
return;
}
/**
*
* rct2: 0x006920B4
*/
static void peep_update_ride_sub_state_2_enter_ride(rct_peep* peep, rct_ride* ride)
{
money16 ridePrice = ride_get_price(ride);
if (ridePrice != 0) {
if ((peep->item_standard_flags & PEEP_ITEM_VOUCHER) &&
(peep->voucher_type == VOUCHER_TYPE_RIDE_FREE) &&
(peep->voucher_arguments == peep->current_ride)){
peep->item_standard_flags &= ~PEEP_ITEM_VOUCHER;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
else{
ride->total_profit += ridePrice;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_PARK_RIDE_TICKETS;
peep_spend_money(peep, &peep->paid_on_rides, ridePrice);
}
}
peep->sub_state++;
uint8 queue_time = peep->days_in_queue;
if (queue_time < 253)queue_time += 3;
queue_time /= 2;
if (queue_time != ride->queue_time[peep->current_ride_station]){
ride->queue_time[peep->current_ride_station] = queue_time;
window_invalidate_by_number(WC_RIDE, peep->current_ride);
}
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
rct_string_id msg_string;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE))
msg_string = STR_PEEP_TRACKING_PEEP_IS_IN_X;
else
msg_string = STR_PEEP_TRACKING_PEEP_IS_ON_X;
if (gConfigNotifications.guest_on_ride) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, msg_string, peep->sprite_index);
}
}
if (ride->type == RIDE_TYPE_SPIRAL_SLIDE){
sub_693BE5(peep, 1);
}
peep_update_ride_sub_state_1(peep);
}
/**
*
* rct2: 0x00691FD4
*/
static void peep_update_ride_sub_state_2_rejoin_queue(rct_peep* peep, rct_ride* ride){
sint16 x, y, z;
x = ride->entrances[peep->current_ride_station] & 0xFF;
y = ride->entrances[peep->current_ride_station] >> 8;
z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 direction_entrance = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
x *= 32;
y *= 32;
x += 16 - word_981D6C[direction_entrance].x * 20;
y += 16 - word_981D6C[direction_entrance].y * 20;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_QUEUING_FRONT;
peep->sub_state = 0;
peep_window_state_update(peep);
peep->next_in_queue = 0xFFFF;
ride->queue_length[peep->current_ride_station]++;
uint16 current_last = ride->last_peep_in_queue[peep->current_ride_station];
if (current_last == 0xFFFF){
ride->last_peep_in_queue[peep->current_ride_station] = peep->sprite_index;
return;
}
rct_peep* queue_peep;
for (queue_peep = GET_PEEP(current_last);
queue_peep->next_in_queue != 0xFFFF;
queue_peep = GET_PEEP(queue_peep->next_in_queue));
queue_peep->next_in_queue = peep->sprite_index;
}
/**
*
* rct2: 0x00691E42
* Note: Before this was the entry
* point for sub state 1 and 3. The
* check has been removed that would
* branch it out to 1 and 3. Now uses
* separate functions.
*/
static void peep_update_ride_sub_state_2(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_13)){
if (ride->status != RIDE_STATUS_OPEN ||
ride->vehicle_change_timeout != 0 ||
(++peep->var_AC) == 0){
peep_update_ride_sub_state_2_rejoin_queue(peep, ride);
return;
}
peep_update_ride_sub_state_2_enter_ride(peep, ride);
return;
}
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
for (int i = peep->current_car; i != 0; --i){
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
rct_ride_entry* ride_entry = get_ride_entry(vehicle->ride_subtype);
if (ride_entry->vehicles[0].flags_a & VEHICLE_ENTRY_FLAG_A_MINI_GOLF){
vehicle->mini_golf_flags &= ~(1 << 5);
for (int i = 0; i < ride->num_vehicles; ++i){
if (ride->vehicles[i] == 0xFFFF)
continue;
rct_vehicle* train = GET_VEHICLE(ride->vehicles[i]);
rct_vehicle* second_vehicle = GET_VEHICLE(train->next_vehicle_on_train);
if (second_vehicle->num_peeps == 0)
continue;
if (second_vehicle->mini_golf_flags & (1 << 5))
continue;
return;
}
}
if (!vehicle_is_used_in_pairs(vehicle)){
peep_update_ride_sub_state_2_enter_ride(peep, ride);
return;
}
if (ride->mode == RIDE_MODE_FORWARD_ROTATION ||
ride->mode == RIDE_MODE_BACKWARD_ROTATION){
if (peep->current_seat & 1 ||
!(vehicle->next_free_seat & 1)){
peep_update_ride_sub_state_2_enter_ride(peep, ride);
return;
}
}
else{
uint8 current_seat = (peep->current_seat & 0xFE) + 1;
if (current_seat < vehicle->next_free_seat)
{
peep_update_ride_sub_state_2_enter_ride(peep, ride);
return;
}
}
rct_vehicle *currentTrain = GET_VEHICLE(ride->vehicles[peep->current_train]);
if (ride->status == RIDE_STATUS_OPEN &&
++peep->var_AC != 0 &&
!(currentTrain->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART)){
return;
}
if (ride->mode != RIDE_MODE_FORWARD_ROTATION &&
ride->mode != RIDE_MODE_BACKWARD_ROTATION){
if (vehicle->next_free_seat - 1 != peep->current_seat)
return;
}
vehicle->next_free_seat--;
vehicle->peep[peep->current_seat] = 0xFFFF;
peep_update_ride_sub_state_2_rejoin_queue(peep, ride);
}
static void peep_update_ride_sub_state_5(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
for (int i = peep->current_car; i != 0; --i){
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
if (ride->mode != RIDE_MODE_FORWARD_ROTATION &&
ride->mode != RIDE_MODE_BACKWARD_ROTATION){
if (peep->current_seat != vehicle->num_peeps)
return;
}
if (vehicle_is_used_in_pairs(vehicle)){
rct_peep* seated_peep = GET_PEEP(vehicle->peep[peep->current_seat ^ 1]);
if (seated_peep->sub_state != 5)
return;
vehicle->num_peeps++;
ride->cur_num_customers++;
vehicle->friction += seated_peep->var_41;
invalidate_sprite_2((rct_sprite*)seated_peep);
sprite_move(0x8000, 0, 0, (rct_sprite*)seated_peep);
peep_decrement_num_riders(seated_peep);
seated_peep->state = PEEP_STATE_ON_RIDE;
peep_window_state_update(seated_peep);
seated_peep->time_on_ride = 0;
seated_peep->sub_state = 6;
peep_on_enter_or_exit_ride(seated_peep, peep->current_ride, 0);
}
vehicle->num_peeps++;
ride->cur_num_customers++;
vehicle->friction += peep->var_41;
invalidate_sprite_2((rct_sprite*)vehicle);
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(0x8000, 0, 0, (rct_sprite*)peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_ON_RIDE;
peep_window_state_update(peep);
peep->time_on_ride = 0;
peep->sub_state = 6;
peep_on_enter_or_exit_ride(peep, peep->current_ride, 0);
}
/**
*
* rct2: 0x00693028
*/
static void peep_update_ride_sub_state_7(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
uint8 ride_station = vehicle->current_station;
for (int i = peep->current_car; i != 0; --i){
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
// Check if ride is NOT Ferris Wheel.
if (ride->mode != RIDE_MODE_FORWARD_ROTATION &&
ride->mode != RIDE_MODE_BACKWARD_ROTATION){
if (vehicle->num_peeps - 1 != peep->current_seat)
return;
}
peep->action_sprite_image_offset++;
if (peep->action_sprite_image_offset & 3)
return;
peep->action_sprite_image_offset = 0;
vehicle->num_peeps--;
vehicle->friction -= peep->var_41;
invalidate_sprite_2((rct_sprite*)vehicle);
peep->current_ride_station = ride_station;
rct_ride_entry* ride_entry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle* vehicle_entry = &ride_entry->vehicles[vehicle->vehicle_type];
if (!(vehicle_entry->flags_b & VEHICLE_ENTRY_FLAG_B_10)){
sint16 x, y, z;
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
exit_direction ^= (1 << 1);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_16)){
for (; vehicle->is_child; vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride)){
uint16 trackType = vehicle->track_type >> 2;
if (trackType == TRACK_ELEM_FLAT || trackType > TRACK_ELEM_MIDDLE_STATION)
continue;
rct_map_element* inner_map = map_get_first_element_at(vehicle->track_x / 32, vehicle->track_y / 32);
for (;; inner_map++){
if (map_element_get_type(inner_map) != MAP_ELEMENT_TYPE_TRACK)
continue;
if (inner_map->base_height == vehicle->track_z / 8)
break;
}
uint8 al = (inner_map->properties.track.sequence & 0x70) >> 4;
if (al == peep->current_ride_station)
break;
}
uint8 shift_multiplier = 12;
uint8 direction = exit_direction;
ride_entry = get_ride_entry(ride->subtype);
if (ride_entry != NULL) {
vehicle_entry = &ride_entry->vehicles[ride_entry->default_vehicle];
if (vehicle_entry->flags_b & VEHICLE_ENTRY_FLAG_B_14){
shift_multiplier = 9;
}
if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){
direction = ((vehicle->sprite_direction + 3) / 8) + 1;
direction &= 3;
if (vehicle->var_CD == 6)
direction ^= (1 << 1);
}
}
sint16 x_shift = word_981D6C[direction].x;
sint16 y_shift = word_981D6C[direction].y;
x = vehicle->x + x_shift * shift_multiplier;
y = vehicle->y + y_shift * shift_multiplier;
z *= 8;
peep_go_to_ride_exit(peep, ride, x, y, z, exit_direction);
return;
}
x = vehicle->x + word_981D6C[exit_direction].x * 12;
y = vehicle->y + word_981D6C[exit_direction].y * 12;
sint8 load_position = vehicle_entry->peep_loading_positions[peep->current_seat];
switch (vehicle->sprite_direction / 8){
case 0:
x -= load_position;
break;
case 1:
y += load_position;
break;
case 2:
x += load_position;
break;
case 3:
y -= load_position;
break;
}
z = ride->station_heights[peep->current_ride_station] * 8;
peep_go_to_ride_exit(peep, ride, x, y, z, exit_direction);
return;
}
sint16 x, y, z;
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
map_element = ride_get_station_start_track_element(ride, peep->current_ride_station);
uint8 station_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
ride_entry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
uint8 cl = peep->current_seat;
uint8 ch = peep->current_seat & 0xF8;
if (ride->type != RIDE_TYPE_ENTERPRISE)
station_direction *= 2;
if (*vehicle_type->peep_loading_positions == 0){
station_direction /= 2;
cl = 0;
ch = 0;
}
cl += station_direction;
cl &= 0x7;
cl += ch;
peep->var_37 = ((exit_direction | cl * 4) * 4) | 1;
x *= 32;
y *= 32;
x += 16;
y += 16;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
x = vehicle->x;
y = vehicle->y;
}
sint16 exit_x = x + vehicle_type->peep_loading_positions[(peep->var_37 + 1) * 2 + 1];
sint16 exit_y = y + vehicle_type->peep_loading_positions[(peep->var_37 + 1) * 2 + 2];
z *= 8;
z += RideData5[ride->type].z;
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR)
z += 15;
sprite_move(exit_x, exit_y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
x += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 1];
y += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 2];
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep->sub_state = 13;
}
/**
*
* rct2: 0x0069376A
*/
static void peep_update_ride_prepare_for_state_9(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
sint16 x = ride->exits[peep->current_ride_station] & 0xFF;
sint16 y = ride->exits[peep->current_ride_station] >> 8;
sint16 z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry* ride_type = get_ride_entry(ride->subtype);
if (ride_type != NULL) {
rct_ride_entry_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle];
if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep->sub_state = 9;
}
/**
*
* rct2: 0x0069374F
*/
static void peep_update_ride_sub_state_8(rct_peep* peep){
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep_update_ride_prepare_for_state_9(peep);
}
/**
*
* rct2: 0x0069382E
*/
static void peep_update_ride_sub_state_9(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
if (xy_distance >= 16){
sint16 z = ride->station_heights[peep->current_ride_station] * 8;
z += RideData5[ride->type].z;
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
sub_693BE5(peep, 0);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO){
uint8 secondaryItem = RidePhotoItems[ride->type];
if (sub_69AF1E(peep, peep->current_ride, secondaryItem, ride->price_secondary)) {
ride->no_secondary_items_sold++;
}
}
peep->sub_state = 18;
}
/**
*
* rct2: 0x006926AD
*/
static void peep_update_ride_sub_state_12(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
if (peep_update_action(&x, &y, &xy_distance, peep)){
sint16 z;
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR){
z = ride->station_heights[peep->current_ride_station] * 8 + 2;
if ((peep->var_37 & 3) == 2){
xy_distance -= 12;
if (xy_distance < 0)
xy_distance = 0;
if (xy_distance <= 15){
z += 15 - xy_distance;
}
}
}
else{
z = peep->z;
}
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
if ((peep->var_37 & 3) == 2){
peep->sub_state = 5;
return;
}
peep->var_37++;
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
x += 16;
y += 16;
if (ride->type == RIDE_TYPE_ENTERPRISE){
x = vehicle->x;
y = vehicle->y;
}
rct_ride_entry* ride_entry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
x += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 1];
y += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 2];
peep->destination_x = x;
peep->destination_y = y;
}
/**
*
* rct2: 0x0069357D
*/
static void peep_update_ride_sub_state_13(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
if (peep_update_action(&x, &y, &xy_distance, peep)){
sint16 z;
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR){
z = ride->station_heights[peep->current_ride_station] * 8 + 2;
if ((peep->var_37 & 3) == 1){
if (xy_distance > 15)
xy_distance = 15;
z += xy_distance;
}
}
else{
z = peep->z;
}
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
if ((peep->var_37 & 3) != 0){
if ((peep->var_37 & 3) == 3){
peep_update_ride_prepare_for_state_9(peep);
return;
}
peep->var_37--;
rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
x += 16;
y += 16;
if (ride->type == RIDE_TYPE_ENTERPRISE){
x = vehicle->x;
y = vehicle->y;
}
rct_ride_entry* ride_entry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
x += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 1];
y += vehicle_type->peep_loading_positions[peep->var_37 * 2 + 2];
peep->destination_x = x;
peep->destination_y = y;
return;
}
peep->var_37 |= 3;
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
sint16 z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
exit_direction ^= (1 << 1);
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry* ride_type = get_ride_entry(ride->subtype);
rct_ride_entry_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle];
if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){
shift_multiplier = 32;
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
}
/**
*
* rct2: 0x006927B3
*/
static void peep_update_ride_sub_state_14(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
if ((peep->var_37 & 3) == 3){
peep->sub_state = 15;
peep->destination_x = 0;
peep->destination_y = 0;
peep->var_37 = (peep->var_37 / 4) & 0xC;
sprite_move(0x8000, y, peep->z, (rct_sprite*)peep);
return;
}
else if ((peep->var_37 & 3) == 2){
uint8 last_ride = 0;
if (ride->status != RIDE_STATUS_OPEN)
last_ride = 1;
else if (peep->current_car++ != 0){
if (ride->mode == RIDE_MODE_SINGLE_RIDE_PER_ADMISSION)
last_ride = 1;
if ((uint8)(peep->current_car - 1) > (scenario_rand() & 0xF))
last_ride = 1;
}
if (last_ride){
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
sint16 z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
peep->var_37 = (exit_direction * 4) | (peep->var_37 & 0x30) | 1;
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
const rct_xy16 edx = _97e1bc[ride->type][peep->var_37];
x += edx.x;
y += edx.y;
peep->destination_x = x;
peep->destination_y = y;
peep->sub_state = 16;
return;
}
}
peep->var_37++;
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
const rct_xy16 edx = _97e1bc[ride->type][peep->var_37];
x += edx.x;
y += edx.y;
peep->destination_x = x;
peep->destination_y = y;
}
/** rct2: 0x00981F0C, 0x00981F0E */
static const rct_xy16 _981F0C[] = {
{25, 56},
{56, 7},
{7, -24},
{-24, 25},
};
/** rct2: 0x00981F1C, 0x00981F1E */
static const rct_xy16 _981F1C[] = {
{8, 56},
{56, 24},
{24, -24},
{-24, 8},
};
/**
*
* rct2: 0x00692D83
*/
static void peep_update_ride_sub_state_15(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type != RIDE_TYPE_SPIRAL_SLIDE)
return;
if ((peep->var_37 & 3) == 0){
switch (peep->destination_x){
case 0:
peep->destination_y++;
if (peep->destination_y >= 30)
peep->destination_x++;
return;
case 1:
if (ride->slide_in_use != 0)
return;
ride->slide_in_use++;
ride->slide_peep = peep->sprite_index;
ride->slide_peep_t_shirt_colour = peep->tshirt_colour;
ride->spiral_slide_progress = 0;
peep->destination_x++;
return;
case 2:
return;
case 3:
{
sint16 x = ride->station_starts[peep->current_ride_station] & 0xFF;
sint16 y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
uint8 direction = (peep->var_37 / 4) & 3;
sint16 dest_x = x + _981F1C[direction].x;
sint16 dest_y = y + _981F1C[direction].y;
peep->destination_x = dest_x;
peep->destination_y = dest_y;
x += _981F0C[direction].x;
y += _981F0C[direction].y;
sprite_move(x, y, peep->z, (rct_sprite*)peep);
peep->sprite_direction = (peep->var_37 & 0xC) * 2;
invalidate_sprite_2((rct_sprite*)peep);
peep->var_37++;
return;
}
default:
return;
}
}
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep->var_37 = (peep->var_37 * 4 & 0x30) + 2;
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
const rct_xy16 edx = _97e1bc[ride->type][peep->var_37];
x += edx.x;
y += edx.y;
peep->destination_x = x;
peep->destination_y = y;
peep->sub_state = 14;
}
/**
*
* rct2: 0x00692C6B
*/
static void peep_update_ride_sub_state_16(rct_peep* peep){
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
rct_ride* ride = get_ride(peep->current_ride);
if ((peep->var_37 & 0x3) != 0){
if ((peep->var_37 & 0x3) == 3){
peep_update_ride_prepare_for_state_9(peep);
return;
}
peep->var_37--;
x = ride->station_starts[peep->current_ride_station] & 0xFF;
y = ride->station_starts[peep->current_ride_station] >> 8;
x *= 32;
y *= 32;
const rct_xy16 edx = _97e1bc[ride->type][peep->var_37];
x += edx.x;
y += edx.y;
peep->destination_x = x;
peep->destination_y = y;
return;
}
peep->var_37 |= 3;
x = ride->exits[peep->current_ride_station] & 0xFF;
y = ride->exits[peep->current_ride_station] >> 8;
sint16 z = ride->station_heights[peep->current_ride_station];
rct_map_element* map_element = ride_get_station_exit_element(ride, x, y, z);
uint8 exit_direction = (map_element == NULL ? 0 : map_element->type & MAP_ELEMENT_DIRECTION_MASK);
exit_direction ^= (1 << 1);
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
}
/** rct2: 0x00981FE4 */
static const uint8 _981FE4[][4] = {
{ 15, 7, 15, 7 },
{ 11, 3, 11, 3 },
{ 7, 15, 7, 15 },
{ 3, 11, 3, 11 },
};
/** rct2: 0x00981FF4 */
static const uint8 _981FF4[][4] = {
{ 1, 2, 14, 0 },
{ 4, 5, 6, 2 },
{ 6, 8, 9, 10 },
{ 14, 10, 12, 13 },
};
/**
*
* rct2: 0x00692A83
*/
static void peep_update_ride_sub_state_17(rct_peep* peep){
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
rct_ride* ride = get_ride(peep->current_ride);
if (peep->var_37 == 16){
peep_update_ride_prepare_for_state_9(peep);
return;
}
if (peep->action >= PEEP_ACTION_NONE_1){
if (peep->energy > 64 &&
(scenario_rand() & 0xFFFF) <= 2427){
peep->action = PEEP_ACTION_JUMP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
x = peep->destination_x & 0xFFE0;
y = peep->destination_y & 0xFFE0;
sint16 z = ride->station_heights[0];
// Find the station track element
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK && z == mapElement->base_height)
break;
} while (!map_element_is_last_for_tile(mapElement++));
uint16 maze_entry = mapElement->properties.track.maze_entry;
uint16 open_hedges = 0;
uint8 var_37 = peep->var_37;
// var_37 is 3, 7, 11 or 15
if (maze_entry & (1 << _981FF4[var_37 / 4][3])) {
open_hedges = 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][2])) {
open_hedges |= 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][1])) {
open_hedges |= 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][0])) {
open_hedges |= 1;
}
open_hedges ^= 0xF;
if (open_hedges == 0)
return;
uint8 maze_last_edge = peep->maze_last_edge ^ (1 << 1);
open_hedges &= ~(1 << maze_last_edge);
if (open_hedges == 0)
open_hedges |= (1 << maze_last_edge);
uint8 chosen_edge = scenario_rand() & 0x3;
while (!(open_hedges & (1 << chosen_edge))){
chosen_edge = (chosen_edge + 1) & 3;
}
x = TileDirectionDelta[chosen_edge].x / 2;
y = TileDirectionDelta[chosen_edge].y / 2;
x += peep->destination_x;
y += peep->destination_y;
uint8 type = 0;
mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (z != mapElement->base_height)
continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK){
type = 1;
break;
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_ENTRANCE &&
mapElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT){
type = 2;
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
switch (type){
case 0:
peep->maze_last_edge++;
peep->maze_last_edge &= 3;
return;
case 1:
peep->destination_x = x;
peep->destination_y = y;
peep->var_37 = _981FE4[peep->var_37 / 4][chosen_edge];
peep->maze_last_edge = chosen_edge;
break;
case 2:
x = peep->destination_x;
y = peep->destination_y;
if (chosen_edge & 1){
x &= 0xFFE0;
x += 16;
}
else{
y &= 0xFFE0;
y += 16;
}
peep->destination_x = x;
peep->destination_y = y;
peep->var_37 = 16;
peep->maze_last_edge = chosen_edge;
break;
}
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
}
/**
*
* rct2: 0x006938D2
*/
static void peep_update_ride_sub_state_18(rct_peep* peep){
sint16 x, y, xy_distance;
rct_ride* ride = get_ride(peep->current_ride);
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, ride->station_heights[peep->current_ride_station] * 8, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep_on_enter_or_exit_ride(peep, peep->current_ride, 1);
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
if (gConfigNotifications.guest_left_ride) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_LEFT_RIDE_X, peep->sprite_index);
}
}
peep->var_79 = 0xFF;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
x = peep->x & 0xFFE0;
y = peep->y & 0xFFE0;
// Find the station track element
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
sint16 z = map_height_from_slope(peep->x, peep->y, mapElement->properties.path.type);
z += mapElement->base_height * 8;
sint16 z_diff = peep->z - z;
if (z_diff > 0 || z_diff < -16)
continue;
sprite_move(peep->x, peep->y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
} while (!map_element_is_last_for_tile(mapElement++));
}
/**
*
* rct2: 0x0069299C
*/
static void peep_update_ride_sub_state_19(rct_peep* peep){
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep->sub_state++;
}
/**
*
* rct2: 0x006929BB
*/
static void peep_update_ride_sub_state_20(rct_peep* peep){
sint16 x, y;
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_FIRST_AID){
if (peep->nausea <= 35){
peep->sub_state++;
x = peep->next_x + 16;
y = peep->next_y + 16;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
peep->happiness_growth_rate = min(peep->happiness_growth_rate + 30, 0xFF);
peep->happiness = peep->happiness_growth_rate;
}
else{
peep->nausea--;
peep->nausea_growth_rate = peep->nausea;
}
return;
}
if (peep->bathroom != 0){
peep->bathroom--;
return;
}
// Do not play toilet flush sound on title screen as its considered loud and annoying
if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) {
audio_play_sound_at_location(SOUND_TOILET_FLUSH, peep->x, peep->y, peep->z);
}
peep->sub_state++;
x = peep->next_x + 16;
y = peep->next_y + 16;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
peep->happiness_growth_rate = min(peep->happiness_growth_rate + 30, 0xFF);
peep->happiness = peep->happiness_growth_rate;
peep_stop_purchase_thought(peep, ride->type);
}
/**
*
* rct2: 0x00692935
*/
static void peep_update_ride_sub_state_21(rct_peep* peep){
sint16 x, y, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
x = peep->x & 0xFFE0;
y = peep->y & 0xFFE0;
if (x != peep->next_x)
return;
if (y != peep->next_y)
return;
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
rct_ride* ride = get_ride(peep->current_ride);
ride->total_customers++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER;
ride_update_satisfaction(ride, peep->happiness / 64);
}
/**
*
* rct2: 0x691A30
* Used by entering_ride and queueing_front */
static void peep_update_ride(rct_peep* peep){
switch (peep->sub_state){
case 0:
peep_update_ride_sub_state_0(peep);
break;
case 1:
peep_update_ride_sub_state_1(peep);
break;
case 2:
peep_update_ride_sub_state_2(peep);
break;
case 3:
peep_update_ride_sub_state_1(peep);
break;
case 4:
{
sint16 x, y, xy_distance;
if (!peep_update_action(&x, &y, &xy_distance, peep))
{
peep->sub_state = 5;
break;
}
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
break;
}
case 5:
peep_update_ride_sub_state_5(peep);
break;
case 6:
// No action, on ride.
break;
case 7:
peep_update_ride_sub_state_7(peep);
break;
case 8:
peep_update_ride_sub_state_8(peep);
break;
case 9:
peep_update_ride_sub_state_9(peep);
break;
case 10:
case 11:
assert(false);
break;
case 12:
peep_update_ride_sub_state_12(peep);
break;
case 13:
peep_update_ride_sub_state_13(peep);
break;
case 14:
peep_update_ride_sub_state_14(peep);
break;
case 15:
peep_update_ride_sub_state_15(peep);
break;
case 16:
peep_update_ride_sub_state_16(peep);
break;
case 17:
peep_update_ride_sub_state_17(peep);
break;
case 18:
peep_update_ride_sub_state_18(peep);
break;
case 19:
peep_update_ride_sub_state_19(peep);
break;
case 20:
peep_update_ride_sub_state_20(peep);
break;
case 21:
peep_update_ride_sub_state_21(peep);
break;
default:
// Invalid peep sub-state
assert(false);
break;
}
}
static const uint32 loc_992A18[9] = {
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 2) | (1 << 1),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 4) | (1 << 1),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 3) | (1 << 1),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 5) | (1 << 1),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 6) | (1 << 1),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 11)| (1 << 9),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7),
(1 << 14) | (1 << 13) | (1 << 12) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7),
};
/**
*
* rct2: 0x006C0E8B
* Also used by inspecting.
*/
static void peep_update_fixing(int steps, rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_NULL)
{
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
bool progressToNextSubstate = true;
bool firstRun = true;
while (progressToNextSubstate) {
switch (peep->sub_state) {
case 0:
progressToNextSubstate = peep_update_fixing_sub_state_0(ride);
break;
case 1:
progressToNextSubstate = peep_update_fixing_sub_state_1(firstRun, peep, ride);
break;
case 2:
case 3:
case 4:
case 5:
progressToNextSubstate = peep_update_fixing_sub_state_2345(firstRun, peep, ride);
break;
case 6:
progressToNextSubstate = peep_update_fixing_sub_state_6(firstRun, peep, ride);
break;
case 7:
progressToNextSubstate = peep_update_fixing_sub_state_7(firstRun, peep, ride);
break;
case 8:
progressToNextSubstate = peep_update_fixing_sub_state_8(firstRun, peep);
break;
case 9:
progressToNextSubstate = peep_update_fixing_sub_state_9(firstRun, peep, ride);
break;
case 10:
progressToNextSubstate = peep_update_fixing_sub_state_10(firstRun, peep, ride);
break;
case 11:
progressToNextSubstate = peep_update_fixing_sub_state_11(firstRun, peep, ride);
break;
case 12:
progressToNextSubstate = peep_update_fixing_sub_state_12(firstRun, peep, ride);
break;
case 13:
progressToNextSubstate = peep_update_fixing_sub_state_13(firstRun, steps, peep, ride);
break;
case 14:
progressToNextSubstate = peep_update_fixing_sub_state_14(firstRun, peep, ride);
break;
default:
log_error("Invalid substate");
progressToNextSubstate = false;
}
firstRun = false;
if (!progressToNextSubstate) {
break;
}
int subState = peep->sub_state;
uint32 ebp = loc_992A18[8];
if (peep->state != PEEP_STATE_INSPECTING) {
ebp = loc_992A18[ride->breakdown_reason_pending];
}
do {
subState++;
} while ((ebp & (1 << subState)) == 0);
peep->sub_state = subState & 0xFF;
}
}
/**
* rct2: 0x006C0EEC
*/
static bool peep_update_fixing_sub_state_0(rct_ride *ride) {
ride->mechanic_status = RIDE_MECHANIC_STATUS_FIXING;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE;
return true;
}
/**
* rct2: 0x006C0F09
*/
static bool peep_update_fixing_sub_state_1(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 x, y, tmp_xy_distance;
if (!firstRun) {
rct_vehicle *vehicle = ride_get_broken_vehicle(ride);
if (vehicle == NULL) {
return true;
}
while (true) {
if (vehicle->is_child == 0) {
break;
}
uint8 trackType = vehicle->track_type >> 2;
if (trackType == TRACK_ELEM_END_STATION) {
break;
}
if (trackType == TRACK_ELEM_BEGIN_STATION) {
break;
}
if (trackType == TRACK_ELEM_MIDDLE_STATION) {
break;
}
vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride);
}
rct_xy16 offset = word_981D6C[peep->direction];
peep->destination_x = (offset.x * -12) + vehicle->x;
peep->destination_y = (offset.y * -12) + vehicle->y;
peep->destination_tolerence = 2;
}
invalidate_sprite_2((rct_sprite *) peep);
if (peep_update_action(&x, &y, &tmp_xy_distance, peep)) {
sprite_move(x, y, peep->z, (rct_sprite *) peep);
invalidate_sprite_2((rct_sprite *) peep);
return false;
}
return true;
}
/**
* rct2: 0x006C0FD3
*/
static bool peep_update_fixing_sub_state_2345(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 tmp_x, tmp_y, tmp_distance;
if (!firstRun) {
peep->sprite_direction = peep->direction << 3;
peep->action = (scenario_rand() & 1) ? PEEP_ACTION_STAFF_FIX_2 : PEEP_ACTION_STAFF_FIX;
peep->action_sprite_image_offset = 0;
peep->action_frame = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action == PEEP_ACTION_NONE_2) {
return true;
}
peep_update_action(&tmp_x, &tmp_y, &tmp_distance, peep);
uint8 actionFrame = (peep->action == PEEP_ACTION_STAFF_FIX) ? 0x25 : 0x50;
if (peep->action_frame != actionFrame) {
return false;
}
rct_vehicle *vehicle = ride_get_broken_vehicle(ride);
if (vehicle == NULL) {
return true;
}
vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_CAR;
return false;
}
/**
* rct2: 0x006C107B
*/
static bool peep_update_fixing_sub_state_6(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 tmp_x, tmp_y, tmp_distance;
if (!firstRun) {
peep->sprite_direction = peep->direction << 3;
peep->action = PEEP_ACTION_STAFF_FIX_3;
peep->action_sprite_image_offset = 0;
peep->action_frame = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action == PEEP_ACTION_NONE_2) {
return true;
}
peep_update_action(&tmp_x, &tmp_y, &tmp_distance, peep);
if (peep->action_frame != 0x65) {
return false;
}
rct_vehicle *vehicle = ride_get_broken_vehicle(ride);
if (vehicle == NULL) {
return true;
}
vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_TRAIN;
return false;
}
/** rct2: 0x00992A3C */
static const rct_xy16 _992A3C[] = {
{ -12, 0 },
{ 0, 12 },
{ 12, 0 },
{ 0, -12 },
};
/**
* rct2: 0x006C1114
*/
static bool peep_update_fixing_sub_state_7(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 x, y, tmp_distance;
if (!firstRun) {
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3 | RIDE_TYPE_FLAG_HAS_NO_TRACK)) {
return true;
}
uint16 stationPosition = ride->station_starts[peep->current_ride_station];
if (stationPosition == 0xFFFF) {
return true;
}
uint8 stationZ = ride->station_heights[peep->current_ride_station];
uint16 stationX = (stationPosition & 0xFF) * 32;
uint16 stationY = (stationPosition >> 8) * 32;
rct_map_element *mapElement = map_get_track_element_at(stationX, stationY, stationZ);
if (mapElement == NULL) {
log_error("Couldn't find map_element");
return false;
}
int direction = map_element_get_direction(mapElement);
rct_xy16 offset = _992A3C[direction];
stationX += 16 + offset.x;
if (offset.x == 0) {
stationX = peep->destination_x;
}
stationY += 16 + offset.y;
if (offset.y == 0) {
stationY = peep->destination_y;
}
peep->destination_x = stationX;
peep->destination_y = stationY;
peep->destination_tolerence = 2;
}
invalidate_sprite_2((rct_sprite *) peep);
if (!peep_update_action(&x, &y, &tmp_distance, peep)) {
return true;
}
sprite_move(x, y, peep->z, (rct_sprite *) peep);
invalidate_sprite_2((rct_sprite *) peep);
return false;
}
/**
* rct2: 0x006C11F5
*/
static bool peep_update_fixing_sub_state_8(bool firstRun, rct_peep *peep) {
sint16 tmp_x, tmp_y, tmp_xy_distance;
if (!firstRun) {
peep->sprite_direction = peep->direction << 3;
peep->action = PEEP_ACTION_STAFF_CHECKBOARD;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action == PEEP_ACTION_NONE_2) {
return true;
}
peep_update_action(&tmp_x, &tmp_y, &tmp_xy_distance, peep);
return false;
}
/**
* rct2: 0x006C1239
*/
static bool peep_update_fixing_sub_state_9(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 x, y, tmp_xy_distance;
if (!firstRun) {
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3 | RIDE_TYPE_FLAG_HAS_NO_TRACK)) {
return true;
}
uint16 stationPosition = ride->station_starts[peep->current_ride_station];
if (stationPosition == 0xFFFF) {
return true;
}
uint8 stationZ = ride->station_heights[peep->current_ride_station];
rct_xy_element input;
input.x = (stationPosition & 0xFF) * 32;
input.y = (stationPosition >> 8) * 32;
input.element = map_get_track_element_at_from_ride(input.x, input.y, stationZ, peep->current_ride);
if (input.element == NULL) {
return true;
}
uint8 direction = 0;
track_begin_end trackBeginEnd;
while (track_block_get_previous(input.x, input.y, input.element, &trackBeginEnd)) {
uint8 trackType = trackBeginEnd.begin_element->properties.track.type;
if (trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION || trackType == TRACK_ELEM_END_STATION) {
input.x = trackBeginEnd.begin_x;
input.y = trackBeginEnd.begin_y;
input.element = trackBeginEnd.begin_element;
direction = trackBeginEnd.begin_element->type & 3;
continue;
}
break;
}
// loc_6C12ED:
uint16 destinationX = input.x + 16;
uint16 destinationY = input.y + 16;
rct_xy16 offset = _992A3C[direction];
destinationX -= offset.x;
if (offset.x == 0) {
destinationX = peep->destination_x;
}
destinationY -= offset.y;
if (offset.y == 0) {
destinationY = peep->destination_y;
}
peep->destination_x = destinationX;
peep->destination_y = destinationY;
peep->destination_tolerence = 2;
}
invalidate_sprite_2((rct_sprite *) peep);
if (!peep_update_action(&x, &y, &tmp_xy_distance, peep)) {
return true;
}
sprite_move(x, y, peep->z, (rct_sprite *) peep);
invalidate_sprite_2((rct_sprite *) peep);
return false;
}
/**
* rct2: 0x006C1368
*/
static bool peep_update_fixing_sub_state_10(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 tmp_x, tmp_y, tmp_xy_distance;
if (!firstRun) {
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3 | RIDE_TYPE_FLAG_HAS_NO_TRACK)) {
return true;
}
peep->sprite_direction = peep->direction << 3;
peep->action = PEEP_ACTION_STAFF_FIX;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action == PEEP_ACTION_NONE_2) {
return true;
}
peep_update_action(&tmp_x, &tmp_y, &tmp_xy_distance, peep);
return false;
}
/**
* rct2: 0x006C13CE
*/
static bool peep_update_fixing_sub_state_11(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 tmp_x, tmp_y, tmp_xy_distance;
if (!firstRun) {
peep->sprite_direction = peep->direction << 3;
peep->action = PEEP_ACTION_STAFF_FIX_GROUND;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action == PEEP_ACTION_NONE_2) {
return true;
}
peep_update_action(&tmp_x, &tmp_y, &tmp_xy_distance, peep);
if (peep->action_frame == 0x28) {
ride->mechanic_status = 4;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE;
}
if (peep->action_frame == 0x13 ||
peep->action_frame == 0x19 ||
peep->action_frame == 0x1F ||
peep->action_frame == 0x25 ||
peep->action_frame == 0x2B) {
audio_play_sound_at_location(SOUND_MECHANIC_FIX, peep->x, peep->y, peep->z);
}
return false;
}
/**
* rct2: 0x006C1474
*/
static bool peep_update_fixing_sub_state_12(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 x, y, tmp_xy_distance;
if (!firstRun) {
uint16 stationPosition = ride->exits[peep->current_ride_station];
if (stationPosition == 0xFFFF) {
stationPosition = ride->entrances[peep->current_ride_station];
if (stationPosition == 0xFFFF) {
return true;
}
}
uint16 stationX = (stationPosition & 0xFF) * 32;
uint16 stationY = (stationPosition >> 8) * 32;
stationX += 16;
stationY += 16;
rct_xy16 direction = word_981D6C[peep->direction];
stationX += direction.x * 20;
stationY += direction.y * 20;
peep->destination_x = stationX;
peep->destination_y = stationY;
peep->destination_tolerence = 2;
}
invalidate_sprite_2((rct_sprite *) peep);
if (peep_update_action(&x, &y, &tmp_xy_distance, peep) == 0) {
return true;
} else {
sprite_move(x, y, peep->z, (rct_sprite *) peep);
invalidate_sprite_2((rct_sprite *) peep);
}
return false;
}
/**
* rct2: 0x006C1504
*/
static bool peep_update_fixing_sub_state_13(bool firstRun, int steps, rct_peep *peep, rct_ride *ride) {
sint16 tmp_x, tmp_y, tmp_xy_distance;
if (!firstRun) {
if (peep->state == PEEP_STATE_INSPECTING) {
sub_6B7588(peep->current_ride);
peep->staff_rides_inspected++;
peep->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME | RIDE_INVALIDATE_RIDE_LIST;
return true;
}
peep->staff_rides_fixed++;
peep->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME | RIDE_INVALIDATE_RIDE_LIST;
peep->sprite_direction = peep->direction << 3;
peep->action = PEEP_ACTION_STAFF_ANSWER_CALL_2;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite *) peep);
}
if (peep->action != 0xFF) {
peep_update_action(&tmp_x, &tmp_y, &tmp_xy_distance, peep);
return false;
}
ride_fix_breakdown(peep->current_ride, steps);
return true;
}
/**
* rct2: 0x006C157E
*/
static bool peep_update_fixing_sub_state_14(bool firstRun, rct_peep *peep, rct_ride *ride) {
sint16 x, y, xy_distance;
if (!firstRun) {
uint16 exitPosition = ride->exits[peep->current_ride_station];
if (exitPosition == 0xFFFF) {
exitPosition = ride->entrances[peep->current_ride_station];
if (exitPosition == 0xFFFF) {
peep_decrement_num_riders(peep);
peep->state = 0;
peep_window_state_update(peep);
return false;
}
}
uint16 exitX = (exitPosition & 0xFF) * 32;
uint16 exitY = (exitPosition >> 8) * 32;
exitX += 16;
exitY += 16;
rct_xy16 ebx_direction = word_981D6C[peep->direction];
exitX -= ebx_direction.x * 19;
exitY -= ebx_direction.y * 19;
peep->destination_x = exitX;
peep->destination_y = exitY;
peep->destination_tolerence = 2;
}
invalidate_sprite_2((rct_sprite *) peep);
if (peep_update_action(&x, &y, &xy_distance, peep) == 0) {
peep_decrement_num_riders(peep);
peep->state = 0;
peep_window_state_update(peep);
return false;
}
uint16 z = ride->station_heights[peep->current_ride_station] * 8;
if (xy_distance >= 16) {
z += RideData5[ride->type].z;
}
sprite_move(x, y, z, (rct_sprite *) peep);
invalidate_sprite_2((rct_sprite *) peep);
return false;
}
/**
* rct2: 0x6B7588
*/
static void sub_6B7588(int rideIndex) {
rct_ride *ride = get_ride(rideIndex);
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION;
ride->reliability += ((100 - (ride->reliability >> 8)) >> 2) * (scenario_rand() & 0xFF);
ride->last_inspection = 0;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE | RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
}
/**
*
* rct2: 0x69185D
*/
static void peep_update_queuing(rct_peep* peep){
if (!checkForPath(peep)){
remove_peep_from_queue(peep);
return;
}
rct_ride* ride = get_ride(peep->current_ride);
if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
return;
}
if (peep->sub_state != 10){
if (peep->next_in_queue == 0xFFFF){
//Happens every time peep goes onto ride.
peep->destination_tolerence = 0;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_QUEUING_FRONT;
peep_window_state_update(peep);
peep->sub_state = 0;
return;
}
//Give up queueing for the ride
peep->sprite_direction ^= (1 << 4);
invalidate_sprite_2((rct_sprite*)peep);
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
return;
}
sub_693C9E(peep);
if (peep->action < 0xFE)return;
if (peep->sprite_type == PEEP_SPRITE_TYPE_NORMAL) {
if (peep->time_in_queue >= 2000 && (0xFFFF & scenario_rand()) <= 119){
// Eat Food/Look at watch
peep->action = PEEP_ACTION_EAT_FOOD;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
if (peep->time_in_queue >= 3500 && (0xFFFF & scenario_rand()) <= 93)
{
//Create the I have been waiting in line ages thought
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_QUEUING_AGES, peep->current_ride);
}
}
else{
if (!(peep->time_in_queue & 0x3F) && peep->action == 0xFE && peep->next_action_sprite_type == 2){
switch (peep->sprite_type){
case 0xF:
case 0x10:
case 0x11:
case 0x12:
case 0x14:
case 0x16:
case 0x18:
case 0x1F:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x27:
case 0x29:
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2D:
case 0x2E:
case 0x2F:
// Eat food/Look at watch
peep->action = PEEP_ACTION_EAT_FOOD;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
break;
}
}
}
if (peep->time_in_queue < 4300) return;
if (peep->happiness <= 65 && (0xFFFF & scenario_rand()) < 2184){
//Give up queueing for the ride
peep->sprite_direction ^= (1 << 4);
invalidate_sprite_2((rct_sprite*)peep);
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
}
}
/** rct2: 0x009929C8 */
static const rct_xy16 _9929C8[] = {
{ 28, 28 },
{ 28, 4 },
{ 20, 4 },
{ 20, 28 },
{ 12, 28 },
{ 12, 4 },
{ 4, 4 },
{ 4, 28 },
};
/**
*
* rct2: 0x006BF567
*/
static void peep_update_mowing(rct_peep* peep){
if (!checkForPath(peep))return;
invalidate_sprite_2((rct_sprite*)peep);
while (1){
sint16 x = 0, y = 0, z, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
z = map_element_height(x, y) & 0xFFFF;
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep->var_37++;
if (peep->var_37 == 1){
sub_693BE5(peep, 2);
}
if (peep->var_37 == countof(_9929C8)) {
peep_state_reset(peep);
return;
}
peep->destination_x = _9929C8[peep->var_37].x + peep->next_x;
peep->destination_y = _9929C8[peep->var_37].y + peep->next_y;
if (peep->var_37 != 7)continue;
rct_map_element *map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (; (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SURFACE); map_element++);
if ((map_element->properties.surface.terrain & MAP_ELEMENT_SURFACE_TERRAIN_MASK) == (TERRAIN_GRASS << 5)){
map_element->properties.surface.grass_length = 0;
map_invalidate_tile_zoom0(peep->next_x, peep->next_y, map_element->base_height * 8, map_element->base_height * 8 + 16);
}
peep->staff_lawns_mown++;
peep->window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS;
}
}
/**
*
* rct2: 0x006BF7E6
*/
static void peep_update_watering(rct_peep* peep){
peep->var_E2 = 0;
if (peep->sub_state == 0){
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
peep->sprite_direction = (peep->var_37 & 3) << 3;
peep->action = PEEP_ACTION_STAFF_WATERING;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
peep->sub_state = 1;
}
else if (peep->sub_state == 1){
if (peep->action != PEEP_ACTION_NONE_2){
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
return;
}
int x = peep->next_x + TileDirectionDelta[peep->var_37].x;
int y = peep->next_y + TileDirectionDelta[peep->var_37].y;
rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32);
do{
if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY)
continue;
if (abs(((int)peep->next_z) - map_element->base_height) > 4)
continue;
rct_scenery_entry* scenery_entry = get_small_scenery_entry(map_element->properties.scenery.type);
if (!(scenery_entry->small_scenery.flags & SMALL_SCENERY_FLAG_CAN_BE_WATERED))
continue;
map_element->properties.scenery.age = 0;
map_invalidate_tile_zoom0(x, y, map_element->base_height * 8, map_element->clearance_height * 8);
peep->staff_gardens_watered++;
peep->window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS;
} while (!map_element_is_last_for_tile(map_element++));
peep_state_reset(peep);
}
}
/**
*
* rct2: 0x006BF6C9
*/
static void peep_update_emptying_bin(rct_peep* peep){
peep->var_E2 = 0;
if (peep->sub_state == 0){
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
peep->sprite_direction = (peep->var_37 & 3) << 3;
peep->action = PEEP_ACTION_STAFF_EMPTY_BIN;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
peep->sub_state = 1;
}
else if (peep->sub_state == 1){
if (peep->action == PEEP_ACTION_NONE_2){
peep_state_reset(peep);
return;
}
sint16 x = 0, y = 0, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
if (peep->action_frame != 11)return;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;; map_element++) {
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH) {
if (peep->next_z == map_element->base_height)
break;
}
if (map_element_is_last_for_tile(map_element)) {
peep_state_reset(peep);
return;
}
}
if (!footpath_element_has_path_scenery(map_element)) {
peep_state_reset(peep);
return;
}
rct_scenery_entry* scenery_entry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (
!(scenery_entry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)
|| map_element->flags & (1 << 5)
|| footpath_element_path_scenery_is_ghost(map_element)
) {
peep_state_reset(peep);
return;
}
map_element->properties.path.addition_status |= ((3 << peep->var_37) << peep->var_37);
map_invalidate_tile_zoom0(peep->next_x, peep->next_y, map_element->base_height * 8, map_element->clearance_height * 8);
peep->staff_bins_emptied++;
peep->window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS;
}
}
/**
*
* rct2: 0x6BF641
*/
static void peep_update_sweeping(rct_peep* peep){
peep->var_E2 = 0;
if (!checkForPath(peep))return;
invalidate_sprite_2((rct_sprite*)peep);
if (peep->action == PEEP_ACTION_STAFF_SWEEP && peep->action_frame == 8){
// Remove sick at this location
litter_remove_at(peep->x, peep->y, peep->z);
peep->staff_litter_swept++;
peep->window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS;
}
sint16 x = 0, y = 0, z, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
z = peep_get_height_on_slope(peep, x, y);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep->var_37++;
if (peep->var_37 != 2){
peep->action = PEEP_ACTION_STAFF_SWEEP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep_state_reset(peep);
}
/**
*
* rct2: 0x6902A2
*/
static void peep_update_1(rct_peep* peep){
if (!checkForPath(peep))return;
peep_decrement_num_riders(peep);
if (peep->type == PEEP_TYPE_GUEST){
peep->state = PEEP_STATE_WALKING;
}
else{
peep->state = PEEP_STATE_PATROLLING;
}
peep_window_state_update(peep);
peep->destination_x = peep->x;
peep->destination_y = peep->y;
peep->destination_tolerence = 10;
peep->var_76 = 0;
peep->direction = peep->sprite_direction >> 3;
}
/**
*
* rct2: 0x690009
*/
static void peep_update_picked(rct_peep* peep){
if (gCurrentTicks & 0x1F) return;
peep->sub_state++;
if (peep->sub_state == 13){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_HELP, 0xFF);
}
}
/**
*
* rct2: 0x6914CD
*/
static void peep_update_leaving_park(rct_peep* peep){
if (peep->var_37 != 0){
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_OUTSIDE_PARK)) return;
peep_sprite_remove(peep);
return;
}
sint16 x = 0, y = 0, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep->outside_of_park = 1;
peep->destination_tolerence = 5;
gNumGuestsInPark--;
gToolbarDirtyFlags |= BTM_TB_DIRTY_FLAG_PEEP_COUNT;
peep->var_37 = 1;
window_invalidate_by_class(WC_GUEST_LIST);
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_OUTSIDE_PARK)) return;
peep_sprite_remove(peep);
}
/**
*
* rct2: 0x6916D6
*/
static void peep_update_watching(rct_peep* peep){
if (peep->sub_state == 0){
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
peep->destination_x = peep->x;
peep->destination_y = peep->y;
peep->sprite_direction = (peep->var_37 & 3) * 8;
invalidate_sprite_2((rct_sprite*)peep);
peep->action = 0xFE;
peep->next_action_sprite_type = 2;
sub_693BAB(peep);
peep->sub_state++;
peep->time_to_stand = clamp(0, ((129 - peep->energy) * 16 + 50) / 2, 255);
peep_update_sprite_type(peep);
}
else if (peep->sub_state == 1){
if (peep->action < 0xFE){
//6917F6
sint16 x = 0, y = 0, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
if (peep->action != 0xFF)return;
peep->action = 0xFE;
}
else{
if (peep_has_food(peep)){
if ((scenario_rand() & 0xFFFF) <= 1310){
peep->action = PEEP_ACTION_EAT_FOOD;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
}
if ((scenario_rand() & 0xFFFF) <= 655){
peep->action = PEEP_ACTION_TAKE_PHOTO;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
if ((peep->standing_flags & 1)){
if ((scenario_rand() & 0xFFFF) <= 655){
peep->action = PEEP_ACTION_WAVE;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
}
}
peep->standing_flags ^= (1 << 7);
if (!(peep->standing_flags & (1 << 7)))return;
peep->time_to_stand--;
if (peep->time_to_stand != 0)return;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
peep_update_sprite_type(peep);
// Send peep to the center of current tile.
peep->destination_x = (peep->x & 0xFFE0) + 16;
peep->destination_y = (peep->y & 0xFFE0) + 16;
peep->destination_tolerence = 5;
sub_693B58(peep);
}
}
/**
* rct2: 0x691451
*/
static void peep_update_entering_park(rct_peep* peep){
if (peep->var_37 != 1){
sub_693C9E(peep);
if ((_unk_F1EE18 & F1EE18_OUTSIDE_PARK)) {
gNumGuestsHeadingForPark--;
peep_sprite_remove(peep);
}
return;
}
sint16 x = 0, y = 0, xy_distance;
if (peep_update_action(&x, &y, &xy_distance, peep)){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return;
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
peep->outside_of_park = 0;
peep->time_in_park = gScenarioTicks;
gNumGuestsInPark++;
gNumGuestsHeadingForPark--;
gToolbarDirtyFlags |= BTM_TB_DIRTY_FLAG_PEEP_COUNT;
window_invalidate_by_class(WC_GUEST_LIST);
}
/**
*
* rct2: 0x00690582
*/
static int peep_update_walking_find_bench(rct_peep* peep){
if (!peep_should_find_bench(peep))return 0;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;; map_element++){
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH){
if (peep->next_z == map_element->base_height)break;
}
if (map_element_is_last_for_tile(map_element)){
return 0;
}
}
if (!footpath_element_has_path_scenery(map_element)) return 0;
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BENCH))return 0;
if (map_element->flags & MAP_ELEMENT_FLAG_BROKEN)return 0;
if (footpath_element_path_scenery_is_ghost(map_element)) return 0;
int edges = (map_element->properties.path.edges & 0xF) ^ 0xF;
if (edges == 0) return 0;
uint8 chosen_edge = scenario_rand() & 0x3;
for (; !(edges & (1 << chosen_edge));)chosen_edge = (chosen_edge + 1) & 0x3;
uint16 sprite_id = sprite_get_first_in_quadrant(peep->x, peep->y);
uint8 free_edge = 3;
for (rct_sprite* sprite; sprite_id != SPRITE_INDEX_NULL; sprite_id = sprite->unknown.next_in_quadrant){
sprite = get_sprite(sprite_id);
if (sprite->unknown.linked_list_type_offset != SPRITE_LIST_PEEP * 2) continue;
if (sprite->peep.state != PEEP_STATE_SITTING)continue;
if (peep->z != sprite->peep.z)continue;
if ((sprite->peep.var_37 & 0x3) != chosen_edge)continue;
free_edge &= ~(1 << ((sprite->peep.var_37 & 0x4) >> 2));
}
if (!free_edge) return 0;
free_edge ^= 0x3;
if (!free_edge){
if (scenario_rand() & 0x8000000) free_edge = 1;
}
peep->var_37 = ((free_edge & 1) << 2) | chosen_edge;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_SITTING;
peep_window_state_update(peep);
peep->sub_state = 0;
int ebx = peep->var_37 & 0x7;
int x = (peep->x & 0xFFE0) + _981F2C[ebx].x;
int y = (peep->y & 0xFFE0) + _981F2C[ebx].y;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
return 1;
}
/** rct2: 0x00992A4C */
static const rct_xy16 _992A4C[] = {
{ 11, 16 },
{ 16, 21 },
{ 21, 16 },
{ 16, 11 },
};
static int peep_update_walking_find_bin(rct_peep* peep){
if (!peep_has_empty_container(peep)) return 0;
if (peep->next_var_29 & 0x18)return 0;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;; map_element++){
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH){
if (peep->next_z == map_element->base_height)break;
}
if (map_element_is_last_for_tile(map_element)){
return 0;
}
}
if (!footpath_element_has_path_scenery(map_element)) return 0;
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN))return 0;
if (map_element->flags & MAP_ELEMENT_FLAG_BROKEN)return 0;
if (footpath_element_path_scenery_is_ghost(map_element)) return 0;
int edges = (map_element->properties.path.edges & 0xF) ^ 0xF;
if (edges == 0) return 0;
uint8 chosen_edge = scenario_rand() & 0x3;
// Note: Bin quantity is inverted 0 = full, 3 = empty
uint8 bin_quantities = map_element->properties.path.addition_status;
// Rotate the bin to the correct edge. Makes it easier for next calc.
bin_quantities = ror8(ror8(bin_quantities, chosen_edge), chosen_edge);
for (uint8 free_edge = 4; free_edge != 0; free_edge--){
// If not full
if (bin_quantities & 0x3){
if (edges&(1 << chosen_edge))break;
}
chosen_edge = (chosen_edge + 1) & 0x3;
bin_quantities = ror8(bin_quantities, 2);
if ((free_edge - 1) == 0) return 0;
}
peep->var_37 = chosen_edge;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_USING_BIN;
peep_window_state_update(peep);
peep->sub_state = 0;
int ebx = peep->var_37 & 0x3;
int x = (peep->x & 0xFFE0) + _992A4C[ebx].x;
int y = (peep->y & 0xFFE0) + _992A4C[ebx].y;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
return 1;
}
/**
*
* rct2: 0x00690848
*/
static void peep_update_walking_break_scenery(rct_peep* peep){
if(gCheatsDisableVandalism)
return;
if (!(peep->peep_flags & PEEP_FLAGS_ANGRY)){
if (peep->happiness >= 48) return;
if (peep->energy < 85) return;
if (peep->state != PEEP_STATE_WALKING) return;
if ((peep->litter_count & 0xC0) != 0xC0 &&
(peep->disgusting_count & 0xC0) != 0xC0) return;
if ((scenario_rand() & 0xFFFF) > 3276) return;
}
if (peep->next_var_29 & 0x18) return;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;; map_element++){
if ( map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH){
if (peep->next_z == map_element->base_height)break;
}
if (map_element_is_last_for_tile(map_element)){
return;
}
}
if (!footpath_element_has_path_scenery(map_element)) return;
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_BREAKABLE))return;
if (map_element->flags & MAP_ELEMENT_FLAG_BROKEN)return;
if (footpath_element_path_scenery_is_ghost(map_element))return;
int edges = map_element->properties.path.edges & 0xF;
if (edges == 0xF) return;
rct_peep* inner_peep;
uint16 sprite_index;
FOR_ALL_STAFF(sprite_index, inner_peep){
if (inner_peep->staff_type != STAFF_TYPE_SECURITY) continue;
if (inner_peep->x == SPRITE_LOCATION_NULL) continue;
int x_diff = abs(inner_peep->x - peep->x);
int y_diff = abs(inner_peep->y - peep->y);
if (max(x_diff, y_diff) < 224)return;
}
map_element->flags |= MAP_ELEMENT_FLAG_BROKEN;
map_invalidate_tile_zoom1(
peep->next_x,
peep->next_y,
(map_element->base_height << 3) + 32,
map_element->base_height << 3);
peep->angriness = 16;
return;
}
/**
*
* rct2: 0x006912A3
*/
static void peep_update_buying(rct_peep* peep)
{
if (!checkForPath(peep))return;
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_NULL || ride->status != RIDE_STATUS_OPEN){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (peep->sub_state == 1){
if (peep->action != 0xFF){
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
return;
}
if (ride->type == RIDE_TYPE_CASH_MACHINE){
if (peep->current_ride != peep->previous_ride){
peep->cash_in_pocket += MONEY(50,00);
}
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
}
peep->sprite_direction ^= 0x10;
peep->destination_x = peep->next_x + 16;
peep->destination_y = peep->next_y + 16;
peep->direction ^= 2;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
return;
}
uint8 item_bought = 0;
if (peep->current_ride != peep->previous_ride){
if (ride->type == RIDE_TYPE_CASH_MACHINE){
item_bought = peep_should_use_cash_machine(peep, peep->current_ride);
if (!item_bought) {
peep->previous_ride = peep->current_ride;
peep->previous_ride_time_out = 0;
} else {
peep->action = PEEP_ACTION_WITHDRAW_MONEY;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
ride->no_primary_items_sold++;
}
}
else{
rct_ride_entry* ride_type = get_ride_entry(ride->subtype);
if (ride_type->shop_item_secondary != 0xFF){
money16 price = ride->price_secondary;
item_bought = sub_69AF1E(peep, peep->current_ride, ride_type->shop_item_secondary, price);
if (item_bought) {
ride->no_secondary_items_sold++;
}
}
if (!item_bought && ride_type->shop_item != 0xFF){
money16 price = ride->price;
item_bought = sub_69AF1E(peep, peep->current_ride, ride_type->shop_item, price);
if (item_bought) {
ride->no_primary_items_sold++;
}
}
}
}
if (item_bought){
ride_update_popularity(ride, 1);
peep_stop_purchase_thought(peep, ride->type);
}
else{
ride_update_popularity(ride, 0);
}
peep->sub_state = 1;
return;
}
/** rct2: 0x0097EFCC */
static const uint8 item_standard_litter[32] = {
LITTER_TYPE_RUBBISH, // PEEP_ITEM_BALLOON
LITTER_TYPE_RUBBISH, // PEEP_ITEM_TOY
LITTER_TYPE_RUBBISH, // PEEP_ITEM_MAP
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PHOTO
LITTER_TYPE_RUBBISH, // PEEP_ITEM_UMBRELLA
LITTER_TYPE_RUBBISH, // PEEP_ITEM_DRINK
LITTER_TYPE_RUBBISH, // PEEP_ITEM_BURGER
LITTER_TYPE_RUBBISH, // PEEP_ITEM_FRIES
LITTER_TYPE_RUBBISH, // PEEP_ITEM_ICE_CREAM
LITTER_TYPE_RUBBISH, // PEEP_ITEM_COTTON_CANDY
LITTER_TYPE_EMPTY_CAN, // PEEP_ITEM_EMPTY_CAN
LITTER_TYPE_RUBBISH, // PEEP_ITEM_RUBBISH
LITTER_TYPE_EMPTY_BURGER_BOX, // PEEP_ITEM_EMPTY_BURGER_BOX
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PIZZA
LITTER_TYPE_RUBBISH, // PEEP_ITEM_VOUCHER
LITTER_TYPE_RUBBISH, // PEEP_ITEM_POPCORN
LITTER_TYPE_RUBBISH, // PEEP_ITEM_HOT_DOG
LITTER_TYPE_RUBBISH, // PEEP_ITEM_TENTACLE
LITTER_TYPE_RUBBISH, // PEEP_ITEM_HAT
LITTER_TYPE_RUBBISH, // PEEP_ITEM_CANDY_APPLE
LITTER_TYPE_RUBBISH, // PEEP_ITEM_TSHIRT
LITTER_TYPE_RUBBISH, // PEEP_ITEM_DONUT
LITTER_TYPE_RUBBISH, // PEEP_ITEM_COFFEE
LITTER_TYPE_EMPTY_CUP, // PEEP_ITEM_EMPTY_CUP
LITTER_TYPE_EMPTY_BOX, // PEEP_ITEM_CHICKEN
LITTER_TYPE_EMPTY_BOTTLE, // PEEP_ITEM_LEMONADE
LITTER_TYPE_EMPTY_BOX, // PEEP_ITEM_EMPTY_BOX
LITTER_TYPE_EMPTY_BOTTLE, // PEEP_ITEM_EMPTY_BOTTLE
};
/** rct2: 0x0097EFE8 */
static const uint8 item_extra_litter[32] = {
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PHOTO2
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PHOTO3
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PHOTO4
LITTER_TYPE_RUBBISH, // PEEP_ITEM_PRETZEL
LITTER_TYPE_RUBBISH, // PEEP_ITEM_CHOCOLATE
LITTER_TYPE_RUBBISH, // PEEP_ITEM_ICED_TEA
LITTER_TYPE_RUBBISH, // PEEP_ITEM_FUNNEL_CAKE
LITTER_TYPE_RUBBISH, // PEEP_ITEM_SUNGLASSES
LITTER_TYPE_RUBBISH, // PEEP_ITEM_BEEF_NOODLES
LITTER_TYPE_RUBBISH, // PEEP_ITEM_FRIED_RICE_NOODLES
LITTER_TYPE_RUBBISH, // PEEP_ITEM_WONTON_SOUP
LITTER_TYPE_RUBBISH, // PEEP_ITEM_MEATBALL_SOUP
LITTER_TYPE_RUBBISH, // PEEP_ITEM_FRUIT_JUICE
LITTER_TYPE_RUBBISH, // PEEP_ITEM_SOYBEAN_MILK
LITTER_TYPE_RUBBISH, // PEEP_ITEM_SU_JONGKWA
LITTER_TYPE_RUBBISH, // PEEP_ITEM_SUB_SANDWICH
LITTER_TYPE_RUBBISH, // PEEP_ITEM_COOKIE
LITTER_TYPE_EMPTY_BOWL_RED, // PEEP_ITEM_EMPTY_BOWL_RED
LITTER_TYPE_EMPTY_DRINK_CARTON, // PEEP_ITEM_EMPTY_DRINK_CARTON
LITTER_TYPE_EMPTY_JUICE_CUP, // PEEP_ITEM_EMPTY_JUICE_CUP
LITTER_TYPE_RUBBISH, // PEEP_ITEM_ROAST_SAUSAGE
LITTER_TYPE_EMPTY_BOWL_BLUE, // PEEP_ITEM_EMPTY_BOWL_BLUE
};
/**
*
* rct2: 0x00691089
*/
static void peep_update_using_bin(rct_peep* peep){
if (peep->sub_state == 0){
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
peep->sub_state = 1;
}
else if (peep->sub_state == 1){
if (peep->action != PEEP_ACTION_NONE_2){
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
return;
}
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;;map_element++){
if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_PATH){
continue;
}
if (map_element->base_height == peep->next_z)break;
if (map_element_is_last_for_tile(map_element)){
peep_state_reset(peep);
return;
}
}
if (!footpath_element_has_path_scenery(map_element)){
peep_state_reset(peep);
return;
}
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)){
peep_state_reset(peep);
return;
}
if (map_element->flags & MAP_ELEMENT_FLAG_BROKEN){
peep_state_reset(peep);
return;
}
if (footpath_element_path_scenery_is_ghost(map_element)){
peep_state_reset(peep);
return;
}
// Bin selection is one of 4 corners
uint8 selected_bin = peep->var_37 * 2;
// This counts down 2 = No rubbish, 0 = full
uint8 space_left_in_bin = 0x3 & (map_element->properties.path.addition_status >> selected_bin);
uint32 empty_containers = peep_empty_container_standard_flag(peep);
for (uint8 cur_container = 0; cur_container < 32; cur_container++){
if (!(empty_containers & (1u << cur_container))) continue;
if (space_left_in_bin != 0){
// OpenRCT2 modification: This previously used
// the tick count as a simple random function
// switched to scenario_rand as it is more reliable
if ((scenario_rand() & 7) == 0) space_left_in_bin--;
peep->item_standard_flags &= ~(1 << cur_container);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
continue;
}
uint8 bp = item_standard_litter[cur_container];
int x, y;
x = peep->x + (scenario_rand() & 7) - 3;
y = peep->y + (scenario_rand() & 7) - 3;
litter_create(x, y, peep->z, scenario_rand() & 3, bp);
peep->item_standard_flags &= ~(1 << cur_container);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
// Original bug: This would clear any rubbish placed by the previous function
//rubbish_in_bin = 0x3 & (map_element->properties.path.addition_status >> selected_bin);
empty_containers = peep_empty_container_extra_flag(peep);
for (uint8 cur_container = 0; cur_container < 32; cur_container++){
if (!(empty_containers & (1u << cur_container))) continue;
if (space_left_in_bin != 0){
// OpenRCT2 modification: This previously used
// the tick count as a simple random function
// switched to scenario_rand as it is more reliable
if ((scenario_rand() & 7) == 0) space_left_in_bin--;
peep->item_extra_flags &= ~(1 << cur_container);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
continue;
}
uint8 bp = item_extra_litter[cur_container];
int x, y;
x = peep->x + (scenario_rand() & 7) - 3;
y = peep->y + (scenario_rand() & 7) - 3;
litter_create(x, y, peep->z, scenario_rand() & 3, bp);
peep->item_extra_flags &= ~(1 << cur_container);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
// Place new amount in bin by first clearing the value
map_element->properties.path.addition_status &= ~(3 << selected_bin);
// Then placing the new value.
map_element->properties.path.addition_status |= space_left_in_bin << selected_bin;
map_invalidate_tile_zoom0(peep->next_x, peep->next_y, map_element->base_height << 3, map_element->clearance_height << 3);
peep_state_reset(peep);
}
}
/**
*
* rct2: 0x006C16D7
*/
static void peep_update_heading_to_inspect(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_NULL){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (ride->exits[peep->current_ride_station] == 0xFFFF){
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (ride->mechanic_status != RIDE_MECHANIC_STATUS_HEADING ||
!(ride->lifecycle_flags & RIDE_LIFECYCLE_DUE_INSPECTION)){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (peep->sub_state == 0){
peep->var_74 = 0;
peep_reset_pathfind_goal(peep);
peep->sub_state = 2;
}
if (peep->sub_state <= 3){
peep->var_74++;
if (peep->var_74 > 2500){
if (ride->lifecycle_flags & RIDE_LIFECYCLE_DUE_INSPECTION&&
ride->mechanic_status == RIDE_MECHANIC_STATUS_HEADING){
ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_RIDE_EXIT) &&
!(_unk_F1EE18 & F1EE18_RIDE_ENTRANCE)
) {
return;
}
rct_map_element* map_element = _peepRideEntranceExitElement;
if (peep->current_ride !=
map_element->properties.entrance.ride_index)
return;
uint8 exit_index = ((map_element->properties.entrance.index & 0x70) >> 4);
if (peep->current_ride_station != exit_index)
return;
if (_unk_F1EE18 & F1EE18_RIDE_ENTRANCE) {
if (ride->exits[exit_index] != 0xFFFF)return;
}
uint8 direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK;
peep->direction = direction;
int x = peep->next_x + 16 + word_981D6C[direction].x * 53;
int y = peep->next_y + 16 + word_981D6C[direction].y * 53;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep->sprite_direction = direction << 3;
peep->z = map_element->base_height * 4;
peep->sub_state = 4;
// Falls through into sub_state 4
}
invalidate_sprite_2((rct_sprite*)peep);
sint16 delta_y = abs(peep->y - peep->destination_y);
sint16 x, y, xy_distance;
if (!peep_update_action(&x, &y, &xy_distance, peep)){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_INSPECTING;
peep->sub_state = 0;
peep_window_state_update(peep);
return;
}
int z = ride->station_heights[peep->current_ride_station] * 8;
if (delta_y < 20){
z += RideData5[ride->type].z;
}
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
}
/**
*
* rct2: 0x006C0CB8
*/
static void peep_update_answering(rct_peep* peep){
rct_ride* ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_NULL ||
ride->mechanic_status != RIDE_MECHANIC_STATUS_HEADING){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (peep->sub_state == 0){
peep->action = PEEP_ACTION_STAFF_ANSWER_CALL;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
peep->sub_state = 1;
peep_window_state_update(peep);
return;
}
else if (peep->sub_state == 1){
if (peep->action == PEEP_ACTION_NONE_2){
peep->sub_state = 2;
peep_window_state_update(peep);
peep->var_74 = 0;
peep_reset_pathfind_goal(peep);
return;
}
sint16 x, y, xy_distance;
peep_update_action(&x, &y, &xy_distance, peep);
return;
}
else if (peep->sub_state <= 3){
peep->var_74++;
if (peep->var_74 > 2500){
if (ride->mechanic_status == RIDE_MECHANIC_STATUS_HEADING){
ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE;
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_RIDE_EXIT) &&
!(_unk_F1EE18 & F1EE18_RIDE_ENTRANCE)
) {
return;
}
rct_map_element* map_element = _peepRideEntranceExitElement;
if (peep->current_ride !=
map_element->properties.entrance.ride_index)
return;
uint8 exit_index = ((map_element->properties.entrance.index & 0x70) >> 4);
if (peep->current_ride_station != exit_index)
return;
if (_unk_F1EE18 & F1EE18_RIDE_ENTRANCE) {
if (ride->exits[exit_index] != 0xFFFF)return;
}
uint8 direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK;
peep->direction = direction;
int x = peep->next_x + 16 + word_981D6C[direction].x * 53;
int y = peep->next_y + 16 + word_981D6C[direction].y * 53;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 2;
peep->sprite_direction = direction << 3;
peep->z = map_element->base_height * 4;
peep->sub_state = 4;
// Falls through into sub_state 4
}
invalidate_sprite_2((rct_sprite*)peep);
sint16 delta_y = abs(peep->y - peep->destination_y);
sint16 x, y, xy_distance;
if (!peep_update_action(&x, &y, &xy_distance, peep)){
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FIXING;
peep->sub_state = 0;
peep_window_state_update(peep);
return;
}
int z = ride->station_heights[peep->current_ride_station] * 8;
if (delta_y < 20){
z += RideData5[ride->type].z;
}
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
}
/** rct2: 0x00992A5C */
static const rct_xy16 _992A5C[] = {
{ 3, 16 },
{ 16, 29 },
{ 29, 16 },
{ 16, 3 },
{ 3, 29 },
{ 29, 29 },
{ 29, 3 },
{ 3, 3 },
};
/**
*
* rct2: 0x006BF483
*/
static int peep_update_patrolling_find_watering(rct_peep* peep){
if (!(peep->staff_orders & STAFF_ORDERS_WATER_FLOWERS))
return 0;
uint8 chosen_position = scenario_rand() & 7;
for (int i = 0; i < 8; ++i, ++chosen_position){
chosen_position &= 7;
int x = peep->next_x + TileDirectionDelta[chosen_position].x;
int y = peep->next_y + TileDirectionDelta[chosen_position].y;
rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32);
do {
if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY){
continue;
}
uint8 z_diff = abs(peep->next_z - map_element->base_height);
if (z_diff >= 4){
continue;
}
rct_scenery_entry* sceneryEntry = get_small_scenery_entry(map_element->properties.scenery.type);
if (!(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_CAN_BE_WATERED)){
continue;
}
if (map_element->properties.scenery.age < 55){
if (chosen_position >= 4){
continue;
}
if (map_element->properties.scenery.age < 40){
continue;
}
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WATERING;
peep->var_37 = chosen_position;
peep_window_state_update(peep);
peep->sub_state = 0;
peep->destination_x = (peep->x & 0xFFE0) + _992A5C[chosen_position].x;
peep->destination_y = (peep->y & 0xFFE0) + _992A5C[chosen_position].y;
peep->destination_tolerence = 3;
return 1;
} while (!map_element_is_last_for_tile(map_element++));
}
return 0;
}
/**
*
* rct2: 0x006BF3A1
*/
static int peep_update_patrolling_find_bin(rct_peep* peep){
if (!(peep->staff_orders & STAFF_ORDERS_EMPTY_BINS))
return 0;
if ((peep->next_var_29 & 0x18) != 0) return 0;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
if (map_element == NULL)return 0;
for (;; map_element++){
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH
&& (map_element->base_height == peep->next_z))
break;
if (map_element_is_last_for_tile(map_element))
return 0;
}
if (!footpath_element_has_path_scenery(map_element)) return 0;
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN))
return 0;
if (map_element->flags & MAP_ELEMENT_FLAG_BROKEN)
return 0;
if (footpath_element_path_scenery_is_ghost(map_element))
return 0;
uint8 bin_positions = map_element->properties.path.edges & 0xF;
uint8 bin_quantity = map_element->properties.path.addition_status;
uint8 chosen_position = 0;
for (; chosen_position < 4; ++chosen_position){
if (!(bin_positions & 1) &&
!(bin_quantity & 3))
break;
bin_positions >>= 1;
bin_quantity >>= 2;
}
if (chosen_position == 4)return 0;
peep->var_37 = chosen_position;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_EMPTYING_BIN;
peep_window_state_update(peep);
peep->sub_state = 0;
peep->destination_x = (peep->x & 0xFFE0) + _992A4C[chosen_position].x;
peep->destination_y = (peep->y & 0xFFE0) + _992A4C[chosen_position].y;
peep->destination_tolerence = 3;
return 1;
}
/**
*
* rct2: 0x006BF322
*/
static int peep_update_patrolling_find_grass(rct_peep* peep){
if (!(peep->staff_orders & STAFF_ORDERS_MOWING))
return 0;
if (peep->var_E2 < 12)return 0;
if ((peep->next_var_29 & 0x18) != 8) return 0;
rct_map_element* map_element = map_get_surface_element_at(peep->next_x / 32, peep->next_y / 32);
if ((map_element->properties.surface.terrain & MAP_ELEMENT_SURFACE_TERRAIN_MASK) != TERRAIN_GRASS)
return 0;
if ((map_element->properties.surface.grass_length & 0x7) < GRASS_LENGTH_CLEAR_1)
return 0;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_MOWING;
peep_window_state_update(peep);
peep->var_37 = 0;
// Original code used .y for both x and y. Changed to .x to make more sense (both x and y are 28)
peep->destination_x = peep->next_x + _9929C8[0].x;
peep->destination_y = peep->next_y + _9929C8[0].y;
peep->destination_tolerence = 3;
return 1;
}
/**
*
* rct2: 0x006BF295
*/
static int peep_update_patrolling_find_sweeping(rct_peep* peep){
if (!(peep->staff_orders & STAFF_ORDERS_SWEEPING))
return 0;
uint16 sprite_id = sprite_get_first_in_quadrant(peep->x, peep->y);
for (rct_sprite* sprite = NULL;
sprite_id != 0xFFFF;
sprite_id = sprite->unknown.next_in_quadrant){
sprite = get_sprite(sprite_id);
if (sprite->unknown.linked_list_type_offset != SPRITE_LIST_LITTER * 2) continue;
uint16 z_diff = abs(peep->z - sprite->litter.z);
if (z_diff >= 16)continue;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_SWEEPING;
peep_window_state_update(peep);
peep->var_37 = 0;
peep->destination_x = sprite->litter.x;
peep->destination_y = sprite->litter.y;
peep->destination_tolerence = 5;
return 1;
}
return 0;
}
/**
*
* rct2: 0x006BF1FD
*/
static void peep_update_patrolling(rct_peep* peep){
if (!checkForPath(peep))return;
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
if ((peep->next_var_29 & 0x18) == 8){
rct_map_element* map_element = map_get_surface_element_at(peep->next_x / 32, peep->next_y / 32);
if (map_element != NULL){
int water_height = map_element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK;
if (water_height){
invalidate_sprite_2((rct_sprite*)peep);
water_height *= 16;
sprite_move(peep->x, peep->y, water_height, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
}
}
if (peep->staff_type != STAFF_TYPE_HANDYMAN) return;
if (peep_update_patrolling_find_sweeping(peep))return;
if (peep_update_patrolling_find_grass(peep))return;
if (peep_update_patrolling_find_bin(peep))return;
peep_update_patrolling_find_watering(peep);
}
/** rct2: 0x00981F4C, 0x00981F4E */
static const rct_xy16 _981F4C[] = {
{ 7, 5 },
{ 5, 25 },
{ 25, 5 },
{ 5, 7 },
{ 7, 9 },
{ 9, 25 },
{ 25, 9 },
{ 9, 7 },
{ 7, 23 },
{ 23, 25 },
{ 25, 23 },
{ 23, 7 },
{ 7, 27 },
{ 27, 25 },
{ 25, 27 },
{ 27, 7 },
{ 7, 0 },
{ 0, 25 },
{ 25, 0 },
{ 0, 7 },
{ 7, 0 },
{ 0, 25 },
{ 25, 0 },
{ 0, 7 },
{ 7, 0 },
{ 0, 25 },
{ 25, 0 },
{ 0, 7 },
{ 7, 0 },
{ 0, 25 },
{ 25, 0 },
{ 0, 7 },
};
/**
*
* rct2: 0x0069030A
*/
static void peep_update_walking(rct_peep* peep){
if (!checkForPath(peep))return;
if (peep->peep_flags & PEEP_FLAGS_WAVING){
if (peep->action >= PEEP_ACTION_NONE_1){
if ((0xFFFF & scenario_rand()) < 936){
invalidate_sprite_2((rct_sprite*)peep);
peep->action = PEEP_ACTION_WAVE_2;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
if (peep->peep_flags & PEEP_FLAGS_PHOTO){
if (peep->action >= PEEP_ACTION_NONE_1){
if ((0xFFFF & scenario_rand()) < 936){
invalidate_sprite_2((rct_sprite*)peep);
peep->action = PEEP_ACTION_TAKE_PHOTO;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
if (peep->peep_flags & PEEP_FLAGS_PAINTING){
if (peep->action >= PEEP_ACTION_NONE_1){
if ((0xFFFF & scenario_rand()) < 936){
invalidate_sprite_2((rct_sprite*)peep);
peep->action = PEEP_ACTION_DRAW_PICTURE;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
if (peep->peep_flags & PEEP_FLAGS_LITTER){
if (!(peep->next_var_29 & 0x18)){
if ((0xFFFF & scenario_rand()) <= 4096){
static const uint8 litter_types[] = {
LITTER_TYPE_EMPTY_CAN,
LITTER_TYPE_RUBBISH,
LITTER_TYPE_EMPTY_BURGER_BOX,
LITTER_TYPE_EMPTY_CUP,
};
int ebp = litter_types[scenario_rand() & 0x3];
int x = peep->x + (scenario_rand() & 0x7) - 3;
int y = peep->y + (scenario_rand() & 0x7) - 3;
int direction = (scenario_rand() & 0x3);
litter_create(x, y, peep->z, direction, ebp);
}
}
}
else if (peep_has_empty_container(peep)){
if ((!(peep->next_var_29 & 0x18)) &&
((peep->sprite_index & 0x1FF) == (gCurrentTicks & 0x1FF))&&
((0xFFFF & scenario_rand()) <= 4096)){
uint8 pos_stnd = 0;
for (int container = peep_empty_container_standard_flag(peep); pos_stnd < 32; pos_stnd++)
if (container & (1u << pos_stnd))
break;
int bp = 0;
if (pos_stnd != 32){
peep->item_standard_flags &= ~(1u << pos_stnd);
bp = item_standard_litter[pos_stnd];
}
else{
uint8 pos_extr = 0;
for (int container = peep_empty_container_extra_flag(peep); pos_extr < 32; pos_extr++)
if (container & (1u << pos_extr))
break;
peep->item_extra_flags &= ~(1u << pos_extr);
bp = item_extra_litter[pos_extr];
}
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
int x = peep->x + (scenario_rand() & 0x7) - 3;
int y = peep->y + (scenario_rand() & 0x7) - 3;
int direction = (scenario_rand() & 0x3);
litter_create(x, y, peep->z, direction, bp);
}
}
sub_693C9E(peep);
if (!(_unk_F1EE18 & F1EE18_DESTINATION_REACHED)) return;
if ((peep->next_var_29 & 0x18) == 8){
rct_map_element* map_element = map_get_surface_element_at(peep->next_x / 32, peep->next_y / 32);
int water_height = map_element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK;
if (water_height){
invalidate_sprite_2((rct_sprite*)peep);
water_height *= 16;
sprite_move(peep->x, peep->y, water_height, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
return;
}
}
peep_check_if_lost(peep);
peep_check_cant_find_ride(peep);
peep_check_cant_find_exit(peep);
if (peep_update_walking_find_bench(peep))return;
if (peep_update_walking_find_bin(peep))return;
peep_update_walking_break_scenery(peep);
if (peep->state != PEEP_STATE_WALKING)return;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)return;
if (peep->nausea > 140)return;
if (peep->happiness < 120)return;
if (peep->bathroom > 140)return;
uint16 chance = peep_has_food(peep) ? 13107 : 2849;
if ((scenario_rand() & 0xFFFF) > chance)return;
if (peep->next_var_29 & 0x1C)return;
rct_map_element* map_element = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32);
for (;; map_element++){
if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH){
if (peep->next_z == map_element->base_height)break;
}
if (map_element_is_last_for_tile(map_element)){
return;
}
}
int ebp = 15;
if (footpath_element_has_path_scenery(map_element)) {
if (!footpath_element_path_scenery_is_ghost(map_element)) {
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(map_element));
if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BENCH)) ebp = 9;
}
}
int edges = (map_element->properties.path.edges & 0xF) ^ 0xF;
if (edges == 0) return;
uint8 chosen_edge = scenario_rand() & 0x3;
for (; !(edges & (1 << chosen_edge));)chosen_edge = (chosen_edge + 1) & 3;
uint8 ride_to_view, ride_seat_to_view;
if (!peep_find_ride_to_look_at(peep, chosen_edge, &ride_to_view, &ride_seat_to_view))
return;
uint16 sprite_id = sprite_get_first_in_quadrant(peep->x, peep->y);
for (rct_sprite* sprite; sprite_id != SPRITE_INDEX_NULL; sprite_id = sprite->unknown.next_in_quadrant){
sprite = get_sprite(sprite_id);
if (sprite->unknown.linked_list_type_offset != SPRITE_LIST_PEEP * 2) continue;
if (sprite->peep.state != PEEP_STATE_WATCHING)continue;
if (peep->z != sprite->peep.z)continue;
if ((sprite->peep.var_37 & 0x3) != chosen_edge)continue;
ebp &= ~(1 << ((sprite->peep.var_37 & 0x1C) >> 2));
}
if (!ebp)return;
uint8 chosen_position = scenario_rand() & 0x3;
for (; !(ebp & (1 << chosen_position));)chosen_position = (chosen_position + 1) & 3;
peep->current_ride = ride_to_view;
peep->current_seat = ride_seat_to_view;
peep->var_37 = chosen_edge | (chosen_position << 2);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WATCHING;
peep_window_state_update(peep);
peep->sub_state = 0;
int ebx = peep->var_37 & 0x1F;
int x = (peep->x & 0xFFE0) + _981F4C[ebx].x;
int y = (peep->y & 0xFFE0) + _981F4C[ebx].y;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerence = 3;
if (peep->current_seat&1){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NEW_RIDE, 0xFF);
}
if (peep->current_ride == 0xFF){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SCENERY, 0xFF);
}
}
/* From peep_update */
static void peep_update_thoughts(rct_peep* peep){
// Thoughts must always have a gap of at least
// 220 ticks in age between them. In order to
// allow this when a thought is new it enters
// a holding zone. Before it becomes fresh.
int add_fresh = 1;
int fresh_thought = -1;
for (int i = 0; i < PEEP_MAX_THOUGHTS; i++) {
if (peep->thoughts[i].type == PEEP_THOUGHT_TYPE_NONE)
break;
if (peep->thoughts[i].var_2 == 1) {
add_fresh = 0;
// If thought is fresh we wait 220 ticks
// before allowing a new thought to become fresh.
if (++peep->thoughts[i].var_3 >= 220) {
peep->thoughts[i].var_3 = 0;
// Thought is no longer fresh
peep->thoughts[i].var_2++;
add_fresh = 1;
}
}
else if (peep->thoughts[i].var_2 > 1) {
if (++peep->thoughts[i].var_3 == 0) {
// When thought is older than ~6900 ticks remove it
if (++peep->thoughts[i].var_2 >= 28) {
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS;
// Clear top thought, push others up
if (i < PEEP_MAX_THOUGHTS - 2) {
memmove(&peep->thoughts[i], &peep->thoughts[i + 1], sizeof(rct_peep_thought)*(PEEP_MAX_THOUGHTS - i - 1));
}
peep->thoughts[PEEP_MAX_THOUGHTS - 1].type = PEEP_THOUGHT_TYPE_NONE;
}
}
}
else {
fresh_thought = i;
}
}
// If there are no fresh thoughts
// a previously new thought can become
// fresh.
if (add_fresh && fresh_thought != -1) {
peep->thoughts[fresh_thought].var_2 = 1;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS;
}
}
/**
*
* rct2: 0x0068FC1E
*/
static void peep_update(rct_peep *peep)
{
if (peep->type == PEEP_TYPE_GUEST) {
if (peep->previous_ride != 255)
if (++peep->previous_ride_time_out >= 720)
peep->previous_ride = 255;
peep_update_thoughts(peep);
}
// Walking speed logic
unsigned int stepsToTake = peep->energy;
if (stepsToTake < 95 && peep->state == PEEP_STATE_QUEUING)
stepsToTake = 95;
if ((peep->peep_flags & PEEP_FLAGS_SLOW_WALK) && peep->state != PEEP_STATE_QUEUING)
stepsToTake /= 2;
if (peep->action == 255 && (peep->next_var_29 & 4)) {
stepsToTake /= 2;
if (peep->state == PEEP_STATE_QUEUING)
stepsToTake += stepsToTake / 2;
}
unsigned int carryCheck = peep->var_73 + stepsToTake;
peep->var_73 = carryCheck;
if (carryCheck <= 255) {
peep_easter_egg_peep_interactions(peep);
} else {
// loc_68FD2F
switch (peep->state) {
case PEEP_STATE_FALLING:
peep_update_falling(peep);
break;
case PEEP_STATE_1:
peep_update_1(peep);
break;
case PEEP_STATE_QUEUING_FRONT:
peep_update_ride(peep);
break;
case PEEP_STATE_ON_RIDE:
// No action
break;
case PEEP_STATE_LEAVING_RIDE:
peep_update_ride(peep);
break;
case PEEP_STATE_WALKING:
peep_update_walking(peep);
break;
case PEEP_STATE_QUEUING:
peep_update_queuing(peep);
break;
case PEEP_STATE_ENTERING_RIDE:
peep_update_ride(peep);
break;
case PEEP_STATE_SITTING:
peep_update_sitting(peep);
break;
case PEEP_STATE_PICKED:
peep_update_picked(peep);
break;
case PEEP_STATE_PATROLLING:
peep_update_patrolling(peep);
break;
case PEEP_STATE_MOWING:
peep_update_mowing(peep);
break;
case PEEP_STATE_SWEEPING:
peep_update_sweeping(peep);
break;
case PEEP_STATE_ENTERING_PARK:
peep_update_entering_park(peep);
break;
case PEEP_STATE_LEAVING_PARK:
peep_update_leaving_park(peep);
break;
case PEEP_STATE_ANSWERING:
peep_update_answering(peep);
break;
case PEEP_STATE_FIXING:
peep_update_fixing(stepsToTake, peep);
break;
case PEEP_STATE_BUYING:
peep_update_buying(peep);
break;
case PEEP_STATE_WATCHING:
peep_update_watching(peep);
break;
case PEEP_STATE_EMPTYING_BIN:
peep_update_emptying_bin(peep);
break;
case PEEP_STATE_USING_BIN:
peep_update_using_bin(peep);
break;
case PEEP_STATE_WATERING:
peep_update_watering(peep);
break;
case PEEP_STATE_HEADING_TO_INSPECTION:
peep_update_heading_to_inspect(peep);
break;
case PEEP_STATE_INSPECTING:
peep_update_fixing(stepsToTake, peep);
break;
//There shouldn't be any more
default:
assert(0);
break;
}
}
}
/**
*
* rct2: 0x0069BF41
*/
void peep_problem_warnings_update()
{
rct_peep* peep;
rct_ride* ride;
uint16 spriteIndex;
uint16 guests_in_park = gNumGuestsInPark;
int hunger_counter = 0, lost_counter = 0, noexit_counter = 0, thirst_counter = 0,
litter_counter = 0, disgust_counter = 0, bathroom_counter = 0 ,vandalism_counter = 0;
uint8 *warning_throttle = gPeepWarningThrottle;
gRideCount = ride_get_count(); // refactor this to somewhere else
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->outside_of_park != 0 || peep->thoughts[0].var_2 > 5)
continue;
switch (peep->thoughts[0].type) {
case PEEP_THOUGHT_TYPE_LOST: //0x10
lost_counter++;
break;
case PEEP_THOUGHT_TYPE_HUNGRY: // 0x14
if (peep->guest_heading_to_ride_id == 0xFF){
hunger_counter++;
break;
}
ride = get_ride(peep->guest_heading_to_ride_id);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE))
hunger_counter++;
break;
case PEEP_THOUGHT_TYPE_THIRSTY:
if (peep->guest_heading_to_ride_id == 0xFF){
thirst_counter++;
break;
}
ride = get_ride(peep->guest_heading_to_ride_id);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SELLS_DRINKS))
thirst_counter++;
break;
case PEEP_THOUGHT_TYPE_BATHROOM:
if (peep->guest_heading_to_ride_id == 0xFF){
bathroom_counter++;
break;
}
ride = get_ride(peep->guest_heading_to_ride_id);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_BATHROOM))
bathroom_counter++;
break;
case PEEP_THOUGHT_TYPE_BAD_LITTER: // 0x1a
litter_counter++;
break;
case PEEP_THOUGHT_TYPE_CANT_FIND_EXIT: // 0x1b
noexit_counter++;
break;
case PEEP_THOUGHT_TYPE_PATH_DISGUSTING: // 0x1f
disgust_counter++;
break;
case PEEP_THOUGHT_TYPE_VANDALISM: //0x21
vandalism_counter++;
break;
default:
break;
}
}
// could maybe be packed into a loop, would lose a lot of clarity though
if (warning_throttle[0])
--warning_throttle[0];
else if ( hunger_counter >= PEEP_HUNGER_WARNING_THRESHOLD && hunger_counter >= guests_in_park / 16) {
warning_throttle[0] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_ARE_HUNGRY, 20);
}
}
if (warning_throttle[1])
--warning_throttle[1];
else if (thirst_counter >= PEEP_THIRST_WARNING_THRESHOLD && thirst_counter >= guests_in_park / 16) {
warning_throttle[1] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_ARE_THIRSTY, 21);
}
}
if (warning_throttle[2])
--warning_throttle[2];
else if (bathroom_counter >= PEEP_BATHROOM_WARNING_THRESHOLD && bathroom_counter >= guests_in_park / 16) {
warning_throttle[2] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_CANT_FIND_BATHROOM, 22);
}
}
if (warning_throttle[3])
--warning_throttle[3];
else if (litter_counter >= PEEP_LITTER_WARNING_THRESHOLD && litter_counter >= guests_in_park / 32) {
warning_throttle[3] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_DISLIKE_LITTER, 26);
}
}
if (warning_throttle[4])
--warning_throttle[4];
else if (disgust_counter >= PEEP_DISGUST_WARNING_THRESHOLD && disgust_counter >= guests_in_park / 32) {
warning_throttle[4] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_DISGUSTED_BY_PATHS, 31);
}
}
if (warning_throttle[5])
--warning_throttle[5];
else if (vandalism_counter >= PEEP_VANDALISM_WARNING_THRESHOLD && vandalism_counter >= guests_in_park / 32) {
warning_throttle[5] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_DISLIKE_VANDALISM, 33);
}
}
if (warning_throttle[6])
--warning_throttle[6];
else if (noexit_counter >= PEEP_NOEXIT_WARNING_THRESHOLD) {
warning_throttle[6] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_GETTING_LOST_OR_STUCK, 27);
}
} else if (lost_counter >= PEEP_LOST_WARNING_THRESHOLD) {
warning_throttle[6] = 4;
if (gConfigNotifications.guest_warnings) {
news_item_add_to_queue(NEWS_ITEM_PEEPS, STR_PEEPS_GETTING_LOST_OR_STUCK, 16);
}
}
}
/**
*
* rct2: 0x006BD18A
*/
void peep_update_crowd_noise()
{
rct_viewport *viewport;
uint16 spriteIndex;
rct_peep *peep;
int visiblePeeps;
if (gGameSoundsOff)
return;
if (!gConfigSound.sound_enabled)
return;
if (gScreenFlags & 2)
return;
viewport = g_music_tracking_viewport;
if (viewport == (rct_viewport*)-1)
return;
// Count the number of peeps visible
visiblePeeps = 0;
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->sprite_left == (sint16)0x8000)
continue;
if (viewport->view_x > peep->sprite_right)
continue;
if (viewport->view_x + viewport->view_width < peep->sprite_left)
continue;
if (viewport->view_y > peep->sprite_bottom)
continue;
if (viewport->view_y + viewport->view_height < peep->sprite_top)
continue;
visiblePeeps += peep->state == PEEP_STATE_QUEUING ? 1 : 2;
}
// This function doesn't account for the fact that the screen might be so big that 100 peeps could potentially be very
// spread out and therefore not produce any crowd noise. Perhaps a more sophisticated solution would check how many peeps
// were in close proximity to each other.
// Allows queuing peeps to make half as much noise, and at least 6 peeps must be visible for any crowd noise
visiblePeeps = (visiblePeeps / 2) - 6;
if (visiblePeeps < 0) {
// Mute crowd noise
if (gCrowdSoundChannel) {
Mixer_Stop_Channel(gCrowdSoundChannel);
gCrowdSoundChannel = 0;
}
} else {
sint32 volume;
// Formula to scale peeps to dB where peeps [0, 120] scales approximately logarithmically to [-3314, -150] dB/100
// 207360000 maybe related to DSBVOLUME_MIN which is -10,000 (dB/100)
volume = 120 - min(visiblePeeps, 120);
volume = volume * volume * volume * volume;
volume = (((207360000 - volume) >> viewport->zoom) - 207360000) / 65536 - 150;
// Load and play crowd noise if needed and set volume
if (!gCrowdSoundChannel) {
gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2, MIXER_LOOP_INFINITE, false);
if (gCrowdSoundChannel) {
Mixer_Channel_SetGroup(gCrowdSoundChannel, MIXER_GROUP_SOUND);
}
}
if (gCrowdSoundChannel) {
Mixer_Channel_Volume(gCrowdSoundChannel, DStoMixerVolume(volume));
}
}
}
/**
*
* rct2: 0x0069BE9B
*/
void peep_applause()
{
uint16 spriteIndex;
rct_peep* peep;
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->outside_of_park != 0)
continue;
// Release balloon
if (peep->item_standard_flags & PEEP_ITEM_BALLOON) {
peep->item_standard_flags &= ~PEEP_ITEM_BALLOON;
if (peep->x != (sint16)0x8000) {
create_balloon(peep->x, peep->y, peep->z + 9, peep->balloon_colour, 0);
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
}
}
// Clap
if ((peep->state == PEEP_STATE_WALKING || peep->state == PEEP_STATE_QUEUING) && peep->action >= 254) {
peep->action = PEEP_ACTION_CLAP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
// Play applause noise
audio_play_sound_panned(SOUND_APPLAUSE, gScreenWidth / 2, 0, 0, 0);
}
/**
*
* rct2: 0x0069C35E
*/
void peep_update_days_in_queue()
{
uint16 sprite_index;
rct_peep *peep;
FOR_ALL_GUESTS(sprite_index, peep) {
if (peep->outside_of_park == 0 && peep->state == PEEP_STATE_QUEUING) {
if (peep->days_in_queue < 255) {
peep->days_in_queue += 1;
}
}
}
}
/** rct2: 0x009823A0 */
static const enum PEEP_NAUSEA_TOLERANCE nausea_tolerance_distribution[] = {
PEEP_NAUSEA_TOLERANCE_NONE,
PEEP_NAUSEA_TOLERANCE_LOW, PEEP_NAUSEA_TOLERANCE_LOW,
PEEP_NAUSEA_TOLERANCE_AVERAGE, PEEP_NAUSEA_TOLERANCE_AVERAGE, PEEP_NAUSEA_TOLERANCE_AVERAGE,
PEEP_NAUSEA_TOLERANCE_HIGH, PEEP_NAUSEA_TOLERANCE_HIGH, PEEP_NAUSEA_TOLERANCE_HIGH, PEEP_NAUSEA_TOLERANCE_HIGH, PEEP_NAUSEA_TOLERANCE_HIGH, PEEP_NAUSEA_TOLERANCE_HIGH,
};
/** rct2: 0x009823BC */
static const uint8 trouser_colours[] = {
COLOUR_BLACK,
COLOUR_GREY,
COLOUR_LIGHT_BROWN,
COLOUR_SATURATED_BROWN,
COLOUR_DARK_BROWN,
COLOUR_SALMON_PINK,
COLOUR_BLACK,
COLOUR_GREY,
COLOUR_LIGHT_BROWN,
COLOUR_SATURATED_BROWN,
COLOUR_DARK_BROWN,
COLOUR_SALMON_PINK,
COLOUR_BLACK,
COLOUR_GREY,
COLOUR_LIGHT_BROWN,
COLOUR_SATURATED_BROWN,
COLOUR_DARK_BROWN,
COLOUR_SALMON_PINK,
COLOUR_DARK_PURPLE,
COLOUR_LIGHT_PURPLE,
COLOUR_DARK_BLUE,
COLOUR_SATURATED_GREEN,
COLOUR_SATURATED_RED,
COLOUR_DARK_ORANGE,
COLOUR_BORDEAUX_RED,
};
/** rct2: 0x009823D5 */
static const uint8 tshirt_colours[] = {
COLOUR_BLACK,
COLOUR_GREY,
COLOUR_LIGHT_BROWN,
COLOUR_SATURATED_BROWN,
COLOUR_DARK_BROWN,
COLOUR_SALMON_PINK,
COLOUR_BLACK,
COLOUR_GREY,
COLOUR_LIGHT_BROWN,
COLOUR_SATURATED_BROWN,
COLOUR_DARK_BROWN,
COLOUR_SALMON_PINK,
COLOUR_DARK_PURPLE,
COLOUR_LIGHT_PURPLE,
COLOUR_DARK_BLUE,
COLOUR_SATURATED_GREEN,
COLOUR_SATURATED_RED,
COLOUR_DARK_ORANGE,
COLOUR_BORDEAUX_RED,
COLOUR_WHITE,
COLOUR_BRIGHT_PURPLE,
COLOUR_LIGHT_BLUE,
COLOUR_TEAL,
COLOUR_DARK_GREEN,
COLOUR_MOSS_GREEN,
COLOUR_BRIGHT_GREEN,
COLOUR_OLIVE_GREEN,
COLOUR_DARK_OLIVE_GREEN,
COLOUR_YELLOW,
COLOUR_LIGHT_ORANGE,
COLOUR_BRIGHT_RED,
COLOUR_DARK_PINK,
COLOUR_BRIGHT_PINK,
};
/**
*
* rct2: 0x0069A05D
*/
rct_peep *peep_generate(int x, int y, int z)
{
if (gSpriteListCount[SPRITE_LIST_NULL] < 400)
return NULL;
rct_peep* peep = (rct_peep*)create_sprite(1);
move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP * 2);
peep->sprite_identifier = SPRITE_IDENTIFIER_PEEP;
peep->sprite_type = PEEP_SPRITE_TYPE_NORMAL;
peep->outside_of_park = 1;
peep->state = PEEP_STATE_FALLING;
peep->action = PEEP_ACTION_NONE_2;
peep->special_sprite = 0;
peep->action_sprite_image_offset = 0;
peep->no_action_frame_no = 0;
peep->action_sprite_type = 0;
peep->peep_flags = 0;
peep->favourite_ride = 0xFF;
peep->favourite_ride_rating = 0;
const rct_sprite_bounds* spriteBounds = g_sprite_entries[peep->sprite_type].sprite_bounds;
peep->sprite_width = spriteBounds[peep->action_sprite_type].sprite_width;
peep->sprite_height_negative = spriteBounds[peep->action_sprite_type].sprite_height_negative;
peep->sprite_height_positive = spriteBounds[peep->action_sprite_type].sprite_height_positive;
peep->sprite_direction = 0;
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
peep->var_41 = (scenario_rand() & 0x1F) + 45;
peep->var_C4 = 0;
peep->var_79 = 0xFF;
peep->type = PEEP_TYPE_GUEST;
peep->previous_ride = 0xFF;
peep->thoughts->type = PEEP_THOUGHT_TYPE_NONE;
peep->window_invalidate_flags = 0;
uint8 al = (scenario_rand() & 0x7) + 3;
uint8 ah = min(al, 7) - 3;
if (al >= 7) al = 15;
if (gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES){
ah = 0;
al = 4;
}
if (gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES){
ah = 9;
al = 15;
}
peep->intensity = (al << 4) | ah;
uint8 nausea_tolerance = scenario_rand() & 0x7;
if (gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES){
nausea_tolerance += 4;
}
peep->nausea_tolerance = nausea_tolerance_distribution[nausea_tolerance];
sint8 happiness = (scenario_rand() & 0x1F) - 15 + gGuestInitialHappiness;
if (gGuestInitialHappiness == 0)
happiness += 0x80;
peep->happiness = happiness;
peep->happiness_growth_rate = happiness;
peep->nausea = 0;
peep->nausea_growth_rate = 0;
sint8 hunger = (scenario_rand() & 0x1F) - 15 + gGuestInitialHunger;
peep->hunger = hunger;
sint8 thirst = (scenario_rand() & 0x1F) - 15 + gGuestInitialThirst;
peep->thirst = thirst;
peep->bathroom = 0;
peep->var_42 = 0;
memset(&peep->rides_been_on, 0, 32);
peep->no_of_rides = 0;
memset(&peep->ride_types_been_on, 0, 16);
peep->id = gNextGuestNumber++;
peep->name_string_idx = STR_GUEST_X;
money32 cash = (scenario_rand() & 0x3) * 100 - 100 + gGuestInitialCash;
if (cash < 0) cash = 0;
if (gGuestInitialCash == 0){
cash = 500;
}
if (gParkFlags & PARK_FLAGS_NO_MONEY){
cash = 0;
}
if (gGuestInitialCash == (money16)0xFFFF){
cash = 0;
}
peep->cash_in_pocket = cash;
peep->cash_spent = 0;
peep->time_in_park = -1;
peep->pathfind_goal.x = 0xFF;
peep->pathfind_goal.y = 0xFF;
peep->pathfind_goal.z = 0xFF;
peep->pathfind_goal.direction = 0xFF;
peep->item_standard_flags = 0;
peep->item_extra_flags = 0;
peep->guest_heading_to_ride_id = 0xFF;
peep->litter_count = 0;
peep->disgusting_count = 0;
peep->var_EF = 0;
peep->paid_to_enter = 0;
peep->paid_on_rides = 0;
peep->paid_on_food = 0;
peep->paid_on_drink = 0;
peep->paid_on_souvenirs = 0;
peep->no_of_food = 0;
peep->no_of_drinks = 0;
peep->no_of_souvenirs = 0;
peep->var_F2 = 0;
peep->angriness = 0;
peep->var_F4 = 0;
uint8 tshirt_colour = scenario_rand() % countof(tshirt_colours);
peep->tshirt_colour = tshirt_colours[tshirt_colour];
uint8 trousers_colour = scenario_rand() % countof(trouser_colours);
peep->trousers_colour = trouser_colours[trousers_colour];
uint8 energy = (scenario_rand() & 0x3F) + 65;
peep->energy = energy;
peep->energy_growth_rate = energy;
if (gParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES){
peep_give_real_name(peep);
}
peep_update_name_sort(peep);
gNumGuestsHeadingForPark++;
return peep;
}
/**
* rct2: 0x00698B0D
* peep.sprite_index (eax)
* thought.type (ebx)
* argument_1 (ecx & ebx)
* argument_2 (edx)
*/
void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argument_2){
rct_ride *ride;
switch (peep->state){
case PEEP_STATE_FALLING:
*argument_1 = peep->action == PEEP_ACTION_DROWNING ? STR_DROWNING : STR_WALKING;
*argument_2 = 0;
break;
case PEEP_STATE_1:
*argument_1 = STR_WALKING;
*argument_2 = 0;
break;
case PEEP_STATE_ON_RIDE:
case PEEP_STATE_LEAVING_RIDE:
case PEEP_STATE_ENTERING_RIDE:
*argument_1 = STR_ON_RIDE;
ride = get_ride(peep->current_ride);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE))
*argument_1 = STR_IN_RIDE;
*argument_1 |= ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
case PEEP_STATE_BUYING:
ride = get_ride(peep->current_ride);
*argument_1 = STR_AT_RIDE | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
case PEEP_STATE_WALKING:
case PEEP_STATE_USING_BIN:
if (peep->guest_heading_to_ride_id != 0xFF){
ride = get_ride(peep->guest_heading_to_ride_id);
*argument_1 = STR_HEADING_FOR | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
}
else{
*argument_1 = peep->peep_flags & PEEP_FLAGS_LEAVING_PARK ? STR_LEAVING_PARK : STR_WALKING;
*argument_2 = 0;
}
break;
case PEEP_STATE_QUEUING_FRONT:
case PEEP_STATE_QUEUING:
ride = get_ride(peep->current_ride);
*argument_1 = STR_QUEUING_FOR | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
case PEEP_STATE_SITTING:
*argument_1 = STR_SITTING;
*argument_2 = 0;
break;
case PEEP_STATE_WATCHING:
if (peep->current_ride != 0xFF){
ride = get_ride(peep->current_ride);
*argument_1 = STR_WATCHING_RIDE | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
if (peep->current_seat & 0x1)
*argument_1 = STR_WATCHING_CONSTRUCTION_OF | ((uint32)ride->name << 16);
else
*argument_1 = STR_WATCHING_RIDE | ((uint32)ride->name << 16);
}
else{
*argument_1 = peep->current_seat & 0x1 ? STR_WATCHING_NEW_RIDE_BEING_CONSTRUCTED : STR_LOOKING_AT_SCENERY;
*argument_2 = 0;
}
break;
case PEEP_STATE_PICKED:
*argument_1 = STR_SELECT_LOCATION;
*argument_2 = 0;
break;
case PEEP_STATE_PATROLLING:
case PEEP_STATE_ENTERING_PARK:
case PEEP_STATE_LEAVING_PARK:
*argument_1 = STR_WALKING;
*argument_2 = 0;
break;
case PEEP_STATE_MOWING:
*argument_1 = STR_MOWING_GRASS;
*argument_2 = 0;
break;
case PEEP_STATE_SWEEPING:
*argument_1 = STR_SWEEPING_FOOTPATH;
*argument_2 = 0;
break;
case PEEP_STATE_WATERING:
*argument_1 = STR_WATERING_GARDENS;
*argument_2 = 0;
break;
case PEEP_STATE_EMPTYING_BIN:
*argument_1 = STR_EMPTYING_LITTER_BIN;
*argument_2 = 0;
break;
case PEEP_STATE_ANSWERING:
if (peep->sub_state == 0){
*argument_1 = STR_WALKING;
*argument_2 = 0;
}
else if (peep->sub_state == 1){
*argument_1 = STR_ANSWERING_RADIO_CALL;
*argument_2 = 0;
}
else{
ride = get_ride(peep->current_ride);
*argument_1 = STR_RESPONDING_TO_RIDE_BREAKDOWN_CALL | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
}
break;
case PEEP_STATE_FIXING:
ride = get_ride(peep->current_ride);
*argument_1 = STR_FIXING_RIDE | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
case PEEP_STATE_HEADING_TO_INSPECTION:
ride = get_ride(peep->current_ride);
*argument_1 = STR_HEADING_TO_RIDE_FOR_INSPECTION | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
case PEEP_STATE_INSPECTING:
ride = get_ride(peep->current_ride);
*argument_1 = STR_INSPECTING_RIDE | ((uint32)ride->name << 16);
*argument_2 = ride->name_arguments;
break;
}
}
/**
* rct2: 0x00698342
* thought.item (eax)
* thought.type (ebx)
* argument_1 (esi & ebx)
* argument_2 (esi+2)
*/
void peep_thought_set_format_args(rct_peep_thought *thought)
{
set_format_arg(0, rct_string_id, PeepThoughts[thought->type]);
uint8 flags = PeepThoughtToActionMap[thought->type].flags;
if (flags & 1) {
rct_ride *ride = get_ride(thought->item);
set_format_arg(2, rct_string_id, ride->name);
set_format_arg(4, uint32, ride->name_arguments);
} else if (flags & 2) {
set_format_arg(2, rct_string_id, ShopItemStringIds[thought->item].singular);
} else if (flags & 4) {
set_format_arg(2, rct_string_id, ShopItemStringIds[thought->item].indefinite);
} else {
}
}
/** rct2: 0x00982004 */
static const bool peep_allow_pick_up[] = {
true, // PEEP_STATE_FALLING
false, // PEEP_STATE_1
false, // PEEP_STATE_QUEUING_FRONT
false, // PEEP_STATE_ON_RIDE
false, // PEEP_STATE_LEAVING_RIDE
true, // PEEP_STATE_WALKING
true, // PEEP_STATE_QUEUING
false, // PEEP_STATE_ENTERING_RIDE
true, // PEEP_STATE_SITTING
true, // PEEP_STATE_PICKED
true, // PEEP_STATE_PATROLLING
true, // PEEP_STATE_MOWING
true, // PEEP_STATE_SWEEPING
false, // PEEP_STATE_ENTERING_PARK
false, // PEEP_STATE_LEAVING_PARK
true, // PEEP_STATE_ANSWERING
false, // PEEP_STATE_FIXING
false, // PEEP_STATE_BUYING
true, // PEEP_STATE_WATCHING
true, // PEEP_STATE_EMPTYING_BIN
true, // PEEP_STATE_USING_BIN
true, // PEEP_STATE_WATERING
true, // PEEP_STATE_HEADING_TO_INSPECTION
false, // PEEP_STATE_INSPECTING
};
/**
*
* rct2: 0x00698827
* returns 1 on pickup (CF not set)
*/
int peep_can_be_picked_up(rct_peep* peep){
return peep_allow_pick_up[peep->state];
}
enum{
PEEP_FACE_OFFSET_ANGRY = 0,
PEEP_FACE_OFFSET_VERY_VERY_SICK,
PEEP_FACE_OFFSET_VERY_SICK,
PEEP_FACE_OFFSET_SICK,
PEEP_FACE_OFFSET_VERY_TIRED,
PEEP_FACE_OFFSET_TIRED,
PEEP_FACE_OFFSET_VERY_VERY_UNHAPPY,
PEEP_FACE_OFFSET_VERY_UNHAPPY,
PEEP_FACE_OFFSET_UNHAPPY,
PEEP_FACE_OFFSET_NORMAL,
PEEP_FACE_OFFSET_HAPPY,
PEEP_FACE_OFFSET_VERY_HAPPY,
PEEP_FACE_OFFSET_VERY_VERY_HAPPY,
};
const int face_sprite_small[] = {
SPR_PEEP_SMALL_FACE_ANGRY,
SPR_PEEP_SMALL_FACE_VERY_VERY_SICK,
SPR_PEEP_SMALL_FACE_VERY_SICK,
SPR_PEEP_SMALL_FACE_SICK,
SPR_PEEP_SMALL_FACE_VERY_TIRED,
SPR_PEEP_SMALL_FACE_TIRED,
SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY,
SPR_PEEP_SMALL_FACE_VERY_UNHAPPY,
SPR_PEEP_SMALL_FACE_UNHAPPY,
SPR_PEEP_SMALL_FACE_NORMAL,
SPR_PEEP_SMALL_FACE_HAPPY,
SPR_PEEP_SMALL_FACE_VERY_HAPPY,
SPR_PEEP_SMALL_FACE_VERY_VERY_HAPPY,
};
const int face_sprite_large[] = {
SPR_PEEP_LARGE_FACE_ANGRY_0,
SPR_PEEP_LARGE_FACE_VERY_VERY_SICK_0,
SPR_PEEP_LARGE_FACE_VERY_SICK_0,
SPR_PEEP_LARGE_FACE_SICK,
SPR_PEEP_LARGE_FACE_VERY_TIRED,
SPR_PEEP_LARGE_FACE_TIRED,
SPR_PEEP_LARGE_FACE_VERY_VERY_UNHAPPY,
SPR_PEEP_LARGE_FACE_VERY_UNHAPPY,
SPR_PEEP_LARGE_FACE_UNHAPPY,
SPR_PEEP_LARGE_FACE_NORMAL,
SPR_PEEP_LARGE_FACE_HAPPY,
SPR_PEEP_LARGE_FACE_VERY_HAPPY,
SPR_PEEP_LARGE_FACE_VERY_VERY_HAPPY,
};
static int get_face_sprite_offset(rct_peep *peep){
// ANGRY
if (peep->angriness > 0) return PEEP_FACE_OFFSET_ANGRY;
// VERY_VERY_SICK
if (peep->nausea > 200) return PEEP_FACE_OFFSET_VERY_VERY_SICK;
// VERY_SICK
if (peep->nausea > 170) return PEEP_FACE_OFFSET_VERY_SICK;
// SICK
if (peep->nausea > 140) return PEEP_FACE_OFFSET_SICK;
// VERY_TIRED
if (peep->energy < 46) return PEEP_FACE_OFFSET_VERY_TIRED;
// TIRED
if (peep->energy < 70) return PEEP_FACE_OFFSET_TIRED;
int offset = PEEP_FACE_OFFSET_VERY_VERY_UNHAPPY;
//There are 7 different happiness based faces
for (int i = 37; peep->happiness >= i; i += 37)
{
offset++;
}
return offset;
}
/**
* Function split into large and small sprite
* rct2: 0x00698721
*/
int get_peep_face_sprite_small(rct_peep *peep){
return face_sprite_small[get_face_sprite_offset(peep)];
}
/**
* Function split into large and small sprite
* rct2: 0x00698721
*/
int get_peep_face_sprite_large(rct_peep *peep){
return face_sprite_large[get_face_sprite_offset(peep)];
}
/**
*
* rct2: 0x0069A5A0
* tests if a peep's name matches a cheat code, normally returns using a register flag
* @param index (eax)
* @param ride (esi)
*/
int peep_check_easteregg_name(int index, rct_peep *peep)
{
char buffer[256];
format_string(buffer, 256, peep->name_string_idx, &peep->id);
return _stricmp(buffer, gPeepEasterEggNames[index]) == 0;
}
int peep_get_easteregg_name_id(rct_peep *peep)
{
char buffer[256];
int i;
format_string(buffer, 256, peep->name_string_idx, &peep->id);
for (i = 0; i < countof(gPeepEasterEggNames); i++)
if (_stricmp(buffer, gPeepEasterEggNames[i]) == 0)
return i;
return -1;
}
int peep_is_mechanic(rct_peep *peep)
{
return (
peep->sprite_identifier == SPRITE_IDENTIFIER_PEEP &&
peep->type == PEEP_TYPE_STAFF &&
peep->staff_type == STAFF_TYPE_MECHANIC
);
}
bool peep_has_item(rct_peep *peep, int peepItem)
{
if (peepItem < 32) {
return peep->item_standard_flags & (1u << peepItem);
} else {
return peep->item_extra_flags & (1u << (peepItem - 32));
}
}
static int peep_has_food_standard_flag(rct_peep* peep){
return peep->item_standard_flags &(
PEEP_ITEM_DRINK |
PEEP_ITEM_BURGER |
PEEP_ITEM_FRIES |
PEEP_ITEM_ICE_CREAM |
PEEP_ITEM_COTTON_CANDY |
PEEP_ITEM_PIZZA |
PEEP_ITEM_POPCORN |
PEEP_ITEM_HOT_DOG |
PEEP_ITEM_TENTACLE |
PEEP_ITEM_CANDY_APPLE |
PEEP_ITEM_DONUT |
PEEP_ITEM_COFFEE |
PEEP_ITEM_CHICKEN |
PEEP_ITEM_LEMONADE);
}
static int peep_has_food_extra_flag(rct_peep* peep){
return peep->item_extra_flags &(
PEEP_ITEM_PRETZEL |
PEEP_ITEM_CHOCOLATE |
PEEP_ITEM_ICED_TEA |
PEEP_ITEM_FUNNEL_CAKE |
PEEP_ITEM_BEEF_NOODLES |
PEEP_ITEM_FRIED_RICE_NOODLES |
PEEP_ITEM_WONTON_SOUP |
PEEP_ITEM_MEATBALL_SOUP |
PEEP_ITEM_FRUIT_JUICE |
PEEP_ITEM_SOYBEAN_MILK |
PEEP_ITEM_SU_JONGKWA |
PEEP_ITEM_SUB_SANDWICH |
PEEP_ITEM_COOKIE |
PEEP_ITEM_ROAST_SAUSAGE
);
}
/**
* To simplify check of 0x36BA3E0 and 0x11FF78
* returns 0 on no food.
*/
int peep_has_food(rct_peep* peep){
return peep_has_food_standard_flag(peep) ||
peep_has_food_extra_flag(peep);
}
static int peep_has_drink_standard_flag(rct_peep* peep){
return peep->item_standard_flags &(
PEEP_ITEM_DRINK |
PEEP_ITEM_COFFEE |
PEEP_ITEM_LEMONADE);
}
static int peep_has_drink_extra_flag(rct_peep* peep){
return peep->item_extra_flags &(
PEEP_ITEM_CHOCOLATE |
PEEP_ITEM_ICED_TEA |
PEEP_ITEM_FRUIT_JUICE |
PEEP_ITEM_SOYBEAN_MILK |
PEEP_ITEM_SU_JONGKWA
);
}
/**
* To simplify check of NOT(0x12BA3C0 and 0x118F48)
* returns 0 on no food.
*/
static int peep_has_drink(rct_peep* peep){
return peep_has_drink_standard_flag(peep) ||
peep_has_drink_extra_flag(peep);
}
static int peep_empty_container_standard_flag(rct_peep* peep){
return peep->item_standard_flags &(
PEEP_ITEM_EMPTY_CAN |
PEEP_ITEM_EMPTY_BURGER_BOX |
PEEP_ITEM_EMPTY_CUP |
PEEP_ITEM_RUBBISH |
PEEP_ITEM_EMPTY_BOX |
PEEP_ITEM_EMPTY_BOTTLE
);
}
static int peep_empty_container_extra_flag(rct_peep* peep){
return peep->item_extra_flags &(
PEEP_ITEM_EMPTY_BOWL_RED |
PEEP_ITEM_EMPTY_DRINK_CARTON |
PEEP_ITEM_EMPTY_JUICE_CUP |
PEEP_ITEM_EMPTY_BOWL_BLUE
);
}
static int peep_has_empty_container(rct_peep* peep){
return peep_empty_container_standard_flag(peep) ||
peep_empty_container_extra_flag(peep);
}
/* Simplifies 0x690582. Returns 1 if should find bench*/
static int peep_should_find_bench(rct_peep* peep){
if (!(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)){
if (peep_has_food(peep)){
if (peep->hunger < 128 || peep->happiness < 128){
if (!(peep->next_var_29 & 0x1C)){
return 1;
}
}
}
if (peep->nausea <= 170 && peep->energy > 50){
return 0;
}
if (!(peep->next_var_29 & 0x1C)){
return 1;
}
}
return 0;
}
/**
*
* rct2: 0x699F5A
* al:thought_type
* ah:thought_arguments
* esi: peep
*/
void peep_insert_new_thought(rct_peep *peep, uint8 thought_type, uint8 thought_arguments)
{
uint8 action = PeepThoughtToActionMap[thought_type].action;
if (action != 0xFF && peep->action >= 254){
peep->action = action;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
for (int i = 0; i < PEEP_MAX_THOUGHTS; ++i){
rct_peep_thought* thought = &peep->thoughts[i];
// Remove the oldest thought by setting it to NONE.
if (thought->type == PEEP_THOUGHT_TYPE_NONE) break;
if (thought->type == thought_type && thought->item == thought_arguments){
// If the thought type has not changed then we need to move
// it to the top of the thought list. This is done by first removing the
// existing thought and placing it at the top.
if (i < PEEP_MAX_THOUGHTS - 2) {
memmove(thought, thought + 1, sizeof(rct_peep_thought)*(PEEP_MAX_THOUGHTS - i - 1));
}
break;
}
}
memmove(&peep->thoughts[1], &peep->thoughts[0], sizeof(rct_peep_thought)*(PEEP_MAX_THOUGHTS - 1));
peep->thoughts[0].type = thought_type;
peep->thoughts[0].item = thought_arguments;
peep->thoughts[0].var_2 = 0;
peep->thoughts[0].var_3 = 0;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS;
}
/**
*
* rct2: 0x00699FE3
* Stops peeps that are having thoughts
* such as "I'm hungry" after visiting a food shop.
* Works for Thirst/Hungry/Low Money/Bathroom
*/
static void peep_stop_purchase_thought(rct_peep* peep, uint8 ride_type){
uint8 thought_type = PEEP_THOUGHT_TYPE_HUNGRY;
if (!ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_SELLS_FOOD)){
thought_type = PEEP_THOUGHT_TYPE_THIRSTY;
if (!ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_SELLS_DRINKS)){
thought_type = PEEP_THOUGHT_TYPE_RUNNING_OUT;
if (ride_type != RIDE_TYPE_CASH_MACHINE){
thought_type = PEEP_THOUGHT_TYPE_BATHROOM;
if (!ride_type_has_flag(ride_type, RIDE_TYPE_FLAG_IS_BATHROOM)){
return;
}
}
}
}
//Remove the related thought
for (int i = 0; i < PEEP_MAX_THOUGHTS; ++i){
rct_peep_thought* thought = &peep->thoughts[i];
if (thought->type == PEEP_THOUGHT_TYPE_NONE) break;
if (thought->type != thought_type)continue;
if (i < PEEP_MAX_THOUGHTS - 1) {
memmove(thought, thought + 1, sizeof(rct_peep_thought)*(PEEP_MAX_THOUGHTS - i - 1));
}
peep->thoughts[PEEP_MAX_THOUGHTS - 1].type = PEEP_THOUGHT_TYPE_NONE;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS;
i--;
}
}
void peep_set_map_tooltip(rct_peep *peep)
{
if (peep->type == PEEP_TYPE_GUEST) {
set_map_tooltip_format_arg(0, rct_string_id, peep->peep_flags & PEEP_FLAGS_TRACKING ? STR_TRACKED_GUEST_MAP_TIP : STR_GUEST_MAP_TIP);
set_map_tooltip_format_arg(2, uint32, get_peep_face_sprite_small(peep));
set_map_tooltip_format_arg(6, rct_string_id, peep->name_string_idx);
set_map_tooltip_format_arg(8, uint32, peep->id);
uint32 arg0, arg1;
get_arguments_from_action(peep, &arg0, &arg1);
set_map_tooltip_format_arg(12, uint32, arg0);
set_map_tooltip_format_arg(16, uint32, arg1);
} else {
set_map_tooltip_format_arg(0, rct_string_id, STR_STAFF_MAP_TIP);
set_map_tooltip_format_arg(2, rct_string_id, peep->name_string_idx);
set_map_tooltip_format_arg(4, uint32, peep->id);
uint32 arg0, arg1;
get_arguments_from_action(peep, &arg0, &arg1);
set_map_tooltip_format_arg(8, uint32, arg0);
set_map_tooltip_format_arg(12, uint32, arg1);
}
}
void sub_693BAB(rct_peep* peep) {
// TBD: Add nextActionSpriteType as function parameter and make peep->next_action_sprite_type obsolete?
uint8 nextActionSpriteType = peep->next_action_sprite_type;
if (nextActionSpriteType != peep->action_sprite_type) {
invalidate_sprite_2((rct_sprite*)peep);
peep->action_sprite_type = nextActionSpriteType;
const rct_sprite_bounds* spriteBounds = g_sprite_entries[peep->sprite_type].sprite_bounds;
peep->sprite_width = spriteBounds[nextActionSpriteType].sprite_width;
peep->sprite_height_negative = spriteBounds[nextActionSpriteType].sprite_height_negative;
peep->sprite_height_positive = spriteBounds[nextActionSpriteType].sprite_height_positive;
invalidate_sprite_2((rct_sprite*)peep);
}
}
/**
*
* rct2: 0x00693CBB
*/
static int peep_update_queue_position(rct_peep* peep){
peep->time_in_queue++;
if (peep->next_in_queue == 0xFFFF)
return 0;
rct_peep* peep_next = GET_PEEP(peep->next_in_queue);
sint16 x_diff = abs(peep_next->x - peep->x);
sint16 y_diff = abs(peep_next->y - peep->y);
sint16 z_diff = abs(peep_next->z - peep->z);
if (z_diff > 10)
return 0;
if (x_diff < y_diff){
sint16 temp_x = x_diff;
x_diff = y_diff;
y_diff = temp_x;
}
x_diff += y_diff / 2;
if (x_diff > 7){
if (x_diff > 13){
if ((peep->x & 0xFFE0) != (peep_next->x & 0xFFE0) ||
(peep->y & 0xFFE0) != (peep_next->y & 0xFFE0))
return 0;
}
if (peep->sprite_direction != peep_next->sprite_direction)
return 0;
switch (peep_next->sprite_direction / 8){
case 0:
if (peep->x >= peep_next->x)
return 0;
break;
case 1:
if (peep->y <= peep_next->y)
return 0;
break;
case 2:
if (peep->x <= peep_next->x)
return 0;
break;
case 3:
if (peep->y >= peep_next->y)
return 0;
break;
}
}
sint16 xy_dist, x, y;
if (peep->action < PEEP_ACTION_NONE_1)
peep_update_action(&x, &y, &xy_dist, peep);
if (peep->action != PEEP_ACTION_NONE_2)
return 1;
peep->action = PEEP_ACTION_NONE_1;
peep->next_action_sprite_type = 2;
if (_unk_F1AEF1 != PEEP_ACTION_NONE_1)
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
/**
*
* rct2: 0x00693EF2
*/
static int peep_return_to_center_of_tile(rct_peep* peep){
peep->direction ^= (1 << 1);
peep->destination_x = (peep->x & 0xFFE0) + 16;
peep->destination_y = (peep->y & 0xFFE0) + 16;
peep->destination_tolerence = 5;
return 1;
}
/**
*
* rct2: 0x00693f2C
*/
static int peep_interact_with_entrance(rct_peep* peep, sint16 x, sint16 y, rct_map_element* map_element){
uint8 entranceType = map_element->properties.entrance.type;
uint8 rideIndex = map_element->properties.entrance.ride_index;
if (entranceType == ENTRANCE_TYPE_RIDE_EXIT){
_unk_F1EE18 |= F1EE18_RIDE_EXIT;
_peepRideEntranceExitElement = map_element;
}
else if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE){
_unk_F1EE18 |= F1EE18_RIDE_ENTRANCE;
_peepRideEntranceExitElement = map_element;
}
if (entranceType == ENTRANCE_TYPE_RIDE_EXIT){
peep->var_79 = 0xFF;
return peep_return_to_center_of_tile(peep);
}
if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE){
if (peep->type == PEEP_TYPE_STAFF){
peep->var_79 = 0xFF;
return peep_return_to_center_of_tile(peep);
}
if (peep->state == PEEP_STATE_QUEUING){
peep->sub_state = 11;
peep->action_sprite_image_offset = _unk_F1AEF0;
return 1;
}
if (peep->var_79 == rideIndex)
return peep_return_to_center_of_tile(peep);
peep->var_F4 = 0;
uint8 stationNum = (map_element->properties.entrance.index >> 4) & 0x7;
if (!peep_should_go_on_ride(peep, rideIndex, stationNum, 0)){
peep->var_79 = rideIndex;
return peep_return_to_center_of_tile(peep);
}
peep->action_sprite_image_offset = _unk_F1AEF0;
peep->var_79 = rideIndex;
rct_ride* ride = get_ride(rideIndex);
uint16 previous_last = ride->last_peep_in_queue[stationNum];
ride->last_peep_in_queue[stationNum] = peep->sprite_index;
peep->next_in_queue = previous_last;
ride->queue_length[stationNum]++;
peep_decrement_num_riders(peep);
peep->current_ride = rideIndex;
peep->current_ride_station = stationNum;
peep->state = PEEP_STATE_QUEUING;
peep->days_in_queue = 0;
peep_window_state_update(peep);
peep->sub_state = 11;
peep->time_in_queue = 0;
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
if (gConfigNotifications.guest_queuing_for_ride) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_PEEP_JOINED_QUEUE_FOR_X, peep->sprite_index);
}
}
return 1;
}
else{
// PARK_ENTRANCE
if (peep->type == PEEP_TYPE_STAFF)
return peep_return_to_center_of_tile(peep);
// If not the center of the entrance arch
if (map_element->properties.entrance.index & 0xF)
return peep_return_to_center_of_tile(peep);
uint8 entranceDirection = map_element->type & MAP_ELEMENT_DIRECTION_MASK;
if (entranceDirection != peep->direction){
if ((entranceDirection ^ (1 << 1)) != peep->direction)
return peep_return_to_center_of_tile(peep);
// Peep is leaving the park.
if (peep->state != PEEP_STATE_WALKING)
return peep_return_to_center_of_tile(peep);
if (!(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)){
// If the park is open and leaving flag isn't set return to center
if (gParkFlags & PARK_FLAGS_PARK_OPEN)
return peep_return_to_center_of_tile(peep);
}
peep->destination_x += TileDirectionDelta[peep->direction].x;
peep->destination_y += TileDirectionDelta[peep->direction].y;
peep->destination_tolerence = 9;
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_LEAVING_PARK;
peep_window_state_update(peep);
peep->var_37 = 0;
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
if (gConfigNotifications.guest_left_park) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_LEFT_PARK, peep->sprite_index);
}
}
return 1;
}
// Peep is entering the park.
if (peep->state != PEEP_STATE_ENTERING_PARK)
return peep_return_to_center_of_tile(peep);
if (!(gParkFlags & PARK_FLAGS_PARK_OPEN)){
peep->state = PEEP_STATE_LEAVING_PARK;
peep->var_37 = 1;
gNumGuestsHeadingForPark--;
peep_window_state_update(peep);
return peep_return_to_center_of_tile(peep);
}
uint8 entranceIndex = 0;
while (1){
if (gParkEntranceX[entranceIndex] == (x & 0xFFE0) &&
gParkEntranceY[entranceIndex] == (y & 0xFFE0))
break;
entranceIndex++;
}
sint16 z = gParkEntranceZ[entranceIndex] / 8;
entranceDirection = gParkEntranceDirection[entranceIndex];
sint16 next_x = (x & 0xFFE0) + TileDirectionDelta[entranceDirection].x;
sint16 next_y = (y & 0xFFE0) + TileDirectionDelta[entranceDirection].y;
uint8 found = 0;
rct_map_element* nextMapElement = map_get_first_element_at(next_x / 32, next_y / 32);
do{
if (map_element_get_type(nextMapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (nextMapElement->type & 1)
continue;
if (footpath_element_is_sloped(nextMapElement)){
uint8 slopeDirection = footpath_element_get_slope_direction(nextMapElement);
if (slopeDirection == entranceDirection){
if (z != nextMapElement->base_height){
continue;
}
found = 1;
break;
}
if ((slopeDirection ^ (1 << 1)) != entranceDirection)
continue;
if (z - 2 != nextMapElement->base_height)
continue;
found = 1;
break;
}
else{
if (z != nextMapElement->base_height){
continue;
}
found = 1;
break;
}
} while (!map_element_is_last_for_tile(nextMapElement++));
if (!found){
peep->state = PEEP_STATE_LEAVING_PARK;
peep->var_37 = 1;
gNumGuestsHeadingForPark--;
peep_window_state_update(peep);
return peep_return_to_center_of_tile(peep);
}
money16 entranceFee = park_get_entrance_fee();
if (entranceFee != 0) {
if (peep->item_standard_flags & PEEP_ITEM_VOUCHER){
if (peep->voucher_type == VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE){
entranceFee /= 2;
peep->item_standard_flags &= ~PEEP_ITEM_VOUCHER;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
else if (peep->voucher_type == VOUCHER_TYPE_PARK_ENTRY_FREE){
entranceFee = 0;
peep->item_standard_flags &= ~PEEP_ITEM_VOUCHER;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
}
if (entranceFee > peep->cash_in_pocket){
peep->state = PEEP_STATE_LEAVING_PARK;
peep->var_37 = 1;
gNumGuestsHeadingForPark--;
peep_window_state_update(peep);
return peep_return_to_center_of_tile(peep);
}
gTotalIncomeFromAdmissions += entranceFee;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_PARK_ENTRANCE_TICKETS;
peep_spend_money(peep, &peep->paid_to_enter, entranceFee);
peep->peep_flags |= PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY;
}
gTotalAdmissions++;
window_invalidate_by_number(WC_PARK_INFORMATION, 0);
peep->var_37 = 1;
peep->destination_x += TileDirectionDelta[peep->direction].x;
peep->destination_y += TileDirectionDelta[peep->direction].y;
peep->destination_tolerence = 7;
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, peep->z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
}
/**
*
* rct2: 0x006946D8
*/
static int peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct_map_element* map_element, bool vandalism){
peep->next_x = (x & 0xFFE0);
peep->next_y = (y & 0xFFE0);
peep->next_z = map_element->base_height;
peep->next_var_29 = map_element->properties.path.type & 7;
sint16 z = peep_get_height_on_slope(peep, x, y);
if (peep->type == PEEP_TYPE_STAFF){
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
uint8 var_EF = (peep->var_EF * 2) & 0x3F;
peep->var_EF &= 0xC0;
peep->var_EF |= var_EF;
if (vandalism == true){
peep->var_EF |= 1;
if (peep->var_EF & 0x3E &&
!(peep->var_EF & 0xC0)){
if ((scenario_rand() & 0xFFFF) <= 10922){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_VANDALISM, 0xFF);
peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17);
}
peep->var_EF |= 0xC0;
}
}
if (peep->var_EF & 0xC0 &&
(scenario_rand()&0xFFFF) <= 4369){
peep->var_EF -= 0x40;
}
uint16 crowded = 0;
uint8 litter_count = 0;
uint8 sick_count = 0;
uint16 sprite_id = sprite_get_first_in_quadrant(x, y);
for (rct_sprite* sprite; sprite_id != 0xFFFF; sprite_id = sprite->unknown.next_in_quadrant){
sprite = get_sprite(sprite_id);
if (sprite->unknown.sprite_identifier == SPRITE_IDENTIFIER_PEEP){
rct_peep* other_peep = (rct_peep*)sprite;
if (other_peep->state != PEEP_STATE_WALKING)
continue;
if (abs(other_peep->z - peep->next_z * 8) > 16)
continue;
crowded++;
continue;
}
else if (sprite->unknown.sprite_identifier == SPRITE_IDENTIFIER_LITTER){
rct_litter* litter = (rct_litter*)sprite;
if (abs(litter->z - peep->next_z * 8) > 16)
continue;
litter_count++;
if (litter->type != LITTER_TYPE_SICK && litter->type != LITTER_TYPE_SICK_ALT)
continue;
litter_count--;
sick_count++;
}
}
if (crowded >= 10 &&
peep->state == PEEP_STATE_WALKING &&
(scenario_rand() & 0xFFFF) <= 21845){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CROWDED, 0xFF);
peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 14);
}
litter_count = min(3, litter_count);
sick_count = min(3, sick_count);
uint8 disgusting_time = peep->disgusting_count & 0xC0;
uint8 disgusting_count = ((peep->disgusting_count & 0xF) << 2) | sick_count;
peep->disgusting_count = disgusting_count | disgusting_time;
if (disgusting_time & 0xC0 &&
(scenario_rand() & 0xFFFF) <= 4369){
// Reduce the disgusting time
peep->disgusting_count -= 0x40;
}
else{
uint8 total_sick = 0;
for (uint8 time = 0; time < 3; time++){
total_sick += (disgusting_count >> (2 * time)) & 0x3;
}
if (total_sick >= 3 &&
(scenario_rand() & 0xFFFF) <= 10922){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_PATH_DISGUSTING, 0xFF);
peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17);
// Reset disgusting time
peep->disgusting_count |= 0xC0;
}
}
uint8 litter_time = peep->litter_count & 0xC0;
litter_count = ((peep->litter_count & 0xF) << 2) | litter_count;
peep->litter_count = litter_count | litter_time;
if (litter_time & 0xC0 &&
(scenario_rand() & 0xFFFF) <= 4369){
// Reduce the litter time
peep->litter_count -= 0x40;
}
else{
uint8 total_litter = 0;
for (uint8 time = 0; time < 3; time++){
total_litter += (litter_count >> (2 * time)) & 0x3;
}
if (total_litter >= 3 &&
(scenario_rand() & 0xFFFF) <= 10922){
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_LITTER, 0xFF);
peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17);
// Reset litter time
peep->litter_count |= 0xC0;
}
}
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
/**
*
* rct2: 0x0069455E
*/
static int peep_interact_with_path(rct_peep* peep, sint16 x, sint16 y, rct_map_element* map_element){
// 0x00F1AEE2
bool vandalism_present = false;
if (footpath_element_has_path_scenery(map_element) &&
(map_element->flags & MAP_ELEMENT_FLAG_BROKEN) &&
(map_element->properties.path.edges & 0xF) != 0xF){
vandalism_present = 1;
}
sint16 z = map_element->base_height * 8;
if (!map_is_location_owned(x, y, z)){
if (peep->outside_of_park == 0)
return peep_return_to_center_of_tile(peep);
}
else{
if (peep->outside_of_park == 1)
return peep_return_to_center_of_tile(peep);
}
if (peep->type == PEEP_TYPE_GUEST &&
footpath_element_is_queue(map_element)){
uint8 rideIndex = map_element->properties.path.ride_index;
if (rideIndex == 0xFF){
peep->var_79 = 0xFF;
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
if (peep->state == PEEP_STATE_QUEUING){
if (peep->current_ride == rideIndex){
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
if (peep->var_79 == rideIndex){
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
peep->var_F4 = 0;
uint8 stationNum = (map_element->properties.path.additions & 0x70) >> 4;
if (!peep_should_go_on_ride(peep, rideIndex, stationNum, PEEP_RIDE_DECISION_AT_QUEUE)){
peep->var_79 = rideIndex;
return peep_return_to_center_of_tile(peep);
}
peep->var_79 = rideIndex;
rct_ride* ride = get_ride(rideIndex);
uint16 old_last_peep = ride->last_peep_in_queue[stationNum];
ride->last_peep_in_queue[stationNum] = peep->sprite_index;
peep->next_in_queue = old_last_peep;
ride->queue_length[stationNum]++;
peep_decrement_num_riders(peep);
peep->current_ride = rideIndex;
peep->current_ride_station = stationNum;
peep->state = PEEP_STATE_QUEUING;
peep->days_in_queue = 0;
peep_window_state_update(peep);
peep->sub_state = 10;
peep->destination_tolerence = 2;
peep->time_in_queue = 0;
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
if (gConfigNotifications.guest_queuing_for_ride) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_PEEP_JOINED_QUEUE_FOR_X, peep->sprite_index);
}
}
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
else{
peep->var_79 = 0xFF;
if (peep->state == PEEP_STATE_QUEUING){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
}
return peep_footpath_move_forward(peep, x, y, map_element, vandalism_present);
}
}
/**
*
* rct2: 0x00693F70
*/
static int peep_interact_with_shop(rct_peep* peep, sint16 x, sint16 y, rct_map_element* map_element){
uint8 rideIndex = map_element->properties.track.ride_index;
rct_ride* ride = get_ride(rideIndex);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP))
return 0;
if (peep->type == PEEP_TYPE_STAFF)
return peep_return_to_center_of_tile(peep);
peep->var_F4 = 0;
if (ride->status != RIDE_STATUS_OPEN)
return peep_return_to_center_of_tile(peep);
if (peep->var_79 == rideIndex)
return peep_return_to_center_of_tile(peep);
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)
return peep_return_to_center_of_tile(peep);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_PEEP_SHOULD_GO_INSIDE_FACILITY)){
peep->var_F4 = 0;
if (!peep_should_go_on_ride(peep, rideIndex, 0, 0))
return peep_return_to_center_of_tile(peep);
money16 cost = ride->price;
if (cost != 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) {
ride->total_profit += cost;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_PARK_RIDE_TICKETS;
peep_spend_money(peep, NULL, cost);
}
peep->destination_x = (x & 0xFFE0) + 16;
peep->destination_y = (y & 0xFFE0) + 16;
peep->destination_tolerence = 3;
peep_decrement_num_riders(peep);
peep->current_ride = rideIndex;
peep->state = PEEP_STATE_ENTERING_RIDE;
peep->sub_state = 19;
peep_window_state_update(peep);
peep->time_on_ride = 0;
ride->cur_num_customers++;
if (peep->peep_flags & PEEP_FLAGS_TRACKING){
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
rct_string_id string_id = ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) ? STR_PEEP_TRACKING_PEEP_IS_IN_X : STR_PEEP_TRACKING_PEEP_IS_ON_X;
if (gConfigNotifications.guest_used_facility) {
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, string_id, peep->sprite_index);
}
}
return 1;
}
else{
if (peep->guest_heading_to_ride_id == rideIndex)
peep->guest_heading_to_ride_id = 0xFF;
peep->action_sprite_image_offset = _unk_F1AEF0;
peep_decrement_num_riders(peep);
peep->current_ride = rideIndex;
peep->state = PEEP_STATE_BUYING;
peep->sub_state = 0;
peep_window_state_update(peep);
return 1;
}
}
/**
*
* rct2: 0x0069524E
*/
static int peep_move_one_tile(uint8 direction, rct_peep* peep){
assert(direction <= 3);
sint16 x = peep->next_x;
sint16 y = peep->next_y;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
if (x >= 8192 || y >= 8192){
// This could loop!
return guest_surface_path_finding(peep);
}
peep->direction = direction;
peep->destination_x = x + 16;
peep->destination_y = y + 16;
peep->destination_tolerence = 2;
if (peep->state != PEEP_STATE_QUEUING){
peep->destination_tolerence = (scenario_rand() & 7) + 2;
}
return 0;
}
/**
*
* rct2: 0x00694C41
*/
static int guest_surface_path_finding(rct_peep* peep){
sint16 x = peep->next_x;
sint16 y = peep->next_y;
sint16 z = peep->next_z;
uint8 randDirection = scenario_rand() & 3;
if (!fence_in_the_way(x, y, z, z + 4, randDirection)){
x += TileDirectionDelta[randDirection].x;
y += TileDirectionDelta[randDirection].y;
uint8 backwardsDirection = randDirection ^ (1 << 1);
if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)){
if (!map_surface_is_blocked(x, y)){
return peep_move_one_tile(randDirection, peep);
}
}
}
randDirection++;
uint8 rand_backwards = scenario_rand() & 1;
if (rand_backwards){
randDirection -= 2;
}
randDirection &= 3;
x = peep->next_x;
y = peep->next_y;
if (!fence_in_the_way(x, y, z, z + 4, randDirection)){
x += TileDirectionDelta[randDirection].x;
y += TileDirectionDelta[randDirection].y;
uint8 backwardsDirection = randDirection ^ (1 << 1);
if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)){
if (!map_surface_is_blocked(x, y)){
return peep_move_one_tile(randDirection, peep);
}
}
}
randDirection -= 2;
randDirection &= 3;
x = peep->next_x;
y = peep->next_y;
if (!fence_in_the_way(x, y, z, z + 4, randDirection)){
x += TileDirectionDelta[randDirection].x;
y += TileDirectionDelta[randDirection].y;
uint8 backwardsDirection = randDirection ^ (1 << 1);
if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)){
if (!map_surface_is_blocked(x, y)){
return peep_move_one_tile(randDirection, peep);
}
}
}
randDirection--;
if (rand_backwards){
randDirection += 2;
}
randDirection &= 3;
return peep_move_one_tile(randDirection, peep);
}
static rct_map_element* get_banner_on_path(rct_map_element *path_element)
{
// This is an improved version of original.
// That only checked for one fence in the way.
if (map_element_is_last_for_tile(path_element))
return NULL;
rct_map_element *bannerElement = path_element + 1;
do {
// Path on top, so no banners
if (map_element_get_type(bannerElement) == MAP_ELEMENT_TYPE_PATH)
return NULL;
// Found a banner
if (map_element_get_type(bannerElement) == MAP_ELEMENT_TYPE_BANNER)
return bannerElement;
// Last element so there cant be any other banners
if (map_element_is_last_for_tile(bannerElement))
return NULL;
} while (bannerElement++);
return NULL;
}
static int banner_clear_path_edges(rct_map_element *mapElement, int edges)
{
if (_peepPathFindIsStaff) return edges;
rct_map_element *bannerElement = get_banner_on_path(mapElement);
if (bannerElement != NULL) {
do {
edges &= bannerElement->properties.banner.flags;
} while ((bannerElement = get_banner_on_path(bannerElement)) != NULL);
}
return edges;
}
/**
* Gets the connected edges of a path that are permitted (i.e. no 'no entry' signs)
*/
static int path_get_permitted_edges(rct_map_element *mapElement)
{
return banner_clear_path_edges(mapElement, mapElement->properties.path.edges) & 0x0F;
}
static bool is_valid_path_z_and_direction(rct_map_element *mapElement, int currentZ, int currentDirection)
{
if (footpath_element_is_sloped(mapElement)) {
int slopeDirection = footpath_element_get_slope_direction(mapElement);
if (slopeDirection == currentDirection) {
if (currentZ != mapElement->base_height) return false;
} else {
slopeDirection ^= 2;
if (slopeDirection != currentDirection) return false;
if (currentZ != mapElement->base_height + 2) return false;
}
} else {
if (currentZ != mapElement->base_height) return false;
}
return true;
}
/**
*
* Returns:
* 1 - PATH_SEARCH_WIDE (path with wide flag set)
* 4 - PATH_SEARCH_RIDE_QUEUE (queue path connected to a ride)
* 11 - PATH_SEARCH_OTHER (other path than the above)
* 12 - PATH_SEARCH_FAILED (no path element found)
*
* rct2: 0x00694BAE
*
* Returns the type of the next footpath tile a peep can get to from x,y,z /
* inputMapElement in the given direction.
*/
static uint8 footpath_element_next_in_direction(sint16 x, sint16 y, sint16 z, rct_map_element *mapElement, uint8 chosenDirection)
{
rct_map_element *nextMapElement;
if (footpath_element_is_sloped(mapElement)) {
if (footpath_element_get_slope_direction(mapElement) == chosenDirection) {
z += 2;
}
}
x += TileDirectionDelta[chosenDirection].x;
y += TileDirectionDelta[chosenDirection].y;
nextMapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (map_element_get_type(nextMapElement) != MAP_ELEMENT_TYPE_PATH) continue;
if (!is_valid_path_z_and_direction(nextMapElement, z, chosenDirection)) continue;
if (footpath_element_is_wide(nextMapElement)) return PATH_SEARCH_WIDE;
// Only queue tiles that are connected to a ride are returned as ride queues.
if (footpath_element_is_queue(nextMapElement) && nextMapElement->properties.path.ride_index != 0xFF) return PATH_SEARCH_RIDE_QUEUE;
return PATH_SEARCH_OTHER;
} while (!map_element_is_last_for_tile(nextMapElement++));
return PATH_SEARCH_FAILED;
}
/**
*
* Returns:
* 0 - PATH_SEARCH_DEAD_END (path is a dead end, i.e. < 2 edges)
* 1 - PATH_SEARCH_WIDE (path with wide flag set)
* 3 - PATH_SEARCH_JUNCTION (path is a junction, i.e. > 2 edges)
* 5 - PATH_SEARCH_RIDE_ENTRANCE (map element is a ride entrance)
* 6 - PATH_SEARCH_RIDE_EXIT (map element is a ride exit)
* 7 - PATH_SEARCH_PARK_EXIT park entrance / exit (map element is a park entrance/exit)
* 8 - PATH_SEARCH_SHOP_ENTRANCE (map element is a shop entrance)
* 9 - PATH_SEARCH_LIMIT_REACHED (search limit reached without reaching path end)
* 12 - PATH_SEARCH_FAILED (no path element found)
* For return values 5, 6 & 8 the rideIndex is stored in outRideIndex.
*
* rct2: 0x006949B9
*
* This is the recursive portion of footpath_element_destination_in_direction().
*/
static uint8 footpath_element_dest_in_dir(
sint16 x, sint16 y, sint16 z, rct_map_element *inputMapElement, uint8 chosenDirection, uint8 *outRideIndex,
int level
) {
rct_map_element *mapElement;
int direction;
if (level > 25) return PATH_SEARCH_LIMIT_REACHED;
x += TileDirectionDelta[chosenDirection].x;
y += TileDirectionDelta[chosenDirection].y;
mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (mapElement->flags & MAP_ELEMENT_FLAG_GHOST) continue;
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_TRACK:
if (z != mapElement->base_height) continue;
int rideIndex = mapElement->properties.track.ride_index;
rct_ride *ride = get_ride(rideIndex);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) {
*outRideIndex = rideIndex;
return PATH_SEARCH_SHOP_ENTRANCE;
}
break;
case MAP_ELEMENT_TYPE_ENTRANCE:
if (z != mapElement->base_height) continue;
switch (mapElement->properties.entrance.type) {
case ENTRANCE_TYPE_RIDE_ENTRANCE:
direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
if (direction == chosenDirection) {
*outRideIndex = mapElement->properties.entrance.ride_index;
return PATH_SEARCH_RIDE_ENTRANCE;
}
break;
case ENTRANCE_TYPE_RIDE_EXIT:
direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
if (direction == chosenDirection) {
*outRideIndex = mapElement->properties.entrance.ride_index;
return PATH_SEARCH_RIDE_EXIT;
}
break;
case ENTRANCE_TYPE_PARK_ENTRANCE:
return PATH_SEARCH_PARK_EXIT;
}
break;
case MAP_ELEMENT_TYPE_PATH:
if (!is_valid_path_z_and_direction(mapElement, z, chosenDirection)) continue;
if (footpath_element_is_wide(mapElement)) return PATH_SEARCH_WIDE;
uint8 edges = path_get_permitted_edges(mapElement);
edges &= ~(1 << (chosenDirection ^ 2));
z = mapElement->base_height;
for (direction = 0; direction < 4; direction++) {
if (!(edges & (1 << direction))) continue;
edges &= ~(1 << direction);
if (edges != 0) return PATH_SEARCH_JUNCTION;
if (footpath_element_is_sloped(mapElement)) {
if (footpath_element_get_slope_direction(mapElement) == direction) {
z += 2;
}
}
return footpath_element_dest_in_dir(x, y, z, mapElement, direction, outRideIndex, level + 1);
}
return PATH_SEARCH_DEAD_END;
}
} while (!map_element_is_last_for_tile(mapElement++));
return PATH_SEARCH_FAILED;
}
/**
* Returns:
* 0 - PATH_SEARCH_DEAD_END (path is a dead end, i.e. < 2 edges)
* 1 - PATH_SEARCH_WIDE (path with wide flag set)
* 3 - PATH_SEARCH_JUNCTION (path is a junction, i.e. > 2 edges)
* 5 - PATH_SEARCH_RIDE_ENTRANCE (map element is a ride entrance)
* 6 - PATH_SEARCH_RIDE_EXIT (map element is a ride exit)
* 7 - PATH_SEARCH_PARK_EXIT park entrance / exit (map element is a park entrance/exit)
* 8 - PATH_SEARCH_SHOP_ENTRANCE (map element is a shop entrance)
* 9 - PATH_SEARCH_LIMIT_REACHED (search limit reached without reaching path end)
* 12 - PATH_SEARCH_FAILED (no path element found)
* For return values 5, 6 & 8 the rideIndex is stored in outRideIndex.
*
* rct2: 0x006949A4
*
* Returns the destination tile type a peep can get to from x,y,z /
* inputMapElement in the given direction following single width paths only
* and stopping as soon as a path junction is encountered.
* Note that a junction is a path with > 2 reachable neighbouring path tiles,
* so wide paths have LOTS of junctions.
* This is useful for finding out what is at the end of a short single
* width path, for example that leads from a ride exit back to the main path.
*/
static uint8 footpath_element_destination_in_direction(sint16 x, sint16 y, sint16 z, rct_map_element *inputMapElement, uint8 chosenDirection, uint8 *outRideIndex)
{
if (footpath_element_is_sloped(inputMapElement)) {
if (footpath_element_get_slope_direction(inputMapElement) == chosenDirection) {
z += 2;
}
}
return footpath_element_dest_in_dir(x, y, z, inputMapElement, chosenDirection, outRideIndex, 0);
}
/**
*
* rct2: 0x00695225
*/
static int guest_path_find_aimless(rct_peep* peep, uint8 edges){
if (scenario_rand() & 1){
// If possible go straight
if (edges & (1 << peep->direction)){
return peep_move_one_tile(peep->direction, peep);
}
}
while (1){
uint8 direction = scenario_rand() & 3;
// Otherwise go in a random direction allowed from the tile.
if (edges & (1 << direction)){
return peep_move_one_tile(direction, peep);
}
}
}
/**
*
* rct2: 0x0069A60A
*/
static uint8 peep_pathfind_get_max_number_junctions(rct_peep* peep){
if (peep->type == PEEP_TYPE_STAFF)
return 8;
// PEEP_FLAGS_2? It's cleared here but not set anywhere!
if ((peep->peep_flags & PEEP_FLAGS_2)){
if ((scenario_rand() & 0xFFFF) <= 7281)
peep->peep_flags &= ~PEEP_FLAGS_2;
return 8;
}
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK &&
peep->peep_is_lost_countdown < 90){
return 8;
}
if (peep->item_standard_flags & PEEP_ITEM_MAP)
return 7;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)
return 7;
return 5;
}
/**
* Returns if the path as xzy is a 'thin' junction.
* A junction is considered 'thin' if it has more than 2 edges
* leading to non-wide path elements; edges leading to non-path elements
* (e.g. ride/shop entrances) or ride queues are not counted, since entrances
* and ride queues coming off a path should not result in the path being
* considered a junction.
*/
static bool path_is_thin_junction(rct_map_element *path, sint16 x, sint16 y, uint8 z) {
uint8 edges = path_get_permitted_edges(path);
int test_edge = bitscanforward(edges);
if (test_edge == -1) return false;
bool thin_junction = false;
int thin_count = 0;
do
{
int fp_result = footpath_element_next_in_direction(x, y, z, path, test_edge);
/* Ignore non-paths (e.g. ride entrances, shops), wide paths
* and ride queues (per ignoreQueues) when counting
* neighbouring tiles. */
if (fp_result != PATH_SEARCH_FAILED &&
fp_result != PATH_SEARCH_WIDE &&
fp_result != PATH_SEARCH_RIDE_QUEUE) {
thin_count++;
}
if (thin_count > 2) {
thin_junction = true;
break;
}
edges &= ~(1 << test_edge);
} while ((test_edge = bitscanforward(edges)) != -1);
return thin_junction;
}
/**
* Searches for the tile with the best heuristic score within the search limits
* starting from the given tile x,y,z and going in the given direction test_edge.
* The best heuristic score is tracked and returned in the call parameters
* along with the corresponding tile location and search path telemetry
* (junctions passed through and directions taken).
*
* The primary heuristic used is distance from the goal; the secondary
* heuristic used (when the primary heuristic gives equal scores) is the number
* of steps. i.e. the search gets as close as possible to the goal in as few
* steps as possible.
*
* Each tile is checked to determine if the goal is reached.
* When the goal is not reached the search result is only updated at the END
* of each search path (some map element that is not a path or a path at which
* a search limit is reached), NOT at each step along the way.
* This means that the search ignores thin paths that are "no through paths"
* no matter how close to the goal they get, but will follow possible "through
* paths".
*
* The implementation is essentially an A* path finding algorithm over the
* path layout in xyz; however a best score is tracked via the score parameter
* rather than storing scores for each xyz, which means explicit loop detection
* is necessary to limit the search space.
*
* The parameters that hold the best search result so far are:
* - score - the least heuristic distance from the goal
* - endSteps - the least number of steps that achieve the score.
*
* The following parameters provide telemetry information on best search path so far:
* - endXYZ tracks the end location of the search path.
* - endSteps tracks the number of steps to the end of the search path.
* - endJunctions tracks the number of junctions passed through in the
* search path.
* - junctionList[] and directionList[] track the junctions and
* corresponding directions of the search path.
* Other than debugging purposes, these could potentially be used to visualise
* the pathfinding on the map.
*
* The parameters/variables that limit the search space are:
* - counter (param) - number of steps walked in the current search path;
* - _peepPathFindTilesChecked (variable) - cumulative number of tiles that can be
* checked in the entire search;
* - _peepPathFindNumJunctions (variable) - number of thin junctions that can be
* checked in a single search path;
*
* Other global variables/state that affect the search space are:
* - Wide paths - to handle broad paths (> 1 tile wide), the search navigates
* along non-wide (or 'thin' paths) and stops as soon as it encounters a
* wide path. This means peeps heading for a destination will only leave
* thin paths if walking 1 tile onto a wide path is closer than following
* non-wide paths;
* - gPeepPathFindIgnoreForeignQueues
* - gPeepPathFindQueueRideIndex - the ride the peep is heading for
* - _peepPathFindHistory - the search path telemetry consisting of the
* starting point and all thin junctions with directions navigated
* in the current search path - also used to detect path loops.
*
* The score is only updated when:
* - the goal is reached;
* - a wide tile is encountered with a better search result - the goal may
* still be reachable from here (only if the current tile is also wide);
* - a junction is encountered with a better search result and
* maxNumJunctions is exceeded - the goal may still be reachable from here;
* - returning from a recursive call if a search limit (i.e. either
* maxNumStep or maxTilesChecked) was reached and the current tile has a
* better search result and the goal may still be reachable from here
* (i.e. not a dead end path tile).
*
* rct2: 0x0069A997
*/
static void peep_pathfind_heuristic_search(sint16 x, sint16 y, uint8 z, rct_map_element *currentMapElement, uint8 counter, uint16 *endScore, int test_edge, uint8 *endJunctions, rct_xyz8 junctionList[16], uint8 directionList[16], rct_xyz8 *endXYZ, uint8 *endSteps) {
uint8 searchResult = PATH_SEARCH_FAILED;
x += TileDirectionDelta[test_edge].x;
y += TileDirectionDelta[test_edge].y;
++counter;
_peepPathFindTilesChecked--;
/* If this is where the search started this is a search loop and the
* current search path ends here.
* Return without updating the parameters (best result so far). */
if ((_peepPathFindHistory[0].location.x == (uint8)(x >> 5)) &&
(_peepPathFindHistory[0].location.y == (uint8)(y >> 5)) &&
(_peepPathFindHistory[0].location.z == (uint8)z)) {
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; At start", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* Get the next map element in the direction of test_edge, ignoring
* map elements that are not of interest, which includes wide paths
* and foreign ride queues (depending on gPeepPathFindIgnoreForeignQueues) */
bool found = false;
rct_map_element *mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if (mapElement->flags & MAP_ELEMENT_FLAG_GHOST) continue;
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_TRACK:
if (z != mapElement->base_height) continue;
/* More details about the mapElement are not needed.
* If in the future it would be useful to know
* whether the map element is a shop, the following
* commented out code will do it. */
//rideIndex = mapElement->properties.track.ride_index;
//rct_ride *ride = get_ride(rideIndex);
//if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) {
// searchResult = PATH_SEARCH_SHOP_ENTRANCE;
//} else {
searchResult = PATH_SEARCH_OTHER;
//}
found = true;
break;
case MAP_ELEMENT_TYPE_ENTRANCE:
if (z != mapElement->base_height) continue;
int direction;
searchResult = PATH_SEARCH_OTHER;
switch (mapElement->properties.entrance.type) {
case ENTRANCE_TYPE_RIDE_ENTRANCE:
direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
if (direction == test_edge) {
searchResult = PATH_SEARCH_RIDE_ENTRANCE;
}
break;
/* More details for other entrance types are not needed.
* If in the future it would be useful to know
* whether the map element is a ride exit or a park
* entrance/exit, the following commented out code
* will do it. */
//case ENTRANCE_TYPE_RIDE_EXIT:
// direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
// if (direction == test_edge) {
// searchResult = PATH_SEARCH_RIDE_EXIT;
// }
// break;
//case ENTRANCE_TYPE_PARK_ENTRANCE:
// searchResult = PATH_SEARCH_PARK_EXIT;
}
found = true;
break;
case MAP_ELEMENT_TYPE_PATH:
if (!is_valid_path_z_and_direction(mapElement, z, test_edge)) continue;
// Path may be sloped, so set z to path base height.
z = mapElement->base_height;
if (footpath_element_is_wide(mapElement)) {
searchResult = PATH_SEARCH_WIDE;
found = true;
break;
}
searchResult = PATH_SEARCH_THIN;
uint8 numEdges = bitcount(path_get_permitted_edges(mapElement));
if (numEdges < 2) {
searchResult = PATH_SEARCH_DEAD_END;
} else if (numEdges > 2) {
searchResult = PATH_SEARCH_JUNCTION;
} else { // numEdges == 2
if (footpath_element_is_queue(mapElement) && mapElement->properties.path.ride_index != gPeepPathFindQueueRideIndex) {
if (gPeepPathFindIgnoreForeignQueues && (mapElement->properties.path.ride_index != 0xFF)) {
// Path is a queue we aren't interested in
searchResult = PATH_SEARCH_RIDE_QUEUE;
}
}
}
found = true;
break;
}
if (found) {
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
/* No map element could be found.
* Return without updating the parameters (best result so far). */
if (!found) {
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; No map element found", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* At this point the map element is found. */
uint8 height = z;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
// Adjust height for goal comparison according to the path slope.
if (footpath_element_is_sloped(mapElement)) {
if (footpath_element_get_slope_direction(mapElement) == test_edge) {
height += 2;
}
}
}
// Calculate the heuristic score of this map element.
uint16 x_delta = abs(gPeepPathFindGoalPosition.x - x);
uint16 y_delta = abs(gPeepPathFindGoalPosition.y - y);
if (x_delta < y_delta) x_delta >>= 4;
else y_delta >>= 4;
uint16 new_score = x_delta + y_delta;
uint16 z_delta = abs(gPeepPathFindGoalPosition.z - height);
z_delta <<= 1;
new_score += z_delta;
/* If this map element is the search goal the current search path ends here. */
if (new_score == 0) {
/* If the search result is better than the best so far (in the paramaters),
* then update the parameters with this search. */
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
// Update the search results
*endScore = new_score;
*endSteps = counter;
// Update the end x,y,z
endXYZ->x = x >> 5;
endXYZ->y = y >> 5;
endXYZ->z = z;
// Update the telemetry
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; At goal; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
}
return;
}
/* At this point the map element tile is not the goal. */
/* If this map element is not a path, the search cannot be continued.
* Return without updating the parameters (best result so far). */
if (searchResult != PATH_SEARCH_DEAD_END &&
searchResult != PATH_SEARCH_THIN &&
searchResult != PATH_SEARCH_JUNCTION &&
searchResult != PATH_SEARCH_WIDE) {
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; Not a path", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* At this point the map element is a path. */
/* If this is a wide path the search ends here. */
if (searchResult == PATH_SEARCH_WIDE) {
/* Ignore Wide paths as continuing paths UNLESS the current path is also Wide.
* i.e. search across wide paths from a wide path to get onto a thin path,
* thereafter stay on thin paths. */
/* So, if the current path is also wide the goal could still
* be reachable from here.
* If the search result is better than the best so far (in the paramaters),
* then update the parameters with this search. */
if (footpath_element_is_wide(currentMapElement) &&
(new_score < *endScore || (new_score == *endScore && counter < *endSteps ))) {
// Update the search results
*endScore = new_score;
*endSteps = counter;
// Update the end x,y,z
endXYZ->x = x >> 5;
endXYZ->y = y >> 5;
endXYZ->z = z;
// Update the telemetry
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
}
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; Wide path; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* At this point the map element is a non-wide path.*/
/* Get all the permitted_edges of the map element. */
uint8 edges = path_get_permitted_edges(mapElement);
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Path %d,%d,%d; Edges (0123):%d%d%d%d; Reverse: %d", counter, x >> 5, y >> 5, z, edges & 1, (edges & 2) >> 1, (edges & 4) >> 2, (edges & 8) >> 3, test_edge ^ 2);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
/* Remove the reverse edge (i.e. the edge back to the previous map element.) */
edges &= ~(1 << (test_edge ^ 2));
test_edge = bitscanforward(edges);
/* If there are no other edges the current search ends here.
* Return without updating the parameters (best result so far). */
if (test_edge == -1) {
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; No more edges/dead end", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* Check if either of the search limits has been reached:
* - max number of steps or max tiles checked. */
if (counter >= 200 || _peepPathFindTilesChecked <= 0) {
/* The current search ends here.
* The path continues, so the goal could still be reachable from here.
* If the search result is better than the best so far (in the paramaters),
* then update the parameters with this search. */
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
// Update the search results
*endScore = new_score;
*endSteps = counter;
// Update the end x,y,z
endXYZ->x = x >> 5;
endXYZ->y = y >> 5;
endXYZ->z = z;
// Update the telemetry
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
}
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; Search limit reached", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
bool thin_junction = false;
if (searchResult == PATH_SEARCH_JUNCTION) {
/* Check if this is a thin junction. And perform additional
* necessary checks. */
thin_junction = path_is_thin_junction(mapElement, x, y, z);
if (thin_junction) {
/* The current search path is passing through a thin
* junction on this map element. Only 'thin' junctions
* are counted towards the junction search limit. */
/* Check the pathfind_history to see if this junction has been
* previously passed through in the current search path.
* i.e. this is a loop in the current search path.
* If so, the current search path ends here.
* Return without updating the parameters (best result so far). */
for (int junctionNum = _peepPathFindNumJunctions + 1; junctionNum <= _peepPathFindMaxJunctions; junctionNum++) {
if ((_peepPathFindHistory[junctionNum].location.x == (uint8)(x >> 5)) &&
(_peepPathFindHistory[junctionNum].location.y == (uint8)(y >> 5)) &&
(_peepPathFindHistory[junctionNum].location.z == (uint8)z)) {
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; Loop", counter, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
}
/* If the junction search limit is reached, the
* current search path ends here. The goal may still
* be reachable from here.
* If the search result is better than the best so far (in the paramaters),
* then update the parameters with this search. */
if (_peepPathFindNumJunctions <= 0) {
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
// Update the search results
*endScore = new_score;
*endSteps = counter;
// Update the end x,y,z
endXYZ->x = x >> 5;
endXYZ->y = y >> 5;
endXYZ->z = z;
// Update the telemetry
*endJunctions = _peepPathFindMaxJunctions; // - _peepPathFindNumJunctions;
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
}
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return from %d,%d,%d; NumJunctions < 0; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
return;
}
/* This junction was NOT previously visited in the current
* search path, so add the junction to the history. */
_peepPathFindHistory[_peepPathFindNumJunctions].location.x = (uint8)(x >> 5);
_peepPathFindHistory[_peepPathFindNumJunctions].location.y = (uint8)(y >> 5);
_peepPathFindHistory[_peepPathFindNumJunctions].location.z = (uint8)z;
// .direction take is added below.
_peepPathFindNumJunctions--;
}
}
/* Continue searching down each remaining edge of the path
* (recursive call). */
do {
edges &= ~(1 << test_edge);
uint8 savedNumJunctions = _peepPathFindNumJunctions;
uint8 height = z;
if (footpath_element_is_sloped(mapElement) &&
footpath_element_get_slope_direction(mapElement) == test_edge) {
height += 2;
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
if (searchResult == PATH_SEARCH_JUNCTION) {
if (thin_junction)
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Thin-Junction", counter, x >> 5, y >> 5, z, test_edge);
else
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Wide-Junction", counter, x >> 5, y >> 5, z, test_edge);
} else {
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Segment", counter, x >> 5, y >> 5, z, test_edge);
}
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (thin_junction) {
/* Add the current test_edge to the history. */
_peepPathFindHistory[_peepPathFindNumJunctions + 1].direction = test_edge;
}
peep_pathfind_heuristic_search(x, y, height, mapElement, counter, endScore, test_edge, endJunctions, junctionList, directionList, endXYZ, endSteps);
_peepPathFindNumJunctions = savedNumJunctions;
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
if (gPathFindDebug) {
log_info("[%03d] Return to %d,%d,%d edge: %d; Score: %d", counter, x >> 5, y >> 5, z, test_edge, *endScore);
}
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
} while ((test_edge = bitscanforward(edges)) != -1);
return;
}
/**
* Returns:
* -1 - no direction chosen
* 0..3 - chosen direction
*
* rct2: 0x0069A5F0
*/
int peep_pathfind_choose_direction(sint16 x, sint16 y, uint8 z, rct_peep *peep)
{
// The max number of thin junctions searched - a per-search-path limit.
_peepPathFindMaxJunctions = peep_pathfind_get_max_number_junctions(peep);
/* The max number of tiles to check - a whole-search limit.
* Mainly to limit the performance impact of the path finding. */
sint32 maxTilesChecked = (peep->type == PEEP_TYPE_STAFF) ? 50000 : 15000;
// Used to allow walking through no entry banners
_peepPathFindIsStaff = (peep->type == PEEP_TYPE_STAFF);
rct_xyz8 goal = {
.x = (uint8)(gPeepPathFindGoalPosition.x >> 5),
.y = (uint8)(gPeepPathFindGoalPosition.y >> 5),
.z = (uint8)(gPeepPathFindGoalPosition.z)
};
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Choose direction for %s for goal %d,%d,%d from %d,%d,%d", gPathFindDebugPeepName, goal.x, goal.y, goal.z, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
// Get the path element at this location
rct_map_element *dest_map_element = map_get_first_element_at(x / 32, y / 32);
bool found = false;
do {
if (dest_map_element->base_height != z) continue;
if (map_element_get_type(dest_map_element) != MAP_ELEMENT_TYPE_PATH) continue;
found = true;
break;
} while (!map_element_is_last_for_tile(dest_map_element++));
// Peep is not on a path.
if (!found) return -1;
/* Determine if the path is a thin junction.
* Only 'thin' junctions are remembered in peep->pathfind_history. */
bool isThin = path_is_thin_junction(dest_map_element, x, y, z);
uint8 edges = 0xF;
if (isThin && peep->pathfind_goal.x == goal.x &&
peep->pathfind_goal.y == goal.y &&
peep->pathfind_goal.z == goal.z
) {
/* Use of peep->pathfind_history[]:
* When walking to a goal, the peep pathfind_history stores
* the last 4 thin junctions that the peep walked through.
* For each of these 4 thin junctions the peep remembers
* those edges it has not yet taken.
* If a peep returns to one of the 4 thin junctions that it
* remembers, it will only choose from the directions that it
* did not try yet.
* This forces to the peep pathfinding to try the "next best"
* direction after trying the "best" direction(s) and finding
* that the goal could not be reached. */
/* If the peep remembers walking through this junction
* previously while heading for its goal, retrieve the
* directions it has not yet tried. */
for (int i = 0; i < 4; ++i) {
if (peep->pathfind_history[i].x == x / 32 &&
peep->pathfind_history[i].y == y / 32 &&
peep->pathfind_history[i].z == z) {
edges = peep->pathfind_history[i].direction & 0xF;
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Getting untried edges from pf_history for %d,%d,%d: %d", x >> 5, y >> 5, z, edges);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
break;
}
}
}
// Remove any edges that are not permitted
edges &= path_get_permitted_edges(dest_map_element);
// Peep has tried all edges.
if (edges == 0) return -1;
int chosen_edge = bitscanforward(edges);
// Peep has multiple edges still to try.
if (edges & ~(1 << chosen_edge)) {
uint16 best_score = 0xFFFF;
uint8 best_sub = 0xFF;
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
uint8 bestJunctions = 0;
rct_xyz8 bestJunctionList[16] = { 0 };
uint8 bestDirectionList[16] = { 0 };
rct_xyz8 bestXYZ = { 0, 0, 0 };
if (gPathFindDebug) {
log_verbose("Pathfind start for goal %d,%d,%d from %d,%d,%d", goal.x, goal.y, goal.z, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
/* Call the search heuristic on each edge, keeping track of the
* edge that gives the best (i.e. smallest) value (best_score)
* or for different edges with equal value, the edge with the
* least steps (best_sub). */
int numEdges = bitcount(edges);
for (int test_edge = chosen_edge; test_edge != -1; test_edge = bitscanforward(edges)) {
edges &= ~(1 << test_edge);
uint8 height = z;
if (footpath_element_is_sloped(dest_map_element) &&
footpath_element_get_slope_direction(dest_map_element) == test_edge
) {
height += 0x2;
}
_peepPathFindFewestNumSteps = 255;
/* Divide the maxTilesChecked global search limit
* between the remaining edges to ensure the search
* covers all of the remaining edges. */
_peepPathFindTilesChecked = maxTilesChecked / numEdges;
_peepPathFindNumJunctions = _peepPathFindMaxJunctions;
// Initialise _peepPathFindHistory.
memset(_peepPathFindHistory, 0xFF, sizeof(_peepPathFindHistory));
/* The pathfinding will only use elements
* 1.._peepPathFindMaxJunctions, so the starting point
* is placed in element 0 */
_peepPathFindHistory[0].location.x = (uint8)(x >> 5);
_peepPathFindHistory[0].location.y = (uint8)(y >> 5);
_peepPathFindHistory[0].location.z = (uint8)z;
_peepPathFindHistory[0].direction = 0xF;
uint16 score = 0xFFFF;
/* Variable endXYZ contains the end location of the
* search path. */
rct_xyz8 endXYZ;
endXYZ.x = 0;
endXYZ.y = 0;
endXYZ.z = 0;
uint8 endSteps = 255;
/* Variable bestJunctions is the number of junctions
* pass through in the search path.
* Variables bestJunctionList and bestDirectionList
* contain the junctions and corresponding directions
* of the search path.
* In the future these could be used to visualise the
* pathfinding on the map. */
uint8 endJunctions = 0;
rct_xyz8 endJunctionList[16] = { 0 };
uint8 endDirectionList[16] = { 0 };
peep_pathfind_heuristic_search(x, y, height, dest_map_element, 0, &score, test_edge, &endJunctions, endJunctionList, endDirectionList, &endXYZ, &endSteps);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Pathfind test edge: %d score: %d steps: %d end: %d,%d,%d junctions: %d", test_edge, score, endSteps, endXYZ.x, endXYZ.y, endXYZ.z, endJunctions);
for (uint8 listIdx = 0; listIdx < endJunctions; listIdx++) {
log_info("Junction#%d %d,%d,%d Direction %d", listIdx + 1, endJunctionList[listIdx].x, endJunctionList[listIdx].y, endJunctionList[listIdx].z, endDirectionList[listIdx]);
}
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (score < best_score || (score == best_score && endSteps < best_sub)) {
chosen_edge = test_edge;
best_score = score;
best_sub = endSteps;
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
bestJunctions = endJunctions;
for (uint8 index = 0; index < endJunctions; index++) {
bestJunctionList[index].x = endJunctionList[index].x;
bestJunctionList[index].y = endJunctionList[index].y;
bestJunctionList[index].z = endJunctionList[index].z;
bestDirectionList[index] = endDirectionList[index];
}
bestXYZ.x = endXYZ.x;
bestXYZ.y = endXYZ.y;
bestXYZ.z = endXYZ.z;
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
}
}
/* Check if the heuristic search failed. e.g. all connected
* paths are within the search limits and none reaches the
* goal. */
if (best_score == 0xFFFF) {
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Pathfind heuristic search failed.");
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
return -1;
}
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Pathfind best edge %d with score %d steps %d", chosen_edge, best_score, best_sub);
for (uint8 listIdx = 0; listIdx < bestJunctions; listIdx++) {
log_verbose("Junction#%d %d,%d,%d Direction %d", listIdx + 1, bestJunctionList[listIdx].x, bestJunctionList[listIdx].y, bestJunctionList[listIdx].z, bestDirectionList[listIdx]);
}
log_verbose("End at %d,%d,%d", bestXYZ.x, bestXYZ.y, bestXYZ.z);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
}
/* If this is a new goal for the peep. Store it and reset the peep's
* pathfind_history. */
if (peep->pathfind_goal.direction > 3 ||
peep->pathfind_goal.x != goal.x ||
peep->pathfind_goal.y != goal.y ||
peep->pathfind_goal.z != goal.z
) {
peep->pathfind_goal.x = goal.x;
peep->pathfind_goal.y = goal.y;
peep->pathfind_goal.z = goal.z;
peep->pathfind_goal.direction = 0;
// Clear pathfinding history
memset(peep->pathfind_history, 0xFF, sizeof(peep->pathfind_history));
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("New goal; clearing pf_history.");
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
}
if (isThin) {
for (int i = 0; i < 4; ++i) {
if (peep->pathfind_history[i].x == x >> 5 &&
peep->pathfind_history[i].y == y >> 5 &&
peep->pathfind_history[i].z == z
) {
/* Peep remembers this junction, so remove the
* chosen_edge from those left to try. */
peep->pathfind_history[i].direction &= ~(1 << chosen_edge);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Removing edge %d from existing pf_history for %d,%d,%d.", chosen_edge, x >> 5, y >> 5, z);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
return chosen_edge;
}
}
/* Peep does not remember this junction, so forget a junction
* and remember this junction. */
int i = peep->pathfind_goal.direction++;
peep->pathfind_goal.direction &= 3;
peep->pathfind_history[i].x = x >> 5;
peep->pathfind_history[i].y = y >> 5;
peep->pathfind_history[i].z = z;
peep->pathfind_history[i].direction = 0xF;
peep->pathfind_history[i].direction &= ~(1 << chosen_edge);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (gPathFindDebug) {
log_verbose("Storing new pf_history for %d,%d,%d without edge %d.", x >> 5, y >> 5, z, chosen_edge);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
}
return chosen_edge;
}
/**
*
* rct2: 0x006952C0
*/
static int guest_path_find_entering_park(rct_peep *peep, rct_map_element *map_element, uint8 edges){
uint8 chosenEntrance = 0xFF;
uint16 nearestDist = 0xFFFF;
for (uint8 entranceNum = 0; entranceNum < 4; ++entranceNum){
if (gParkEntranceX[entranceNum] == (sint16)0x8000)
continue;
uint16 dist = abs(gParkEntranceX[entranceNum] - peep->next_x) +
abs(gParkEntranceY[entranceNum] - peep->next_y);
if (dist >= nearestDist)
continue;
nearestDist = dist;
chosenEntrance = entranceNum;
}
if (chosenEntrance == 0xFF)
return guest_path_find_aimless(peep, edges);
sint16 x = gParkEntranceX[chosenEntrance];
sint16 y = gParkEntranceY[chosenEntrance];
sint16 z = gParkEntranceZ[chosenEntrance];
gPeepPathFindGoalPosition = (rct_xyz16){ x, y, z >> 3 };
gPeepPathFindIgnoreForeignQueues = true;
gPeepPathFindQueueRideIndex = 255;
int chosenDirection = peep_pathfind_choose_direction(peep->next_x, peep->next_y, peep->next_z, peep);
if (chosenDirection == -1)
return guest_path_find_aimless(peep, edges);
else
return peep_move_one_tile(chosenDirection, peep);
}
/**
*
* rct2: 0x0069536C
*/
static int guest_path_find_leaving_park(rct_peep *peep, rct_map_element *map_element, uint8 edges){
rct2_peep_spawn* peepSpawn = &gPeepSpawns[0];
// Peeps for whatever reason return to their original spawn point
// this in future should look for the nearest.
if (peep->sprite_index & 1 && gPeepSpawns[1].x != 0xFFFF){
peepSpawn++;
}
sint16 x = peepSpawn->x & 0xFFE0;
sint16 y = peepSpawn->y & 0xFFE0;
uint8 z = peepSpawn->z * 2;
uint8 direction = peepSpawn->direction;
gPeepPathFindGoalPosition = (rct_xyz16){ x, y, z };
if (x == peep->next_x && y == peep->next_y){
return peep_move_one_tile(direction, peep);
}
gPeepPathFindIgnoreForeignQueues = true;
gPeepPathFindQueueRideIndex = 255;
direction = peep_pathfind_choose_direction(peep->next_x, peep->next_y, peep->next_z, peep);
if (direction == 0xFF)
return guest_path_find_aimless(peep, edges);
else
return peep_move_one_tile(direction, peep);
}
/**
*
* rct2: 0x00695161
*/
static int guest_path_find_park_entrance(rct_peep* peep, rct_map_element *map_element, uint8 edges){
uint8 entranceNum;
// Resolves already-corrupt guests (e.g. loaded from save)
if (peep->peep_flags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN &&
(peep->current_ride >= 4 || gParkEntranceX[peep->current_ride] == (sint16)0x8000)
) {
peep->peep_flags &= ~(PEEP_FLAGS_PARK_ENTRANCE_CHOSEN);
}
if (!(peep->peep_flags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN)){
uint8 chosenEntrance = 0xFF;
uint16 nearestDist = 0xFFFF;
for (entranceNum = 0; entranceNum < 4; ++entranceNum){
if (gParkEntranceX[entranceNum] == (sint16)0x8000)
continue;
uint16 dist = abs(gParkEntranceX[entranceNum] - peep->next_x) +
abs(gParkEntranceY[entranceNum] - peep->next_y);
if (dist >= nearestDist)
continue;
nearestDist = dist;
chosenEntrance = entranceNum;
}
if (chosenEntrance == 0xFF)
return guest_path_find_aimless(peep, edges);
peep->current_ride = chosenEntrance;
peep->peep_flags |= PEEP_FLAGS_PARK_ENTRANCE_CHOSEN;
}
entranceNum = peep->current_ride;
sint16 x = gParkEntranceX[entranceNum];
sint16 y = gParkEntranceY[entranceNum];
sint16 z = gParkEntranceZ[entranceNum];
gPeepPathFindGoalPosition = (rct_xyz16) { x, y, z >> 3 };
gPeepPathFindIgnoreForeignQueues = true;
gPeepPathFindQueueRideIndex = 255;
int chosenDirection = peep_pathfind_choose_direction(peep->next_x, peep->next_y, peep->next_z, peep);
if (chosenDirection == -1)
return guest_path_find_aimless(peep, edges);
else
return peep_move_one_tile(chosenDirection, peep);
}
/**
*
* rct2: 0x006A72C5
* param dist is not used.
*/
static void get_ride_queue_end(sint16 *x, sint16 *y, sint16 *z){
rct_xy16 result = { 0, 0 };
rct_map_element *mapElement = map_get_first_element_at(*x / 32, *y / 32);
bool found = false;
do{
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE)
continue;
if (*z != mapElement->base_height)
continue;
found = true;
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (!found)
return;
uint8 direction = (mapElement->type & 3) ^ (1 << 1);
rct_map_element* lastPathElement = NULL;
rct_map_element* firstPathElement = NULL;
sint16 baseZ = mapElement->base_height;
sint16 nextX = *x;
sint16 nextY = *y;
while (1){
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH){
lastPathElement = mapElement;
result.x = nextX;
result.y = nextY;
// result.direction = direction;
if (footpath_element_is_sloped(mapElement)){
if (footpath_element_get_slope_direction(mapElement) == direction){
baseZ += 2;
}
}
}
nextX += TileDirectionDelta[direction].x;
nextY += TileDirectionDelta[direction].y;
mapElement = map_get_first_element_at(nextX / 32, nextY / 32);
found = false;
do{
if (mapElement == firstPathElement)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (baseZ == mapElement->base_height){
if (footpath_element_is_sloped(mapElement)){
if (footpath_element_get_slope_direction(mapElement) != direction){
break;
}
}
found = true;
break;
}
if (baseZ - 2 == mapElement->base_height){
if (!footpath_element_is_sloped(mapElement))
break;
if (footpath_element_get_slope_direction(mapElement) != (direction ^ 2))
break;
baseZ -= 2;
found = true;
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
if (found == false)
break;
if (!footpath_element_is_queue(mapElement))
break;
if (!(mapElement->properties.path.edges & (1 << (direction ^ (1 << 1)))))
break;
if (firstPathElement == NULL)
firstPathElement = mapElement;
// More queue to go.
if (mapElement->properties.path.edges & (1 << (direction)))
continue;
direction++;
direction &= 3;
// More queue to go.
if (mapElement->properties.path.edges & (1 << (direction)))
continue;
direction ^= (1 << 1);
// More queue to go.
if (mapElement->properties.path.edges & (1 << (direction)))
continue;
break;
}
if ((uint8)*z == 0xFF)
return;
mapElement = lastPathElement;
if (mapElement == NULL)
return;
if (!footpath_element_is_queue(mapElement))
return;
*x = result.x;
*y = result.y;
*z = mapElement->base_height;
}
/**
*
* rct2: 0x00694C35
*/
static int guest_path_finding(rct_peep* peep)
{
sint16 x, y, z;
if (peep->next_var_29 & 0x18) {
return guest_surface_path_finding(peep);
}
x = peep->next_x;
y = peep->next_y;
z = peep->next_z;
rct_map_element *mapElement = map_get_path_element_at(x / 32, y / 32, z);
if (mapElement == NULL) {
return 1;
}
_peepPathFindIsStaff = false;
uint8 edges = path_get_permitted_edges(mapElement);
if (edges == 0) {
return guest_surface_path_finding(peep);
}
if (peep->outside_of_park == 0 && peep_heading_for_ride_or_park_exit(peep)) {
/* If this mapElement is adjacent to any non-wide paths,
* remove all of the edges to wide paths. */
uint8 adjustedEdges = edges;
for (int chosenDirection = 0; chosenDirection < 4; chosenDirection++) {
// If there is no path in that direction try another
if (!(adjustedEdges & (1 << chosenDirection)))
continue;
/* If there is a wide path in that direction,
remove that edge and try another */
if (footpath_element_next_in_direction(peep->next_x, peep->next_y, peep->next_z, mapElement, chosenDirection) == PATH_SEARCH_WIDE) {
adjustedEdges &= ~(1 << chosenDirection);
}
}
if (adjustedEdges != 0)
edges = adjustedEdges;
}
sint8 direction = peep->direction ^ (1 << 1);
// Check if in a dead end (i.e. only edge is where the peep came from)
if (!(edges & ~(1 << direction))) {
// In a dead end. Check if peep is lost, etc.
peep_check_if_lost(peep);
peep_check_cant_find_ride(peep);
peep_check_cant_find_exit(peep);
} else {
/* Not a dead end. Remove edge peep came from so peep will
* continue on rather than going back where it came from */
edges &= ~(1 << direction);
}
direction = bitscanforward(edges);
// IF only one edge to choose from
if ((edges & ~(1 << direction)) == 0) {
return peep_move_one_tile(direction, peep);
}
// Peep still has multiple edges to choose from.
// Peep is outside the park.
// loc_694F19:
if (peep->outside_of_park != 0){
switch (peep->state) {
case PEEP_STATE_ENTERING_PARK:
return guest_path_find_entering_park(peep, mapElement, edges);
case PEEP_STATE_LEAVING_PARK:
return guest_path_find_leaving_park(peep, mapElement, edges);
default:
return guest_path_find_aimless(peep, edges);
}
}
/* Peep is inside the park.
* If the peep does not have food, randomly cull the useless directions
* (dead ends, ride exits, wide paths) from the edges.
* In principle, peeps with food are not paying as much attention to
* where they are going and are consequently more like to walk up
* dead end paths, paths to ride exits, etc. */
if (!peep_has_food(peep) && (scenario_rand() & 0xFFFF) >= 2184) {
uint8 adjustedEdges = edges;
for (int chosenDirection = 0; chosenDirection < 4; chosenDirection++) {
// If there is no path in that direction try another
if (!(adjustedEdges & (1 << chosenDirection)))
continue;
uint8 rideIndex, pathSearchResult;
pathSearchResult = footpath_element_destination_in_direction(peep->next_x, peep->next_y, peep->next_z, mapElement, chosenDirection, &rideIndex);
switch (pathSearchResult) {
case PATH_SEARCH_DEAD_END:
case PATH_SEARCH_RIDE_EXIT:
case PATH_SEARCH_WIDE:
adjustedEdges &= ~(1 << chosenDirection);
break;
}
}
if (adjustedEdges != 0)
edges = adjustedEdges;
}
/* If there are still multiple directions to choose from,
* peeps with maps will randomly read the map: probability of doing so
* is much higher when heading for a ride or the park exit. */
if (peep->item_standard_flags & PEEP_ITEM_MAP) {
// If at least 2 directions consult map
if (bitcount(edges) >= 2) {
uint16 probability = 1638;
if (peep_heading_for_ride_or_park_exit(peep)) {
probability = 9362;
}
if ((scenario_rand() & 0xFFFF) < probability) {
peep_read_map(peep);
}
}
}
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)
return guest_path_find_park_entrance(peep, mapElement, edges);
if (peep->guest_heading_to_ride_id == 0xFF)
return guest_path_find_aimless(peep, edges);
// Peep is heading for a ride.
uint8 rideIndex = peep->guest_heading_to_ride_id;
rct_ride* ride = get_ride(rideIndex);
if (ride->status != RIDE_STATUS_OPEN)
return guest_path_find_aimless(peep, edges);
// The ride is open.
gPeepPathFindQueueRideIndex = rideIndex;
/* Find the ride's closest entrance station to the peep.
* At the same time, count how many entrance stations there are and
* which stations are entrance stations. */
uint16 closestDist = 0xFFFF;
uint8 closestStationNum = 0;
int numEntranceStations = 0;
uint8 entranceStations = 0;
for (uint8 stationNum = 0; stationNum < 4; ++stationNum){
if (ride->entrances[stationNum] == 0xFFFF) // stationNum has no entrance (so presumably an exit only station).
continue;
numEntranceStations++;
entranceStations |= (1 << stationNum);
sint16 stationX = (ride->entrances[stationNum] & 0xFF) * 32;
sint16 stationY = (ride->entrances[stationNum] & 0xFF00) / 8;
uint16 dist = abs(stationX - peep->next_x) + abs(stationY - peep->next_y);
if (dist < closestDist){
closestDist = dist;
closestStationNum = stationNum;
continue;
}
}
// Ride has no stations with an entrance, so head to station 0.
if (numEntranceStations == 0)
closestStationNum = 0;
/* If a ride has multiple entrance stations and is set to sync with
* adjacent stations, cycle through the entrance stations (based on
* number of rides the peep has been on) so the peep will try the
* different sections of the ride.
* In this case, the ride's various entrance stations will typically,
* though not necessarily, be adjacent to one another and consequently
* not too far for the peep to walk when cycling between them.
* Note: the same choice of station must made while the peep navigates
* to the station. Consequently a random station selection here is not
* appropriate. */
if (numEntranceStations > 1 &&
(ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)) {
int select = peep->no_of_rides % numEntranceStations;
while (select > 0) {
closestStationNum = bitscanforward(entranceStations);
entranceStations &= ~(1 << closestStationNum);
select--;
}
closestStationNum = bitscanforward(entranceStations);
}
uint16 entranceXY;
if (numEntranceStations == 0)
entranceXY = ride->station_starts[closestStationNum]; // closestStationNum is always 0 here.
else
entranceXY = ride->entrances[closestStationNum];
x = (entranceXY & 0xFF) * 32;
y = (entranceXY & 0xFF00) / 8;
z = ride->station_heights[closestStationNum];
get_ride_queue_end(&x, &y, &z);
gPeepPathFindGoalPosition = (rct_xyz16) { x, y, z };
gPeepPathFindIgnoreForeignQueues = true;
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
/* Determine if the pathfinding debugging is wanted for this peep. */
/* For guests, use the existing PEEP_FLAGS_TRACKING flag to
* determine for which guest(s) the pathfinding debugging will
* be output for. */
format_string(gPathFindDebugPeepName, sizeof(gPathFindDebugPeepName), peep->name_string_idx, &(peep->id));
gPathFindDebug = peep->peep_flags & PEEP_FLAGS_TRACKING;
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
direction = peep_pathfind_choose_direction(peep->next_x, peep->next_y, peep->next_z, peep);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
gPathFindDebug = false;
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (direction == -1){
return guest_path_find_aimless(peep, edges);
}
return peep_move_one_tile(direction, peep);
}
/**
*
* rct2: 0x00693C9E
*/
static int sub_693C9E(rct_peep *peep)
{
_unk_F1EE18 = 0;
_unk_F1AEF1 = peep->action;
if (peep->action == PEEP_ACTION_NONE_1)
peep->action = PEEP_ACTION_NONE_2;
if (peep->state == PEEP_STATE_QUEUING){
if (peep_update_queue_position(peep))
return 1;
}
sint16 x, y, xy_dist;
if (!peep_update_action(&x, &y, &xy_dist, peep)){
_unk_F1EE18 |= F1EE18_DESTINATION_REACHED;
uint8 result = 0;
if (peep->type == PEEP_TYPE_GUEST){
result = guest_path_finding(peep);
}
else{
result = staff_path_finding(peep);
}
if (result != 0)
return 1;
if (!peep_update_action(&x, &y, &xy_dist, peep))
return 1;
}
if ((x & 0xFFE0) == peep->next_x && (y & 0xFFE0) == peep->next_y){
sint16 z = peep_get_height_on_slope(peep, x, y);
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
if (x < 32 || y < 32 || x >= gMapSizeUnits || y >= gMapSizeUnits){
if (peep->outside_of_park == 1){
_unk_F1EE18 |= F1EE18_OUTSIDE_PARK;
}
return peep_return_to_center_of_tile(peep);
}
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
sint16 base_z = max(0, (peep->z / 8) - 2);
sint16 top_z = (peep->z / 8) + 1;
do{
if (base_z > mapElement->base_height)
continue;
if (top_z < mapElement->base_height)
continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH){
if ((mapElement->flags & MAP_ELEMENT_FLAG_GHOST))
continue;
if (peep_interact_with_path(peep, x, y, mapElement))
return 1;
}
else if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK){
if (peep_interact_with_shop(peep, x, y, mapElement))
return 1;
}
else if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_ENTRANCE){
if (peep_interact_with_entrance(peep, x, y, mapElement))
return 1;
}
} while (!map_element_is_last_for_tile(mapElement++));
if (peep->type == PEEP_TYPE_STAFF || (peep->next_var_29 & 0x18)){
sint16 z = abs(map_element_height(x, y) - peep->z);
if (z <= 3 || (peep->type == PEEP_TYPE_STAFF && z <= 32)){
peep->var_79 = 0xFF;
if (peep->state == PEEP_STATE_QUEUING){
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_1;
peep_window_state_update(peep);
}
if (!map_is_location_in_park(x & 0xFFE0, y & 0xFFE0)){
return peep_return_to_center_of_tile(peep);
}
mapElement = map_get_surface_element_at(x / 32, y / 32);
if (mapElement == NULL)
return peep_return_to_center_of_tile(peep);
sint16 water_height = mapElement->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK;
if (water_height)
return peep_return_to_center_of_tile(peep);
peep->next_x = x & 0xFFE0;
peep->next_y = y & 0xFFE0;
peep->next_z = mapElement->base_height;
peep->next_var_29 = 8;
sint16 z = peep_get_height_on_slope(peep, x, y);
invalidate_sprite_2((rct_sprite*)peep);
sprite_move(x, y, z, (rct_sprite*)peep);
invalidate_sprite_2((rct_sprite*)peep);
return 1;
}
}
return peep_return_to_center_of_tile(peep);
}
/**
*
* rct2: 0x0069926C
* Expend type was previously an offset saved in 0x00F1AEC0
*/
static void peep_spend_money(rct_peep *peep, money16 *peep_expend_type, money32 amount)
{
assert(!(gParkFlags & PARK_FLAGS_NO_MONEY));
peep->cash_in_pocket = max(0, peep->cash_in_pocket - amount);
peep->cash_spent += amount;
if (peep_expend_type != NULL) {
*peep_expend_type += (money16)amount;
}
window_invalidate_by_number(WC_PEEP, peep->sprite_index);
gUnk141F568 = gUnk13CA740;
finance_payment(-amount, gCommandExpenditureType);
audio_play_sound_at_location(SOUND_PURCHASE, peep->x, peep->y, peep->z);
}
static void peep_set_has_ridden(rct_peep *peep, int rideIndex)
{
peep->rides_been_on[rideIndex / 8] |= 1 << (rideIndex % 8);
rct_ride *ride = get_ride(rideIndex);
peep_set_has_ridden_ride_type(peep, ride->type);
}
static bool peep_has_ridden(rct_peep *peep, int rideIndex)
{
return peep->rides_been_on[rideIndex / 8] & (1 << (rideIndex % 8));
}
static void peep_set_has_ridden_ride_type(rct_peep *peep, int rideType)
{
peep->ride_types_been_on[rideType / 8] |= 1 << (rideType % 8);
}
static bool peep_has_ridden_ride_type(rct_peep *peep, int rideType)
{
return peep->ride_types_been_on[rideType / 8] & (1 << (rideType % 8));
}
/**
* Updates various peep stats upon entering a ride, as well as updating the
* ride's satisfaction value.
* rct2: 0x0069545B
*/
static void peep_on_enter_ride(rct_peep *peep, int rideIndex)
{
rct_ride *ride = get_ride(rideIndex);
// Calculate how satisfying the ride is for the peep. Can range from -140 to +140.
sint16 satisfaction = peep_calculate_ride_satisfaction(peep, ride);
// Update the satisfaction stat of the ride.
uint8 rideSatisfaction = 0;
if (satisfaction >= 40)
rideSatisfaction = 3;
else if (satisfaction >= 20)
rideSatisfaction = 2;
else if (satisfaction >= 0)
rideSatisfaction = 1;
ride_update_satisfaction(ride, rideSatisfaction);
// Update various peep stats.
if (peep->no_of_rides < 255)
peep->no_of_rides++;
peep_set_has_ridden(peep, peep->current_ride);
peep_update_favourite_ride(peep, ride);
peep->happiness_growth_rate = clamp(0, peep->happiness_growth_rate + satisfaction, 255);
peep_update_ride_nausea_growth(peep, ride);
}
/**
* Check to see if the specified ride should become the peep's favourite.
* For this, a "ride rating" is calculated based on the excitement of the ride and the peep's current happiness.
* As this value cannot exceed 255, the happier the peep is, the more irrelevant the ride's excitement becomes.
* Due to the minimum happiness requirement, an excitement rating of more than 3.8 has no further effect.
*
* If the ride rating is higher than any ride the peep has already been on and the happiness criteria is met,
* the ride becomes the peep's favourite. (This doesn't happen right away, but will be updated once the peep
* exits the ride.)
*/
static void peep_update_favourite_ride(rct_peep *peep, rct_ride *ride)
{
peep->peep_flags &= ~PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE;
uint8 peepRideRating = clamp(0, (ride->excitement / 4) + peep->happiness, 255);
if (peepRideRating >= peep->favourite_ride_rating) {
if (peep->happiness >= 160 && peep->happiness_growth_rate >= 160) {
peep->favourite_ride_rating = peepRideRating;
peep->peep_flags |= PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE;
}
}
}
/* rct2: 0x00695555 */
static sint16 peep_calculate_ride_value_satisfaction(rct_peep* peep, rct_ride* ride) {
if (gParkFlags & PARK_FLAGS_NO_MONEY){
return -30;
}
if (ride->value == 0xFFFF) {
return -30;
}
money16 ridePrice = ride_get_price(ride);
if (ride->value >= ridePrice) {
return -5;
}
if ((ride->value + ((ride->value * peep->happiness) / 256)) >= ridePrice) {
return -30;
}
return 0;
}
static sint16 peep_calculate_ride_intensity_nausea_satisfaction(rct_peep* peep, rct_ride* ride) {
ride_rating minIntensity, maxIntensity;
ride_rating minNausea, maxNausea;
// Calculate satisfaction based on the intensity and nausea of the ride.
// The best possible score from this section is achieved by having the intensity and nausea
// of the ride fall exactly within the peep's preferences, but lower scores can still be achieved
// if the peep's happiness is enough to offset it.
uint8 intensitySatisfaction = 0;
uint8 nauseaSatisfaction = 0;
if (ride->excitement == (ride_rating)0xFFFF) {
return 70;
}
intensitySatisfaction = 3;
nauseaSatisfaction = 3;
maxIntensity = (peep->intensity >> 4) * 100;
minIntensity = (peep->intensity & 0xF) * 100;
if (minIntensity <= ride->intensity && maxIntensity >= ride->intensity) {
intensitySatisfaction--;
}
minIntensity -= peep->happiness * 2;
maxIntensity += peep->happiness;
if (minIntensity <= ride->intensity && maxIntensity >= ride->intensity) {
intensitySatisfaction--;
}
minIntensity -= peep->happiness * 2;
maxIntensity += peep->happiness;
if (minIntensity <= ride->intensity && maxIntensity >= ride->intensity) {
intensitySatisfaction--;
}
// Although it's not shown in the interface, a peep with Average or High nausea tolerance
// has a minimum preferred nausea value. (For peeps with None or Low, this is set to zero.)
minNausea = NauseaMinimumThresholds[(peep->nausea_tolerance & 3)];
maxNausea = NauseaMaximumThresholds[(peep->nausea_tolerance & 3)];
if (minNausea <= ride->nausea && maxNausea >= ride->nausea) {
nauseaSatisfaction--;
}
minNausea -= peep->happiness * 2;
maxNausea += peep->happiness;
if (minNausea <= ride->nausea && maxNausea >= ride->nausea) {
nauseaSatisfaction--;
}
minNausea -= peep->happiness * 2;
maxNausea += peep->happiness;
if (minNausea <= ride->nausea && maxNausea >= ride->nausea) {
nauseaSatisfaction--;
}
uint8 highestSatisfaction = max(intensitySatisfaction, nauseaSatisfaction);
uint8 lowestSatisfaction = min(intensitySatisfaction, nauseaSatisfaction);
switch (highestSatisfaction) {
case 0:
return 70;
case 1:
switch (lowestSatisfaction)
{
case 0:
return 50;
case 1:
return 35;
}
case 2:
switch (lowestSatisfaction)
{
case 0:
return 35;
case 1:
return 20;
case 2:
return 10;
}
case 3:
switch (lowestSatisfaction)
{
case 0:
return -35;
case 1:
return -50;
case 2:
return -60;
case 3:
return -60;
}
}
// Should never happen
return 70;
}
/**
* The satisfaction values calculated here are used to determine how happy the peep is with the ride,
* and also affects the satisfaction stat of the ride itself. The factors that affect satisfaction include:
* - The price of the ride compared to the ride's value
* - How closely the intensity and nausea of the ride matches the peep's preferences
* - How long the peep was waiting in the queue
* - If the peep has been on the ride before, or on another ride of the same type
*/
static sint16 peep_calculate_ride_satisfaction(rct_peep *peep, rct_ride *ride)
{
sint16 satisfaction = peep_calculate_ride_value_satisfaction(peep, ride);
satisfaction += peep_calculate_ride_intensity_nausea_satisfaction(peep, ride);
// Calculate satisfaction based on how long the peep has been in the queue for.
// (For comparison: peeps start thinking "I've been queueing for a long time" at 3500 and
// start leaving the queue at 4300.)
if (peep->time_in_queue >= 4500)
satisfaction -= 35;
else if (peep->time_in_queue >= 2250)
satisfaction -= 10;
else if (peep->time_in_queue <= 750)
satisfaction += 10;
// Peeps get a small boost in satisfaction if they've been on a ride of the same type before,
// and this boost is doubled if they've already been on this particular ride.
if (peep_has_ridden_ride_type(peep, ride->type))
satisfaction += 10;
if (peep_has_ridden(peep, peep->current_ride))
satisfaction += 10;
return satisfaction;
}
/**
* Update the nausea growth of the peep based on a ride. This is calculated based on:
* - The nausea rating of the ride
* - Their new happiness growth rate (the higher, the less nauseous)
* - How hungry the peep is (+0% nausea at 50% hunger up to +100% nausea at 100% hunger)
* - The peep's nausea tolerance (Final modifier: none: 100%, low: 50%, average: 25%, high: 12.5%)
*/
static void peep_update_ride_nausea_growth(rct_peep *peep, rct_ride *ride)
{
uint32 nauseaMultiplier = clamp(64, 256 - peep->happiness_growth_rate, 200);
uint32 nauseaGrowthRateChange = (ride->nausea * nauseaMultiplier) / 512;
nauseaGrowthRateChange *= max(128, peep->hunger) / 64;
nauseaGrowthRateChange >>= (peep->nausea_tolerance & 3);
peep->nausea_growth_rate = (uint8)clamp(0, peep->nausea_growth_rate + nauseaGrowthRateChange, 255);
}
static bool peep_should_go_on_ride_again(rct_peep *peep, rct_ride *ride)
{
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_20)) return false;
if (ride->excitement == (ride_rating)0xFFFF) return false;
if (ride->intensity > RIDE_RATING(10,00) && !gCheatsIgnoreRideIntensity) return false;
if (peep->happiness < 180) return false;
if (peep->energy < 100) return false;
if (peep->nausea > 160) return false;
if (peep->hunger < 30) return false;
if (peep->thirst < 20) return false;
if (peep->balloon_colour > 170) return false;
uint8 r = (scenario_rand() & 0xFF);
if (r <= 128) {
if (peep->no_of_rides > 7) return false;
if (r > 64) return false;
}
return true;
}
static bool peep_should_preferred_intensity_increase(rct_peep *peep)
{
if (gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES) return false;
if (peep->happiness < 200) return false;
return (scenario_rand() & 0xFF) >= peep->intensity;
}
static bool peep_really_liked_ride(rct_peep *peep, rct_ride *ride)
{
return
peep->happiness >= 215 &&
((peep->nausea <= 120 &&
ride->excitement != (ride_rating)0xFFFF &&
ride->intensity <= RIDE_RATING(10,00))||gCheatsIgnoreRideIntensity);
}
/**
*
* rct2: 0x0069576E
*/
static void peep_on_exit_ride(rct_peep *peep, int rideIndex)
{
rct_ride *ride = get_ride(rideIndex);
if (peep->peep_flags & PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE) {
peep->peep_flags &= ~PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE;
peep->favourite_ride = rideIndex;
// TODO fix this flag name or add another one
peep->window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS;
}
peep->happiness = peep->happiness_growth_rate;
peep->nausea = peep->nausea_growth_rate;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_STATS;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)
peep->peep_flags &= ~(PEEP_FLAGS_PARK_ENTRANCE_CHOSEN);
if (peep_should_go_on_ride_again(peep, ride)) {
peep->guest_heading_to_ride_id = rideIndex;
peep->peep_is_lost_countdown = 200;
peep_reset_pathfind_goal(peep);
rct_window *w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) {
window_event_invalidate_call(w);
widget_invalidate(w, 12);
}
}
if (peep_should_preferred_intensity_increase(peep)) {
if (peep->intensity <= 255 - 16) {
peep->intensity += 16;
}
}
if (peep_really_liked_ride(peep, ride)) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_WAS_GREAT, rideIndex);
int laugh = scenario_rand() & 7;
if (laugh < 3) {
audio_play_sound_at_location(SOUND_LAUGH_1 + laugh, peep->x, peep->y, peep->z);
}
}
ride->total_customers++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER;
}
/**
*
* rct2: 0x00695444
*/
static void peep_on_enter_or_exit_ride(rct_peep *peep, int rideIndex, int flags)
{
if (flags & 1) {
peep_on_exit_ride(peep, rideIndex);
} else {
peep_on_enter_ride(peep, rideIndex);
}
}
/** Main logic to decide whether a peep should buy an item in question
*
* Also handles the purchase as well, so once it returns, the peep will have the
* item and the money will have been deducted.
*
* eax: shopItem | (rideIndex << 8)
* ecx: price
* esi: *peep
*
* Returns 0 or 1 depending on if the peep decided to buy the item
*
* rct2: 0x0069AF1E
*/
static bool sub_69AF1E(rct_peep *peep, int rideIndex, int shopItem, money32 price)
{
rct_ride* ride = get_ride(rideIndex);
money32 value;
bool has_voucher = false;
if ((peep->item_standard_flags & PEEP_ITEM_VOUCHER) &&
(peep->voucher_type == VOUCHER_TYPE_FOOD_OR_DRINK_FREE) &&
(peep->voucher_arguments == shopItem)) {
has_voucher = true;
}
if (peep_has_item(peep, shopItem)) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_ALREADY_GOT, shopItem);
return 0;
}
if (shop_item_is_food_or_drink(shopItem)) {
int food = -1;
if ((food = peep_has_food_standard_flag(peep))) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food));
return 0;
} else if ((food = peep_has_food_extra_flag(peep))) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food) + 32);
return 0;
} else if (peep->nausea >= 145)
return 0;
}
if ((shopItem == SHOP_ITEM_BALLOON) || (shopItem == SHOP_ITEM_ICE_CREAM)
|| (shopItem == SHOP_ITEM_COTTON_CANDY) || (shopItem == SHOP_ITEM_SUNGLASSES)) {
if (gClimateCurrentRainLevel != 0)
return 0;
}
if ((shopItem == SHOP_ITEM_SUNGLASSES) || (shopItem == SHOP_ITEM_ICE_CREAM)) {
if (gClimateCurrentTemperature < 12)
return 0;
}
if (shop_item_is_food(shopItem) && (peep->hunger > 75)) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NOT_HUNGRY, 0xFF);
return 0;
}
if (shop_item_is_drink(shopItem) && (peep->thirst > 75)) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NOT_THIRSTY, 0xFF);
return 0;
}
if ((shopItem == SHOP_ITEM_UMBRELLA) && (gClimateCurrentRainLevel != 0))
goto loc_69B119;
if ((shopItem != SHOP_ITEM_MAP) && shop_item_is_souvenir(shopItem) && !has_voucher) {
if (((scenario_rand() & 0x7F) + 0x73) > peep->happiness)
return 0;
else if (peep->no_of_rides < 3)
return 0;
}
loc_69B119:
if (!has_voucher) {
if (price != 0) {
if (peep->cash_in_pocket == 0) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, 0xFF);
return 0;
}
if (price > peep->cash_in_pocket) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD, shopItem);
return 0;
}
}
if (gClimateCurrentTemperature >= 21)
value = get_shop_hot_value(shopItem);
else if (gClimateCurrentTemperature <= 11)
value = get_shop_cold_value(shopItem);
else
value = get_shop_base_value(shopItem);
if (value < price) {
value -= price;
if (shopItem == SHOP_ITEM_UMBRELLA) {
if (gClimateCurrentRainLevel != 0)
goto loc_69B221;
}
value = -value;
if (peep->happiness >= 128)
value /= 2;
if (peep->happiness >= 180)
value /= 2;
if (value > ((money16)(scenario_rand() & 0x07))) {
// "I'm not paying that much for x"
uint8 thought_type = (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2_MUCH + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON_MUCH + shopItem));
peep_insert_new_thought(peep, thought_type, rideIndex);
return 0;
}
}
else {
value -= price;
value = max(8, value);
if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) {
if (value >= (money32)(scenario_rand() & 0x07)) {
// "This x is a really good value"
uint8 thought_item = (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2 + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON + shopItem));
peep_insert_new_thought(peep, thought_item, rideIndex);
}
}
int happinessGrowth = value * 4;
peep->happiness_growth_rate = min((peep->happiness_growth_rate + happinessGrowth), 255);
peep->happiness = min((peep->happiness + happinessGrowth), 255);
}
}
loc_69B221:
if (!has_voucher) {
if (gClimateCurrentTemperature >= 21)
value = get_shop_hot_value(shopItem);
else if (gClimateCurrentTemperature <= 11)
value = get_shop_cold_value(shopItem);
else
value = get_shop_base_value(shopItem);
value -= price;
uint8 satisfaction = 0;
if (value > -8) {
satisfaction++;
if (value > -3) {
satisfaction++;
if (value > 3)
satisfaction++;
}
}
ride_update_satisfaction(ride, satisfaction);
}
// The peep has now decided to buy the item (or, specifically, has not been
// dissuaded so far).
if (shopItem >= 32)
peep->item_extra_flags |= (1u << (shopItem - 32));
else
peep->item_standard_flags |= (1u << shopItem);
if (shopItem == SHOP_ITEM_TSHIRT)
peep->tshirt_colour = ride->track_colour_main[0];
if (shopItem == SHOP_ITEM_HAT)
peep->hat_colour = ride->track_colour_main[0];
if (shopItem == SHOP_ITEM_BALLOON)
peep->balloon_colour = ride->track_colour_main[0];
if (shopItem == SHOP_ITEM_UMBRELLA)
peep->umbrella_colour = ride->track_colour_main[0];
if (shopItem == SHOP_ITEM_MAP)
peep_reset_pathfind_goal(peep);
uint16 dl = byte_9822F4[shopItem];
peep->var_42 = min((peep->var_42 + dl), 255);
if (shopItem == SHOP_ITEM_PHOTO)
peep->photo1_ride_ref = rideIndex;
if (shopItem == SHOP_ITEM_PHOTO2)
peep->photo2_ride_ref = rideIndex;
if (shopItem == SHOP_ITEM_PHOTO3)
peep->photo3_ride_ref = rideIndex;
if (shopItem == SHOP_ITEM_PHOTO4)
peep->photo4_ride_ref = rideIndex;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
peep_update_sprite_type(peep);
if (peep->peep_flags & PEEP_FLAGS_TRACKING) {
set_format_arg(0, rct_string_id, peep->name_string_idx);
set_format_arg(2, uint32, peep->id);
set_format_arg(6, rct_string_id, ShopItemStringIds[shopItem].indefinite);
if (gConfigNotifications.guest_bought_item) {
news_item_add_to_queue(2, STR_PEEP_TRACKING_NOTIFICATION_BOUGHT_X, peep->sprite_index);
}
}
if (shop_item_is_food(shopItem))
peep->no_of_food++;
if (shop_item_is_drink(shopItem))
peep->no_of_drinks++;
if (shop_item_is_souvenir(shopItem))
peep->no_of_souvenirs++;
money16* expend_type = &peep->paid_on_souvenirs;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_SHOP_STOCK;
if (shop_item_is_food(shopItem)) {
expend_type = &peep->paid_on_food;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_FOODDRINK_STOCK;
}
if (shop_item_is_drink(shopItem)) {
expend_type = &peep->paid_on_drink;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_FOODDRINK_STOCK;
}
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
finance_payment(get_shop_item_cost(shopItem), gCommandExpenditureType);
// Sets the expenditure type to *_FOODDRINK_SALES or *_SHOP_SALES appropriately.
gCommandExpenditureType--;
if (has_voucher) {
peep->item_standard_flags &= ~PEEP_ITEM_VOUCHER;
peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
else if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) {
peep_spend_money(peep, expend_type, price);
}
ride->total_profit += (price - get_shop_item_cost(shopItem));
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME;
ride->cur_num_customers++;
ride->total_customers++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER;
return 1;
}
/**
*
* rct2: 0x0069AEB7
*/
static bool peep_should_use_cash_machine(rct_peep *peep, int rideIndex)
{
if (gParkFlags & PARK_FLAGS_NO_MONEY) return false;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) return false;
if (peep->cash_in_pocket > MONEY(20,00)) return false;
if (115 + (scenario_rand() % 128) > peep->happiness) return false;
if (peep->energy < 80) return false;
rct_ride *ride = get_ride(rideIndex);
ride_update_satisfaction(ride, peep->happiness >> 6);
ride->cur_num_customers++;
ride->total_customers++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER;
return true;
}
/**
*
* rct2: 0x0069A98C
*/
static void peep_reset_pathfind_goal(rct_peep *peep)
{
peep->pathfind_goal.x = 0xFF;
peep->pathfind_goal.y = 0xFF;
peep->pathfind_goal.z = 0xFF;
peep->pathfind_goal.direction = 0xFF;
}
static bool peep_has_valid_xy(rct_peep *peep)
{
if (peep->x != (sint16)0x8000) {
if (peep->x < (256 * 32) && peep->y < (256 * 32)) {
return true;
}
}
return false;
}
static void peep_give_passing_peeps_purple_clothes(rct_peep *peep)
{
if (peep_has_valid_xy(peep)) {
rct_peep *otherPeep;
uint16 spriteIndex = sprite_get_first_in_quadrant(peep->x, peep->y);
for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) {
otherPeep = GET_PEEP(spriteIndex);
if (otherPeep->type == PEEP_TYPE_GUEST) {
int zDiff = abs(otherPeep->z - peep->z);
if (zDiff <= 32) {
otherPeep->tshirt_colour = COLOUR_BRIGHT_PURPLE;
otherPeep->trousers_colour = COLOUR_BRIGHT_PURPLE;
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
}
}
static void peep_give_passing_peeps_pizza(rct_peep *peep)
{
if (peep_has_valid_xy(peep)) {
rct_peep *otherPeep;
uint16 spriteIndex = sprite_get_first_in_quadrant(peep->x, peep->y);
for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) {
otherPeep = GET_PEEP(spriteIndex);
if (otherPeep->type == PEEP_TYPE_GUEST) {
int zDiff = abs(otherPeep->z - peep->z);
if (zDiff <= 32) {
if (!(otherPeep->item_standard_flags & PEEP_ITEM_PIZZA)) {
otherPeep->item_standard_flags |= PEEP_ITEM_PIZZA;
int peepDirection = (peep->sprite_direction >> 3) ^ 2;
int otherPeepOppositeDirection = otherPeep->sprite_direction >> 3;
if (peepDirection == otherPeepOppositeDirection) {
if (otherPeep->action == PEEP_ACTION_NONE_1 || otherPeep->action == PEEP_ACTION_NONE_2) {
invalidate_sprite_2((rct_sprite*)peep);
otherPeep->action = PEEP_ACTION_WAVE_2;
otherPeep->action_frame = 0;
otherPeep->action_sprite_image_offset = 0;
sub_693B58(otherPeep);
invalidate_sprite_2((rct_sprite*)otherPeep);
}
}
invalidate_sprite_2((rct_sprite*)otherPeep);
}
}
}
}
}
}
static void peep_make_passing_peeps_sick(rct_peep *peep)
{
if (peep_has_valid_xy(peep)) {
rct_peep *otherPeep;
uint16 spriteIndex = sprite_get_first_in_quadrant(peep->x, peep->y);
for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) {
otherPeep = GET_PEEP(spriteIndex);
if (otherPeep->type == PEEP_TYPE_GUEST) {
int zDiff = abs(otherPeep->z - peep->z);
if (zDiff <= 32) {
if (peep != otherPeep) {
if (otherPeep->action == PEEP_ACTION_NONE_1 || otherPeep->action == PEEP_ACTION_NONE_2) {
otherPeep->action = PEEP_ACTION_THROW_UP;
otherPeep->action_frame = 0;
otherPeep->action_sprite_image_offset = 0;
sub_693B58(otherPeep);
invalidate_sprite_2((rct_sprite*)otherPeep);
}
}
}
}
}
}
}
static void peep_give_passing_peeps_ice_cream(rct_peep *peep)
{
if (peep_has_valid_xy(peep)) {
rct_peep *otherPeep;
uint16 spriteIndex = sprite_get_first_in_quadrant(peep->x, peep->y);
for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) {
otherPeep = GET_PEEP(spriteIndex);
if (otherPeep->type == PEEP_TYPE_GUEST) {
int zDiff = abs(otherPeep->z - peep->z);
if (zDiff <= 32) {
if (peep != otherPeep) {
if (!(otherPeep->item_standard_flags & PEEP_ITEM_ICE_CREAM)) {
otherPeep->item_standard_flags |= PEEP_ITEM_ICE_CREAM;
peep_update_sprite_type(otherPeep);
}
}
}
}
}
}
}
/**
*
* rct2: 0x0068FD3A
*/
static void peep_easter_egg_peep_interactions(rct_peep *peep)
{
if (peep->peep_flags & PEEP_FLAGS_PURPLE) {
peep_give_passing_peeps_purple_clothes(peep);
}
if (peep->peep_flags & PEEP_FLAGS_PIZZA) {
peep_give_passing_peeps_pizza(peep);
}
if (peep->peep_flags & PEEP_FLAGS_CONTAGIOUS) {
peep_make_passing_peeps_sick(peep);
}
if (peep->peep_flags & PEEP_FLAGS_JOY) {
if (scenario_rand() <= 1456) {
if (peep->action == PEEP_ACTION_NONE_1 || peep->action == PEEP_ACTION_NONE_2) {
peep->action = PEEP_ACTION_JOY;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
}
if (peep->peep_flags & PEEP_FLAGS_ICE_CREAM) {
peep_give_passing_peeps_ice_cream(peep);
}
}
/**
* rct2: 0x0069101A
*
* @return (CF)
*/
static bool sub_69101A(rct_map_element *esi) {
rct_ride *ride = get_ride(esi->properties.track.ride_index);
if (gRideClassifications[ride->type] != RIDE_CLASS_RIDE) {
return true;
}
if ((uint16) ride->excitement == 0xFFFF) {
return false;
}
if (ride->excitement >= RIDE_RATING(4, 70)) {
return false;
}
if (ride->intensity >= RIDE_RATING(4, 50)) {
return false;
}
if (RideData4[ride->type].flags & 0x2000) {
if ((scenario_rand() & 0xFFFF) > 0x3333) {
return true;
}
} else if (RideData4[ride->type].flags & 0x4000) {
if ((scenario_rand() & 0xFFFF) > 0x1000) {
return true;
}
} else {
return true;
}
return false;
}
/**
*
* rct2: 0x00690B99
*
* @param edge (eax)
* @param peep (esi)
* @param[out] rideToView (cl)
* @param[out] rideSeatToView (ch)
* @return !CF
*/
static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToView, uint8 *rideSeatToView)
{
rct_map_element *mapElement, *surfaceElement;
surfaceElement = map_get_surface_element_at(peep->next_x / 32, peep->next_y / 32);
mapElement = surfaceElement;
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) continue;
if (map_element_get_direction(mapElement) != edge) continue;
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue;
if (peep->next_z + 4 <= mapElement->base_height) continue;
if (peep->next_z + 1 >= mapElement->clearance_height) continue;
return false;
} while (!map_element_is_last_for_tile(mapElement++));
uint16 x = peep->next_x + TileDirectionDelta[edge].x;
uint16 y = peep->next_y + TileDirectionDelta[edge].y;
if (x > 255 * 32 || y > 255 * 32) {
return false;
}
surfaceElement = map_get_surface_element_at(x / 32, y / 32);
mapElement = surfaceElement;
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) continue;
if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue;
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue;
// TODO: Check whether this shouldn't be <=, as the other loops use. If so, also extract as loop A.
if (peep->next_z + 4 >= mapElement->base_height) continue;
if (peep->next_z + 1 >= mapElement->clearance_height) continue;
return false;
} while (!map_element_is_last_for_tile(mapElement++));
// TODO: Extract loop B
mapElement = surfaceElement;
do {
if (mapElement->clearance_height + 1 < peep->next_z) continue;
if (peep->next_z + 6 < mapElement->base_height) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) {
if (!sub_69101A(mapElement)) {
return loc_690FD0(peep, rideToView, rideSeatToView, mapElement);
}
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) {
if (!(get_large_scenery_entry(mapElement->properties.scenerymultiple.type & 0x3FF)->large_scenery.flags & 0x10)) {
continue;
}
*rideSeatToView = 0;
if (mapElement->clearance_height >= peep->next_z + 8) {
*rideSeatToView = 0x02;
}
*rideToView = 0xFF;
return true;
}
} while (!map_element_is_last_for_tile(mapElement++));
// TODO: Extract loop C
mapElement = surfaceElement;
do {
if (mapElement->clearance_height + 1 < peep->next_z) continue;
if (peep->next_z + 6 < mapElement->base_height) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SURFACE) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_FENCE) {
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) {
continue;
}
}
return false;
} while (!map_element_is_last_for_tile(mapElement++));
x += TileDirectionDelta[edge].x;
y += TileDirectionDelta[edge].y;
if (x > 255 * 32 || y > 255 * 32) {
return false;
}
surfaceElement = map_get_surface_element_at(x / 32, y / 32);
// TODO: extract loop A
mapElement = surfaceElement;
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) continue;
if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue;
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue;
if (peep->next_z + 6 <= mapElement->base_height) continue;
if (peep->next_z >= mapElement->clearance_height) continue;
return false;
} while (!map_element_is_last_for_tile(mapElement++));
// TODO: Extract loop B
mapElement = surfaceElement;
do {
if (mapElement->clearance_height + 1 < peep->next_z) continue;
if (peep->next_z + 8 < mapElement->base_height) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) {
if (!sub_69101A(mapElement)) {
return loc_690FD0(peep, rideToView, rideSeatToView, mapElement);
}
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) {
if (!(get_large_scenery_entry(mapElement->properties.scenerymultiple.type & 0x3FF)->large_scenery.flags & 0x10)) {
continue;
}
*rideSeatToView = 0;
if (mapElement->clearance_height >= peep->next_z + 8) {
*rideSeatToView = 0x02;
}
*rideToView = 0xFF;
return true;
}
} while (!map_element_is_last_for_tile(mapElement++));
// TODO: Extract loop C
mapElement = surfaceElement;
do {
if (mapElement->clearance_height + 1 < peep->next_z) continue;
if (peep->next_z + 8 < mapElement->base_height) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SURFACE) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_FENCE) {
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) {
continue;
}
}
return false;
} while (!map_element_is_last_for_tile(mapElement++));
x += TileDirectionDelta[edge].x;
y += TileDirectionDelta[edge].y;
if (x > 255 * 32 || y > 255 * 32) {
return false;
}
surfaceElement = map_get_surface_element_at(x / 32, y / 32);
// TODO: extract loop A
mapElement = surfaceElement;
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) continue;
if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue;
if (get_wall_entry(mapElement->properties.fence.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue;
if (peep->next_z + 8 <= mapElement->base_height) continue;
if (peep->next_z >= mapElement->clearance_height) continue;
return false;
} while (!map_element_is_last_for_tile(mapElement++));
// TODO: Extract loop B
mapElement = surfaceElement;
do {
if (mapElement->clearance_height + 1 < peep->next_z) continue;
if (peep->next_z + 10 < mapElement->base_height) continue;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) {
if (!sub_69101A(mapElement)) {
return loc_690FD0(peep, rideToView, rideSeatToView, mapElement);
}
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) {
if (!(get_large_scenery_entry(mapElement->properties.scenerymultiple.type & 0x3FF)->large_scenery.flags & 0x10)) {
continue;
}
*rideSeatToView = 0;
if (mapElement->clearance_height >= peep->next_z + 8) {
*rideSeatToView = 0x02;
}
*rideToView = 0xFF;
return true;
}
} while (!map_element_is_last_for_tile(mapElement++));
return false;
}
bool loc_690FD0(rct_peep *peep, uint8 *rideToView, uint8 *rideSeatToView, rct_map_element *esi) {
rct_ride *ride = get_ride(esi->properties.track.ride_index);
*rideToView = esi->properties.track.ride_index;
if ((uint16) ride->excitement == 0xFFFF) {
*rideSeatToView = 1;
if (ride->status != RIDE_STATUS_OPEN) {
if (esi->clearance_height > peep->next_z + 8) {
*rideSeatToView |= (1 << 1);
}
return true;
}
} else {
*rideSeatToView = 0;
if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) {
if (esi->clearance_height > peep->next_z + 8) {
*rideSeatToView = 0x02;
}
return true;
}
}
return false;
}
/**
* Gets the height including the bit depending on how far up the slope the peep
* is.
* rct2: 0x00694921
*/
static int peep_get_height_on_slope(rct_peep *peep, int x, int y)
{
if (x == (sint16)0x8000)
return 0;
if (peep->next_var_29 & 0x18){
return map_element_height(x, y) & 0xFFFF;
}
int z = peep->next_z * 8;
return z + map_height_from_slope(x, y, peep->next_var_29);
}
static bool peep_has_voucher_for_free_ride(rct_peep *peep, int rideIndex)
{
return
peep->item_standard_flags & PEEP_ITEM_VOUCHER &&
peep->voucher_type == VOUCHER_TYPE_RIDE_FREE &&
peep->voucher_arguments == rideIndex;
}
static void peep_reset_ride_heading(rct_peep *peep)
{
rct_window *w;
peep->guest_heading_to_ride_id = 255;
w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) {
window_event_invalidate_call(w);
widget_invalidate(w, 12);
}
}
/**
* This function is called whenever a peep is deciding whether or not they want
* to go on a ride or visit a shop. They may be physically present at the
* ride/shop, or they may just be thinking about it.
* rct2: 0x006960AB
*/
static bool peep_should_go_on_ride(rct_peep *peep, int rideIndex, int entranceNum, int flags)
{
rct_ride *ride = get_ride(rideIndex);
// Indicates if the peep is about to enter a queue (as opposed to entering an entrance directly from a path)
bool peepAtQueue = flags & PEEP_RIDE_DECISION_AT_QUEUE;
// Indicates whether a peep is physically at the ride, or is just thinking about going on the ride.
bool peepAtRide = !(flags & PEEP_RIDE_DECISION_THINKING);
if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) {
// Peeps that are leaving the park will refuse to go on any rides, with the exception of free transport rides.
if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_TRANSPORT_RIDE) ||
ride->value == 0xFFFF ||
ride_get_price(ride) != 0
) {
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
}
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) {
return peep_should_go_to_shop(peep, rideIndex, peepAtRide);
}
// This used to check !(flags & 2), but the function is only ever called with flags = 0, 1 or 6.
// This means we can use the existing !(flags & 4) check.
if (peepAtRide) {
// Peeps won't join a queue that has 1000 peeps already in it.
if (ride->queue_length[entranceNum] >= 1000) {
peep_tried_to_enter_full_queue(peep, rideIndex);
return false;
}
// Rides without queues can only have one peep waiting at a time.
if (!peepAtQueue) {
if (ride->last_peep_in_queue[entranceNum] != 0xFFFF) {
peep_tried_to_enter_full_queue(peep, rideIndex);
return false;
}
}
else {
// Check if there's room in the queue for the peep to enter.
if (ride->last_peep_in_queue[entranceNum] != 0xFFFF) {
rct_peep *lastPeepInQueue = GET_PEEP(ride->last_peep_in_queue[entranceNum]);
if (abs(lastPeepInQueue->z - peep->z) <= 6) {
int dx = abs(lastPeepInQueue->x - peep->x);
int dy = abs(lastPeepInQueue->y - peep->y);
int maxD = max(dx, dy);
// Unlike normal paths, peeps cannot overlap when queueing for a ride.
// This check enforces a minimum distance between peeps entering the queue.
if (maxD < 8) {
peep_tried_to_enter_full_queue(peep, rideIndex);
return false;
}
// This checks if there's a peep standing still at the very end of the queue.
if (maxD <= 13
&& lastPeepInQueue->time_in_queue > 10) {
peep_tried_to_enter_full_queue(peep, rideIndex);
return false;
}
}
}
}
}
// Assuming the queue conditions are met, peeps will always go on free transport rides.
// Ride ratings, recent crashes and weather will all be ignored.
money16 ridePrice = ride_get_price(ride);
if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_TRANSPORT_RIDE) || ride->value == 0xFFFF || ridePrice != 0) {
if (peep->previous_ride == rideIndex) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
// Basic price checks
if (ridePrice != 0 && !peep_has_voucher_for_free_ride(peep, rideIndex)) {
if (ridePrice > peep->cash_in_pocket) {
if (peepAtRide) {
if (peep->cash_in_pocket <= 0) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, 255);
}
else {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, rideIndex);
}
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
}
// If happy enough, peeps will ignore the fact that a ride has recently crashed.
if (ride->last_crash_type != RIDE_CRASH_TYPE_NONE && peep->happiness < 225) {
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NOT_SAFE, rideIndex);
if (peep->happiness_growth_rate >= 64) {
peep->happiness_growth_rate -= 8;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
if (ride->excitement != (ride_rating)0xFFFF) {
// If a peep has already decided that they're going to go on a ride, they'll skip the weather and
// excitement check and will only do a basic intensity check when they arrive at the ride itself.
if (rideIndex == peep->guest_heading_to_ride_id) {
if (ride->intensity > RIDE_RATING(10, 00) && !gCheatsIgnoreRideIntensity) {
peep_ride_is_too_intense(peep, rideIndex, peepAtRide);
return false;
}
}
// Peeps won't go on rides that aren't sufficiently undercover while it's raining.
// The threshold is fairly low and only requires about 10-15% of the ride to be undercover.
if (gClimateCurrentRainLevel != 0 && (ride->undercover_portion >> 5) < 3) {
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NOT_WHILE_RAINING, rideIndex);
if (peep->happiness_growth_rate >= 64) {
peep->happiness_growth_rate -= 8;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
if (!gCheatsIgnoreRideIntensity) {
// Intensity calculations. Even though the max intensity can go up to 15, it's capped
// at 10.0 (before happiness calculations). A full happiness bar will increase the max
// intensity and decrease the min intensity by about 2.5.
ride_rating maxIntensity = min((peep->intensity >> 4) * 100, 1000) + peep->happiness;
ride_rating minIntensity = ((peep->intensity & 0x0F) * 100) - peep->happiness;
if (ride->intensity < minIntensity) {
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_MORE_THRILLING, rideIndex);
if (peep->happiness_growth_rate >= 64) {
peep->happiness_growth_rate -= 8;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
if (ride->intensity > maxIntensity) {
peep_ride_is_too_intense(peep, rideIndex, peepAtRide);
return false;
}
// Nausea calculations.
ride_rating maxNausea = NauseaMaximumThresholds[(peep->nausea_tolerance & 3)] + peep->happiness;
if (ride->nausea > maxNausea) {
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SICKENING, rideIndex);
if (peep->happiness_growth_rate >= 64) {
peep->happiness_growth_rate -= 8;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
// Very nauseous peeps will only go on very gentle rides.
if (ride->nausea >= FIXED_2DP(1, 40) && peep->nausea > 160) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
}
}
// If the ride has not yet been rated and is capable of having g-forces,
// there's a 90% chance that the peep will ignore it.
if ((ride->excitement == (ride_rating)0xFFFF)
&& (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_PEEP_CHECK_GFORCES)) {
if ((scenario_rand() & 0xFFFF) > 0x1999U) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
if (!gCheatsIgnoreRideIntensity) {
if (ride->max_positive_vertical_g > FIXED_2DP(5, 00)
|| ride->max_negative_vertical_g < FIXED_2DP(-4, 00)
|| ride->max_lateral_g > FIXED_2DP(4, 00)) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
}
}
uint32 value = ride->value;
// If the value of the ride hasn't yet been calculated, peeps will be willing to pay any amount for the ride.
if (value != 0xFFFF && !peep_has_voucher_for_free_ride(peep, rideIndex) && !(gParkFlags & PARK_FLAGS_NO_MONEY)) {
// The amount peeps are willing to pay is decreased by 75% if they had to pay to enter the park.
if (peep->peep_flags & PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY)
value /= 4;
// Peeps won't pay more than twice the value of the ride.
money16 ridePrice = ride_get_price(ride);
if (ridePrice > (money16)(value * 2)) {
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_VALUE, rideIndex);
if (peep->happiness_growth_rate < 60) {
peep->happiness_growth_rate -= 16;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
return false;
}
// A ride is good value if the price is 50% or less of the ride value and the peep didn't pay to enter the park.
if (ridePrice <= (money16)(value / 2) && peepAtRide) {
if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) {
if (!(peep->peep_flags & PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY)) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_GOOD_VALUE, rideIndex);
}
}
}
}
}
// At this point, the peep has decided to go on the ride.
if (peepAtRide) {
ride_update_popularity(ride, 1);
}
if (rideIndex == peep->guest_heading_to_ride_id) {
peep_reset_ride_heading(peep);
}
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_QUEUE_FULL;
return true;
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false);
return false;
}
static void peep_ride_is_too_intense(rct_peep *peep, int rideIndex, bool peepAtRide)
{
rct_ride *ride = get_ride(rideIndex);
if (peepAtRide) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_INTENSE, rideIndex);
if (peep->happiness_growth_rate >= 64) {
peep->happiness_growth_rate -= 8;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, true);
}
static void peep_chose_not_to_go_on_ride(rct_peep *peep, int rideIndex, bool peepAtRide, bool updateLastRide)
{
if (peepAtRide && updateLastRide) {
peep->previous_ride = rideIndex;
peep->previous_ride_time_out = 0;
}
if (rideIndex == peep->guest_heading_to_ride_id) {
peep_reset_ride_heading(peep);
}
}
/**
* When the queue is full, peeps will ignore the ride when thinking about what to go on next.
* Does not effect peeps that walk up to the queue entrance.
* This flag is reset the next time a peep successfully joins the queue.
*/
static void peep_tried_to_enter_full_queue(rct_peep *peep, int rideIndex)
{
rct_ride *ride = get_ride(rideIndex);
ride->lifecycle_flags |= RIDE_LIFECYCLE_QUEUE_FULL;
peep->previous_ride = rideIndex;
peep->previous_ride_time_out = 0;
//Change status "Heading to" to "Walking" if queue is full
if (rideIndex == peep->guest_heading_to_ride_id) {
peep_reset_ride_heading(peep);
}
}
static bool peep_should_go_to_shop(rct_peep *peep, int rideIndex, bool peepAtShop)
{
rct_ride *ride = get_ride(rideIndex);
// Peeps won't go to the same shop twice in a row.
if (rideIndex == peep->previous_ride) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtShop, true);
return false;
}
if (ride->type == RIDE_TYPE_TOILETS) {
if (peep->bathroom < 70) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtShop, true);
return false;
}
// The amount that peeps are willing to pay to use the Toilets scales with their bathroom stat.
// It effectively has a minimum of $0.10 (due to the check above) and a maximum of $0.60.
if (ride->price * 40 > peep->bathroom) {
if (peepAtShop) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_NOT_PAYING, rideIndex);
if (peep->happiness_growth_rate >= 60) {
peep->happiness_growth_rate -= 16;
}
ride_update_popularity(ride, 0);
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtShop, true);
return false;
}
}
if (ride->type == RIDE_TYPE_FIRST_AID) {
if (peep->nausea < 128) {
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtShop, true);
return false;
}
}
// Basic price checks
if (ride->price != 0 && ride->price > peep->cash_in_pocket) {
if (peepAtShop) {
if (peep->cash_in_pocket <= 0) {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, 255);
}
else {
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, rideIndex);
}
}
peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtShop, true);
return false;
}
if (peepAtShop) {
ride_update_popularity(ride, 1);
if (rideIndex == peep->guest_heading_to_ride_id) {
peep_reset_ride_heading(peep);
}
}
return true;
}
/**
*
* rct2: 0x00695DD2
*/
static void peep_pick_ride_to_go_on(rct_peep *peep)
{
rct_ride *ride;
if (peep->state != PEEP_STATE_WALKING) return;
if (peep->guest_heading_to_ride_id != 255) return;
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) return;
if (peep_has_food(peep)) return;
if (peep->x == (sint16)0x8000) return;
for (int i = 0; i < countof(_peepRideConsideration); i++) {
_peepRideConsideration[i] = 0;
}
// FIX Originally checked for a toy, likely a mistake and should be a map,
// but then again this seems to only allow the peep to go on
// rides they haven't been on before.
if (peep->item_standard_flags & PEEP_ITEM_MAP) {
// Consider rides that peep hasn't been on yet
int i;
FOR_ALL_RIDES(i, ride) {
if (!peep_has_ridden(peep, i)) {
_peepRideConsideration[i >> 5] |= (1u << (i & 0x1F));
}
}
} else {
// Take nearby rides into consideration
int cx = floor2(peep->x, 32);
int cy = floor2(peep->y, 32);
for (int x = cx - 320; x <= cx + 320; x += 32) {
for (int y = cy - 320; y <= cy + 320; y += 32) {
if (x >= 0 && y >= 0 && x < (256 * 32) && y < (256 * 32)) {
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
int rideIndex = mapElement->properties.track.ride_index;
_peepRideConsideration[rideIndex >> 5] |= (1u << (rideIndex & 0x1F));
} while (!map_element_is_last_for_tile(mapElement++));
}
}
}
// Always take the big rides into consideration (realistic as you can usually see them from anywhere in the park)
int i;
FOR_ALL_RIDES(i, ride) {
if (ride->lifecycle_flags == RIDE_LIFECYCLE_TESTED) continue;
if (ride->excitement == (ride_rating)0xFFFF) continue;
if (ride->highest_drop_height <= 66 && ride->excitement < RIDE_RATING(8,00)) continue;
_peepRideConsideration[i >> 5] |= (1u << (i & 0x1F));
}
}
// Filter the considered rides
uint8 *potentialRides = _peepPotentialRides;
uint8 *nextPotentialRide = potentialRides;
int numPotentialRides = 0;
for (int i = 0; i < MAX_RIDES; i++) {
if (!(_peepRideConsideration[i >> 5] & (1u << (i & 0x1F))))
continue;
rct_ride *ride = get_ride(i);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) {
if (peep_should_go_on_ride(peep, i, 0, PEEP_RIDE_DECISION_THINKING)) {
*nextPotentialRide++ = i;
numPotentialRides++;
}
}
}
// Pick the most exciting ride
int mostExcitingRideIndex = -1;
ride_rating mostExcitingRideRating = 0;
for (int i = 0; i < numPotentialRides; i++) {
ride = get_ride(potentialRides[i]);
if (ride->excitement == (ride_rating)0xFFFF) continue;
if (ride->excitement > mostExcitingRideRating) {
mostExcitingRideIndex = potentialRides[i];
mostExcitingRideRating = ride->excitement;
}
}
if (mostExcitingRideIndex == -1)
return;
// Head to that ride
peep->guest_heading_to_ride_id = mostExcitingRideIndex;
peep->peep_is_lost_countdown = 200;
peep_reset_pathfind_goal(peep);
// Invalidate windows
rct_window *w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) {
window_event_invalidate_call(w);
widget_invalidate(w, 12);
}
// Make peep look at their map if they have one
if (peep->item_standard_flags & PEEP_ITEM_MAP) {
peep_read_map(peep);
}
}
/**
*
* rct2: 0x00695B70
*/
static void peep_head_for_nearest_ride_type(rct_peep *peep, int rideType)
{
rct_ride *ride;
if (peep->state != PEEP_STATE_SITTING && peep->state != PEEP_STATE_WATCHING && peep->state != PEEP_STATE_WALKING) {
return;
}
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) return;
if (peep->x == (sint16)0x8000) return;
if (peep->guest_heading_to_ride_id != 255) {
ride = get_ride(peep->guest_heading_to_ride_id);
if (ride->type == rideType) {
return;
}
}
for (int i = 0; i < countof(_peepRideConsideration); i++) {
_peepRideConsideration[i] = 0;
}
// FIX Originally checked for a toy,.likely a mistake and should be a map
if ((peep->item_standard_flags & PEEP_ITEM_MAP) && rideType != RIDE_TYPE_FIRST_AID) {
// Consider all rides in the park
int i;
FOR_ALL_RIDES(i, ride) {
if (ride->type == rideType) {
_peepRideConsideration[i >> 5] |= (1u << (i & 0x1F));
}
}
} else {
// Take nearby rides into consideration
int cx = floor2(peep->x, 32);
int cy = floor2(peep->y, 32);
for (int x = cx - 320; x <= cx + 320; x += 32) {
for (int y = cy - 320; y <= cy + 320; y += 32) {
if (x >= 0 && y >= 0 && x < (256 * 32) && y < (256 * 32)) {
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
int rideIndex = mapElement->properties.track.ride_index;
ride = get_ride(rideIndex);
if (ride->type == rideType) {
_peepRideConsideration[rideIndex >> 5] |= (1u << (rideIndex & 0x1F));
}
} while (!map_element_is_last_for_tile(mapElement++));
}
}
}
}
// Filter the considered rides
uint8 *potentialRides = _peepPotentialRides;
uint8 *nextPotentialRide = potentialRides;
int numPotentialRides = 0;
for (int i = 0; i < MAX_RIDES; i++) {
if (!(_peepRideConsideration[i >> 5] & (1u << (i & 0x1F))))
continue;
rct_ride *ride = get_ride(i);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) {
if (peep_should_go_on_ride(peep, i, 0, PEEP_RIDE_DECISION_THINKING)) {
*nextPotentialRide++ = i;
numPotentialRides++;
}
}
}
// Pick the closest ride
int closestRideIndex = -1;
int closestRideDistance = INT_MAX;
for (int i = 0; i < numPotentialRides; i++) {
ride = get_ride(potentialRides[i]);
int rideX = (ride->station_starts[0] & 0xFF) * 32;
int rideY = (ride->station_starts[0] >> 8) * 32;
int distance = abs(rideX - peep->x) + abs(rideY - peep->y);
if (distance < closestRideDistance) {
closestRideIndex = potentialRides[i];
closestRideDistance = distance;
}
}
if (closestRideIndex == -1)
return;
// Head to that ride
peep->guest_heading_to_ride_id = closestRideIndex;
peep->peep_is_lost_countdown = 200;
peep_reset_pathfind_goal(peep);
// Invalidate windows
rct_window *w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) {
window_event_invalidate_call(w);
widget_invalidate(w, 12);
}
peep->var_F4 = 0;
}
/**
*
* rct2: 0x006958D0
*/
static void peep_head_for_nearest_ride_with_flags(rct_peep *peep, int rideTypeFlags)
{
rct_ride *ride;
if (peep->state != PEEP_STATE_SITTING && peep->state != PEEP_STATE_WATCHING && peep->state != PEEP_STATE_WALKING) {
return;
}
if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) return;
if (peep->x == (sint16)0x8000) return;
if (peep->guest_heading_to_ride_id != 255) {
ride = get_ride(peep->guest_heading_to_ride_id);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_BATHROOM | RIDE_TYPE_FLAG_SELLS_DRINKS | RIDE_TYPE_FLAG_SELLS_FOOD)) {
return;
}
}
if ((rideTypeFlags & RIDE_TYPE_FLAG_IS_BATHROOM) && peep_has_food(peep)) {
return;
}
for (int i = 0; i < countof(_peepRideConsideration); i++) {
_peepRideConsideration[i] = 0;
}
// FIX Originally checked for a toy,.likely a mistake and should be a map
if (peep->item_standard_flags & PEEP_ITEM_MAP) {
// Consider all rides in the park
int i;
FOR_ALL_RIDES(i, ride) {
if (ride_type_has_flag(ride->type, rideTypeFlags)) {
_peepRideConsideration[i >> 5] |= (1u << (i & 0x1F));
}
}
} else {
// Take nearby rides into consideration
int cx = floor2(peep->x, 32);
int cy = floor2(peep->y, 32);
for (int x = cx - 320; x <= cx + 320; x += 32) {
for (int y = cy - 320; y <= cy + 320; y += 32) {
if (x >= 0 && y >= 0 && x < (256 * 32) && y < (256 * 32)) {
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
int rideIndex = mapElement->properties.track.ride_index;
ride = get_ride(rideIndex);
if (ride_type_has_flag(ride->type, rideTypeFlags)) {
_peepRideConsideration[rideIndex >> 5] |= (1u << (rideIndex & 0x1F));
}
} while (!map_element_is_last_for_tile(mapElement++));
}
}
}
}
// Filter the considered rides
uint8 *potentialRides = _peepPotentialRides;
uint8 *nextPotentialRide = potentialRides;
int numPotentialRides = 0;
for (int i = 0; i < MAX_RIDES; i++) {
if (!(_peepRideConsideration[i >> 5] & (1u << (i & 0x1F))))
continue;
rct_ride *ride = get_ride(i);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) {
if (peep_should_go_on_ride(peep, i, 0, PEEP_RIDE_DECISION_THINKING)) {
*nextPotentialRide++ = i;
numPotentialRides++;
}
}
}
// Pick the closest ride
int closestRideIndex = -1;
int closestRideDistance = INT_MAX;
for (int i = 0; i < numPotentialRides; i++) {
ride = get_ride(potentialRides[i]);
int rideX = (ride->station_starts[0] & 0xFF) * 32;
int rideY = (ride->station_starts[0] >> 8) * 32;
int distance = abs(rideX - peep->x) + abs(rideY - peep->y);
if (distance < closestRideDistance) {
closestRideIndex = potentialRides[i];
closestRideDistance = distance;
}
}
if (closestRideIndex == -1)
return;
// Head to that ride
peep->guest_heading_to_ride_id = closestRideIndex;
peep->peep_is_lost_countdown = 200;
peep_reset_pathfind_goal(peep);
// Invalidate windows
rct_window *w = window_find_by_number(WC_PEEP, peep->sprite_index);
if (w != NULL) {
window_event_invalidate_call(w);
window_invalidate(w);
}
peep->var_F4 = 0;
}
/**
*
* rct2: 0x0069C483
*/
static void peep_give_real_name(rct_peep *peep)
{
// Generate a name_string_idx from the peep id using bit twiddling
uint16 ax = (uint16)(peep->id + 0xF0B);
uint16 dx = 0;
dx |= ((ax & 0x400) ? 1 : 0) << 13;
dx |= ((ax & 0x2000) ? 1 : 0) << 12;
dx |= ((ax & 0x800) ? 1 : 0) << 11;
dx |= ((ax & 0x400) ? 1 : 0) << 10;
dx |= ((ax & 0x1) ? 1 : 0) << 9;
dx |= ((ax & 0x40) ? 1 : 0) << 8;
dx |= ((ax & 0x2) ? 1 : 0) << 7;
dx |= ((ax & 0x4) ? 1 : 0) << 6;
dx |= ((ax & 0x100) ? 1 : 0) << 5;
dx |= ((ax & 0x20) ? 1 : 0) << 4;
dx |= ((ax & 0x80) ? 1 : 0) << 3;
dx |= ((ax & 0x8) ? 1 : 0) << 2;
dx |= ((ax & 0x200) ? 1 : 0) << 1;
dx |= ((ax & 0x10) ? 1 : 0) << 0;
ax = dx & 0xF;
dx *= 4;
ax *= 4096;
dx += ax;
if (dx < ax) {
dx += 0x1000;
}
dx /= 4;
dx += 0xA000;
peep->name_string_idx = dx;
}
static int peep_compare(const void *sprite_index_a, const void *sprite_index_b)
{
rct_peep const *peep_a = GET_PEEP(*(uint16*)sprite_index_a);
rct_peep const *peep_b = GET_PEEP(*(uint16*)sprite_index_b);
// Compare types
if (peep_a->type != peep_b->type) {
return peep_a->type - peep_b->type;
}
// Simple ID comparison for when both peeps use a number or a generated name
const bool both_numbers = (
peep_a->name_string_idx >= 767 && peep_a->name_string_idx <= 771 &&
peep_b->name_string_idx >= 767 && peep_b->name_string_idx <= 771
);
if (both_numbers) {
return peep_a->id - peep_b->id;
}
const bool both_have_generated_names = (
peep_a->name_string_idx >= 0xA000 && peep_a->name_string_idx < 0xE000 &&
peep_b->name_string_idx >= 0xA000 && peep_b->name_string_idx < 0xE000
);
if (both_have_generated_names) {
rct_string_id peep_a_format = peep_a->name_string_idx + 0xA000;
rct_string_id peep_b_format = peep_b->name_string_idx + 0xA000;
uint16 peep_a_name = (peep_a_format % countof(real_names));
uint16 peep_b_name = (peep_b_format % countof(real_names));
if (peep_a_name == peep_b_name) {
uint16 peep_a_initial = ((peep_a_format >> 10) % countof(real_name_initials));
uint16 peep_b_initial = ((peep_b_format >> 10) % countof(real_name_initials));
return peep_a_initial - peep_b_initial;
} else {
return peep_a_name - peep_b_name;
}
}
// At least one of them has a custom name assigned
// Compare their names as strings
utf8 name_a[256];
utf8 name_b[256];
uint32 peepIndex = peep_a->id;
format_string(name_a, 256, peep_a->name_string_idx, &peepIndex);
peepIndex = peep_b->id;
format_string(name_b, 256, peep_b->name_string_idx, &peepIndex);
return strlogicalcmp(name_a, name_b);
}
/**
*
* rct2: 0x00699115
*/
void peep_update_name_sort(rct_peep *peep)
{
// Remove peep from sprite list
uint16 nextSpriteIndex = peep->next;
uint16 prevSpriteIndex = peep->previous;
if (prevSpriteIndex != SPRITE_INDEX_NULL) {
rct_peep *prevPeep = GET_PEEP(prevSpriteIndex);
prevPeep->next = nextSpriteIndex;
} else {
gSpriteListHead[SPRITE_LIST_PEEP] = nextSpriteIndex;
}
if (nextSpriteIndex != SPRITE_INDEX_NULL) {
rct_peep *nextPeep = GET_PEEP(nextSpriteIndex);
nextPeep->previous = prevSpriteIndex;
}
rct_peep *otherPeep;
uint16 spriteIndex;
FOR_ALL_PEEPS(spriteIndex, otherPeep) {
// Check if peep should go before this one
if (peep_compare(&peep->sprite_index, &otherPeep->sprite_index) >= 0) {
continue;
}
// Place peep before this one
peep->previous = otherPeep->previous;
otherPeep->previous = peep->sprite_index;
if (peep->previous != SPRITE_INDEX_NULL) {
rct_peep *prevPeep = GET_PEEP(peep->previous);
peep->next = prevPeep->next;
prevPeep->next = peep->sprite_index;
} else {
peep->next = gSpriteListHead[SPRITE_LIST_PEEP];
gSpriteListHead[SPRITE_LIST_PEEP] = peep->sprite_index;
}
goto finish_peep_sort;
}
// Place peep at the end
FOR_ALL_PEEPS(spriteIndex, otherPeep) {
if (otherPeep->next == SPRITE_INDEX_NULL) {
otherPeep->next = peep->sprite_index;
peep->previous = otherPeep->sprite_index;
peep->next = SPRITE_INDEX_NULL;
goto finish_peep_sort;
}
}
gSpriteListHead[SPRITE_LIST_PEEP] = peep->sprite_index;
peep->next = SPRITE_INDEX_NULL;
peep->previous = SPRITE_INDEX_NULL;
finish_peep_sort:
// This is required at the moment because this function reorders peeps in the sprite list
openrct2_reset_object_tween_locations();
}
void peep_sort()
{
// Count number of peeps
uint16 sprite_index, num_peeps = 0;
rct_peep *peep;
FOR_ALL_PEEPS(sprite_index, peep) {
num_peeps++;
}
// No need to sort
if (num_peeps < 2)
return;
// Create a copy of the peep list and sort it using peep_compare
uint16 *peep_list = (uint16*)malloc(num_peeps * sizeof(uint16));
int i = 0;
FOR_ALL_PEEPS(sprite_index, peep) {
peep_list[i++] = peep->sprite_index;
}
qsort(peep_list, num_peeps, sizeof(uint16), peep_compare);
// Set the correct peep->next and peep->previous using the sorted list
for (i = 0; i < num_peeps; i++) {
peep = GET_PEEP(peep_list[i]);
peep->previous = (i > 0) ? peep_list[i - 1] : SPRITE_INDEX_NULL;
peep->next = (i + 1 < num_peeps) ? peep_list[i + 1] : SPRITE_INDEX_NULL;
}
// Make sure the first peep is set
gSpriteListHead[SPRITE_LIST_PEEP] = peep_list[0];
free(peep_list);
i = 0;
FOR_ALL_PEEPS(sprite_index, peep) {
i++;
}
assert(i == num_peeps);
}
/**
*
* rct2: 0x0069926C
*/
void peep_update_names(bool realNames)
{
if (realNames) {
gParkFlags |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
rct_peep *peep;
uint16 spriteIndex;
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->name_string_idx == STR_GUEST_X) {
peep_give_real_name(peep);
}
}
} else {
gParkFlags &= ~PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
rct_peep *peep;
uint16 spriteIndex;
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->name_string_idx >= 0xA000 && peep->name_string_idx < 0xE000) {
peep->name_string_idx = STR_GUEST_X;
}
}
}
peep_sort();
gfx_invalidate_screen();
}
static void peep_read_map(rct_peep *peep)
{
if (peep->action == PEEP_ACTION_NONE_1 || peep->action == PEEP_ACTION_NONE_2) {
peep->action = PEEP_ACTION_READ_MAP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
sub_693B58(peep);
invalidate_sprite_2((rct_sprite*)peep);
}
}
static bool peep_heading_for_ride_or_park_exit(rct_peep *peep)
{
return (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) || peep->guest_heading_to_ride_id != 0xFF;
}
money32 set_peep_name(int flags, int state, uint16 sprite_index, uint8* text_1, uint8* text_2, uint8* text_3) {
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
static char newName[128];
//if (flags & GAME_COMMAND_FLAG_APPLY) { // this check seems to be useless and causes problems in multiplayer
uint8 position = (state - 1) % 3;
memcpy(newName + position * 12, text_1, 4);
memcpy(newName + 4 + position * 12, text_2, 4);
memcpy(newName + 8 + position * 12, text_3, 4);
//}
if (state != 0)
return 0;
rct_peep* peep = GET_PEEP(sprite_index);
set_format_arg(0, uint32, peep->id);
utf8* curName = gCommonStringFormatBuffer;
rct_string_id curId = peep->name_string_idx;
format_string(curName, 256, curId, gCommonFormatArgs);
if (strcmp(curName, newName) == 0)
return 0;
if (*newName == '\0') {
gGameCommandErrorText = STR_ERR_INVALID_NAME_FOR_GUEST;
return MONEY32_UNDEFINED;
}
rct_string_id newId = user_string_allocate(4, newName);
if (newId == 0) {
return MONEY32_UNDEFINED;
}
if (!(flags & GAME_COMMAND_FLAG_APPLY)) {
user_string_free(newId);
return 0;
}
user_string_free(curId);
peep->name_string_idx = newId;
peep_update_name_sort(peep);
peep->peep_flags &= ~PEEP_FLAGS_WAVING;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_BRAYSHAW, peep)) {
peep->peep_flags |= PEEP_FLAGS_WAVING;
}
peep->peep_flags &= ~PEEP_FLAGS_PHOTO;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CHRIS_SAWYER, peep)) {
peep->peep_flags |= PEEP_FLAGS_PHOTO;
}
peep->peep_flags &= ~PEEP_FLAGS_PAINTING;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_SIMON_FOSTER, peep)) {
peep->peep_flags |= PEEP_FLAGS_PAINTING;
}
peep->peep_flags &= ~PEEP_FLAGS_WOW;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_JOHN_WARDLEY, peep)) {
peep->peep_flags |= PEEP_FLAGS_WOW;
}
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_MELANIE_WARN, peep)) {
peep->happiness = 250;
peep->happiness_growth_rate = 250;
peep->energy = 127;
peep->energy_growth_rate = 127;
peep->nausea = 0;
peep->nausea_growth_rate = 0;
}
peep->peep_flags &= ~PEEP_FLAGS_LITTER;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_LISA_STIRLING, peep)) {
peep->peep_flags |= PEEP_FLAGS_LITTER;
}
peep->peep_flags &= ~PEEP_FLAGS_LOST;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_DONALD_MACRAE, peep)) {
peep->peep_flags |= PEEP_FLAGS_LOST;
}
peep->peep_flags &= ~PEEP_FLAGS_HUNGER;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATHERINE_MCGOWAN, peep)) {
peep->peep_flags |= PEEP_FLAGS_HUNGER;
}
peep->peep_flags &= ~PEEP_FLAGS_BATHROOM;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_FRANCES_MCGOWAN, peep)) {
peep->peep_flags |= PEEP_FLAGS_BATHROOM;
}
peep->peep_flags &= ~PEEP_FLAGS_CROWDED;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CORINA_MASSOURA, peep)) {
peep->peep_flags |= PEEP_FLAGS_CROWDED;
}
peep->peep_flags &= ~PEEP_FLAGS_HAPPINESS;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CAROL_YOUNG, peep)) {
peep->peep_flags |= PEEP_FLAGS_HAPPINESS;
}
peep->peep_flags &= ~PEEP_FLAGS_NAUSEA;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_MIA_SHERIDAN, peep)) {
peep->peep_flags |= PEEP_FLAGS_NAUSEA;
}
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_RODGER, peep)) {
peep->peep_flags |= PEEP_FLAGS_LEAVING_PARK;
peep->peep_flags &= ~PEEP_FLAGS_PARK_ENTRANCE_CHOSEN;
}
peep->peep_flags &= ~PEEP_FLAGS_PURPLE;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_EMMA_GARRELL, peep)) {
peep->peep_flags |= PEEP_FLAGS_PURPLE;
}
peep->peep_flags &= ~PEEP_FLAGS_PIZZA;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_JOANNE_BARTON, peep)) {
peep->peep_flags |= PEEP_FLAGS_PIZZA;
}
peep->peep_flags &= ~PEEP_FLAGS_CONTAGIOUS;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_FELICITY_ANDERSON, peep)) {
peep->peep_flags |= PEEP_FLAGS_CONTAGIOUS;
}
peep->peep_flags &= ~PEEP_FLAGS_JOY;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_SMITH, peep)) {
peep->peep_flags |= PEEP_FLAGS_JOY;
}
peep->peep_flags &= ~PEEP_FLAGS_ANGRY;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_EILIDH_BELL, peep)) {
peep->peep_flags |= PEEP_FLAGS_ANGRY;
}
peep->peep_flags &= ~PEEP_FLAGS_ICE_CREAM;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_NANCY_STILLWAGON, peep)) {
peep->peep_flags |= PEEP_FLAGS_ICE_CREAM;
}
peep->peep_flags &= ~PEEP_FLAGS_HERE_WE_ARE;
if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_DAVID_ELLIS, peep)) {
peep->peep_flags |= PEEP_FLAGS_HERE_WE_ARE;
}
gfx_invalidate_screen();
return 0;
}
/**
*
* rct2: 0x00698D6C
*/
void game_command_set_guest_name(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) {
uint16 sprite_index = *ecx & 0xFFFF;
if (sprite_index >= MAX_SPRITES) {
*ebx = MONEY32_UNDEFINED;
return;
}
rct_peep *peep = GET_PEEP(sprite_index);
if (peep->type != PEEP_TYPE_GUEST) {
*ebx = MONEY32_UNDEFINED;
return;
}
*ebx = set_peep_name(
*ebx & 0xFF,
*eax & 0xFFFF,
sprite_index,
(uint8*)edx,
(uint8*)ebp,
(uint8*)edi
);
}