diff --git a/src/openrct2/peep/Guest.cpp b/src/openrct2/peep/Guest.cpp index 526735572a..b1cee3626f 100644 --- a/src/openrct2/peep/Guest.cpp +++ b/src/openrct2/peep/Guest.cpp @@ -3050,3 +3050,740 @@ void rct_peep::UpdateRide() break; } } + +/** + * + * rct2: 0x0069030A + */ +void rct_peep::UpdateWalking() +{ + if (!CheckForPath()) + return; + + if (peep_flags & PEEP_FLAGS_WAVING) + { + if (action >= PEEP_ACTION_NONE_1) + { + if ((0xFFFF & scenario_rand()) < 936) + { + invalidate_sprite_2((rct_sprite *)this); + + action = PEEP_ACTION_WAVE_2; + action_frame = 0; + action_sprite_image_offset = 0; + + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + } + } + } + + if (peep_flags & PEEP_FLAGS_PHOTO) + { + if (action >= PEEP_ACTION_NONE_1) + { + if ((0xFFFF & scenario_rand()) < 936) + { + invalidate_sprite_2((rct_sprite *)this); + + action = PEEP_ACTION_TAKE_PHOTO; + action_frame = 0; + action_sprite_image_offset = 0; + + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + } + } + } + + if (peep_flags & PEEP_FLAGS_PAINTING) + { + if (action >= PEEP_ACTION_NONE_1) + { + if ((0xFFFF & scenario_rand()) < 936) + { + invalidate_sprite_2((rct_sprite *)this); + + action = PEEP_ACTION_DRAW_PICTURE; + action_frame = 0; + action_sprite_image_offset = 0; + + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + } + } + } + + if (peep_flags & PEEP_FLAGS_LITTER) + { + if (!(next_var_29 & 0x18)) + { + if ((0xFFFF & scenario_rand()) <= 4096) + { + static constexpr const uint8 litter_types[] = { + LITTER_TYPE_EMPTY_CAN, + LITTER_TYPE_RUBBISH, + LITTER_TYPE_EMPTY_BURGER_BOX, + LITTER_TYPE_EMPTY_CUP, + }; + sint32 ebp = litter_types[scenario_rand() & 0x3]; + sint32 litterX = x + (scenario_rand() & 0x7) - 3; + sint32 litterY = y + (scenario_rand() & 0x7) - 3; + sint32 litterDirection = (scenario_rand() & 0x3); + + litter_create(litterX, litterY, z, litterDirection, ebp); + } + } + } + else if (peep_has_empty_container(this)) + { + if ((!(next_var_29 & 0x18)) && ((uint32)(sprite_index & 0x1FF) == (gCurrentTicks & 0x1FF)) && + ((0xFFFF & scenario_rand()) <= 4096)) + { + + uint8 pos_stnd = 0; + for (sint32 container = peep_empty_container_standard_flag(this); pos_stnd < 32; pos_stnd++) + if (container & (1u << pos_stnd)) + break; + + sint32 bp = 0; + + if (pos_stnd != 32) + { + item_standard_flags &= ~(1u << pos_stnd); + bp = item_standard_litter[pos_stnd]; + } + else + { + uint8 pos_extr = 0; + for (sint32 container = peep_empty_container_extra_flag(this); pos_extr < 32; pos_extr++) + if (container & (1u << pos_extr)) + break; + item_extra_flags &= ~(1u << pos_extr); + bp = item_extra_litter[pos_extr]; + } + + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; + peep_update_sprite_type(this); + + sint32 litterX = x + (scenario_rand() & 0x7) - 3; + sint32 litterY = y + (scenario_rand() & 0x7) - 3; + sint32 litterDirection = (scenario_rand() & 0x3); + + litter_create(litterX, litterY, z, litterDirection, bp); + } + } + + uint8 pathingResult; + PerformNextAction(pathingResult); + if (!(pathingResult & PATHING_DESTINATION_REACHED)) + return; + + if ((next_var_29 & 0x18) == 8) + { + rct_tile_element * tile_element = map_get_surface_element_at({next_x, next_y}); + + sint32 water_height = surface_get_water_height(tile_element); + if (water_height) + { + invalidate_sprite_2((rct_sprite *)this); + water_height *= 16; + sprite_move(x, y, water_height, (rct_sprite *)this); + invalidate_sprite_2((rct_sprite *)this); + + SetState(PEEP_STATE_FALLING); + return; + } + } + + peep_check_if_lost(this); + peep_check_cant_find_ride(this); + peep_check_cant_find_exit(this); + + if (peep_update_walking_find_bench(this)) + return; + + if (peep_update_walking_find_bin(this)) + return; + + peep_update_walking_break_scenery(this); + + if (state != PEEP_STATE_WALKING) + return; + + if (peep_flags & PEEP_FLAGS_LEAVING_PARK) + return; + + if (nausea > 140) + return; + + if (happiness < 120) + return; + + if (toilet > 140) + return; + + uint16 chance = HasFoodExtraFlag() ? 13107 : 2849; + + if ((scenario_rand() & 0xFFFF) > chance) + return; + + if (next_var_29 & 0x1C) + return; + + rct_tile_element * tile_element = map_get_first_element_at(next_x / 32, next_y / 32); + + for (;; tile_element++) + { + if (tile_element->GetType() == TILE_ELEMENT_TYPE_PATH) + { + if (next_z == tile_element->base_height) + break; + } + if (tile_element_is_last_for_tile(tile_element)) + { + return; + } + } + + sint32 positions_free = 15; + + if (footpath_element_has_path_scenery(tile_element)) + { + if (!footpath_element_path_scenery_is_ghost(tile_element)) + { + rct_scenery_entry * sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(tile_element)); + if (sceneryEntry == nullptr) + { + return; + } + + if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BENCH)) + positions_free = 9; + } + } + + sint32 edges = (tile_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(this, chosen_edge, &ride_to_view, &ride_seat_to_view)) + return; + + // Check if there is a peep watching (and if there is place for us) + uint16 sprite_id = sprite_get_first_in_quadrant(x, 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 (z != sprite->peep.z) + continue; + + if ((sprite->peep.var_37 & 0x3) != chosen_edge) + continue; + + positions_free &= ~(1 << ((sprite->peep.var_37 & 0x1C) >> 2)); + } + + if (!positions_free) + return; + + uint8 chosen_position = scenario_rand() & 0x3; + + for (; !(positions_free & (1 << chosen_position));) + chosen_position = (chosen_position + 1) & 3; + + current_ride = ride_to_view; + current_seat = ride_seat_to_view; + var_37 = chosen_edge | (chosen_position << 2); + + SetState(PEEP_STATE_WATCHING); + sub_state = 0; + + sint32 ebx = var_37 & 0x1F; + sint32 destX = (x & 0xFFE0) + _981F4C[ebx].x; + sint32 destY = (y & 0xFFE0) + _981F4C[ebx].y; + + destination_x = destX; + destination_y = destY; + destination_tolerance = 3; + + if (current_seat & 1) + { + peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NEW_RIDE, PEEP_THOUGHT_ITEM_NONE); + } + if (current_ride == 0xFF) + { + peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SCENERY, PEEP_THOUGHT_ITEM_NONE); + } +} + +/** + * + * rct2: 0x69185D + */ +void rct_peep::UpdateQueuing() +{ + if (!CheckForPath()) + { + RemoveFromQueue(); + return; + } + Ride * ride = get_ride(current_ride); + if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING) + { + RemoveFromQueue(); + SetState(PEEP_STATE_1); + return; + } + + if (sub_state != 10) + { + bool is_front = true; + if (next_in_queue != SPRITE_INDEX_NULL) + { + // Fix #4819: Occasionally the peep->next_in_queue is incorrectly set + // to prevent this from causing the peeps to enter a loop + // first check if the next in queue is actually nearby + // if they are not then it's safe to assume that this is + // the front of the queue. + rct_peep * next_peep = GET_PEEP(next_in_queue); + if (abs(next_peep->x - x) < 32 && abs(next_peep->y - y) < 32) + { + is_front = false; + } + } + + if (is_front) + { + // Happens every time peep goes onto ride. + destination_tolerance = 0; + SetState(PEEP_STATE_QUEUING_FRONT); + sub_state = PEEP_RIDE_AT_ENTRANCE; + return; + } + + // Give up queueing for the ride + sprite_direction ^= (1 << 4); + invalidate_sprite_2((rct_sprite *)this); + RemoveFromQueue(); + SetState(PEEP_STATE_1); + return; + } + + uint8 pathingResult; + PerformNextAction(pathingResult); + if (action < 0xFE) + return; + if (sprite_type == PEEP_SPRITE_TYPE_NORMAL) + { + if (time_in_queue >= 2000 && (0xFFFF & scenario_rand()) <= 119) + { + // Eat Food/Look at watch + action = PEEP_ACTION_EAT_FOOD; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + } + if (time_in_queue >= 3500 && (0xFFFF & scenario_rand()) <= 93) + { + // Create the I have been waiting in line ages thought + peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_QUEUING_AGES, current_ride); + } + } + else + { + if (!(time_in_queue & 0x3F) && action == 0xFE && next_action_sprite_type == 2) + { + switch (sprite_type) + { + case PEEP_SPRITE_TYPE_ICE_CREAM: + case PEEP_SPRITE_TYPE_CHIPS: + case PEEP_SPRITE_TYPE_BURGER: + case PEEP_SPRITE_TYPE_DRINK: + case PEEP_SPRITE_TYPE_CANDYFLOSS: + case PEEP_SPRITE_TYPE_PIZZA: + case PEEP_SPRITE_TYPE_POPCORN: + case PEEP_SPRITE_TYPE_HOT_DOG: + case PEEP_SPRITE_TYPE_TENTACLE: + case PEEP_SPRITE_TYPE_TOFFEE_APPLE: + case PEEP_SPRITE_TYPE_DOUGHNUT: + case PEEP_SPRITE_TYPE_COFFEE: + case PEEP_SPRITE_TYPE_CHICKEN: + case PEEP_SPRITE_TYPE_LEMONADE: + case PEEP_SPRITE_TYPE_PRETZEL: + case PEEP_SPRITE_TYPE_SU_JONGKWA: + case PEEP_SPRITE_TYPE_JUICE: + case PEEP_SPRITE_TYPE_FUNNEL_CAKE: + case PEEP_SPRITE_TYPE_NOODLES: + case PEEP_SPRITE_TYPE_SAUSAGE: + case PEEP_SPRITE_TYPE_SOUP: + case PEEP_SPRITE_TYPE_SANDWICH: + // Eat food + action = PEEP_ACTION_EAT_FOOD; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + break; + } + } + } + if (time_in_queue < 4300) + return; + + if (happiness <= 65 && (0xFFFF & scenario_rand()) < 2184) + { + // Give up queueing for the ride + sprite_direction ^= (1 << 4); + invalidate_sprite_2((rct_sprite *)this); + RemoveFromQueue(); + SetState(PEEP_STATE_1); + } +} + +/** + * rct2: 0x691451 + */ +void rct_peep::UpdateEnteringPark() +{ + if (var_37 != 1) + { + uint8 pathingResult; + PerformNextAction(pathingResult); + if ((pathingResult & PATHING_OUTSIDE_PARK)) + { + decrement_guests_heading_for_park(); + peep_sprite_remove(this); + } + return; + } + sint16 actionX = 0; + sint16 actionY = 0; + sint16 xy_distance; + if (UpdateAction(&actionX, &actionY, &xy_distance)) + { + invalidate_sprite_2((rct_sprite *)this); + sprite_move(actionX, actionY, z, (rct_sprite *)this); + invalidate_sprite_2((rct_sprite *)this); + return; + } + SetState(PEEP_STATE_FALLING); + + outside_of_park = 0; + time_in_park = gScenarioTicks; + increment_guests_in_park(); + decrement_guests_heading_for_park(); + auto intent = Intent(INTENT_ACTION_UPDATE_GUEST_COUNT); + context_broadcast_intent(&intent); +} + +/** + * + * rct2: 0x6914CD + */ +void rct_peep::UpdateLeavingPark() +{ + if (var_37 != 0) + { + uint8 pathingResult; + PerformNextAction(pathingResult); + if (!(pathingResult & PATHING_OUTSIDE_PARK)) + return; + peep_sprite_remove(this); + return; + } + + sint16 actionX = 0; + sint16 actionY = 0; + sint16 xy_distance; + if (UpdateAction(&actionX, &actionY, &xy_distance)) + { + invalidate_sprite_2((rct_sprite *)this); + sprite_move(actionX, actionY, z, (rct_sprite *)this); + invalidate_sprite_2((rct_sprite *)this); + return; + } + + outside_of_park = 1; + destination_tolerance = 5; + decrement_guests_in_park(); + auto intent = Intent(INTENT_ACTION_UPDATE_GUEST_COUNT); + context_broadcast_intent(&intent); + var_37 = 1; + + window_invalidate_by_class(WC_GUEST_LIST); + uint8 pathingResult; + PerformNextAction(pathingResult); + if (!(pathingResult & PATHING_OUTSIDE_PARK)) + return; + peep_sprite_remove(this); +} + +/** + * + * rct2: 0x6916D6 + */ +void rct_peep::UpdateWatching() +{ + if (sub_state == 0) + { + if (!CheckForPath()) + return; + uint8 pathingResult; + PerformNextAction(pathingResult); + if (!(pathingResult & PATHING_DESTINATION_REACHED)) + return; + + destination_x = x; + destination_y = y; + + sprite_direction = (var_37 & 3) * 8; + invalidate_sprite_2((rct_sprite *)this); + + action = 0xFE; + next_action_sprite_type = 2; + + SwitchNextActionSpriteType(); + + sub_state++; + + time_to_stand = Math::Clamp(0, ((129 - energy) * 16 + 50) / 2, 255); + peep_update_sprite_type(this); + } + else if (sub_state == 1) + { + if (action < 0xFE) + { + // 6917F6 + sint16 actionX = 0; + sint16 actionY = 0; + sint16 xy_distance; + UpdateAction(&actionX, &actionY, &xy_distance); + + if (action != 0xFF) + return; + action = 0xFE; + } + else + { + if (HasFood()) + { + if ((scenario_rand() & 0xFFFF) <= 1310) + { + action = PEEP_ACTION_EAT_FOOD; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + return; + } + } + + if ((scenario_rand() & 0xFFFF) <= 655) + { + action = PEEP_ACTION_TAKE_PHOTO; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + return; + } + + if ((standing_flags & 1)) + { + if ((scenario_rand() & 0xFFFF) <= 655) + { + action = PEEP_ACTION_WAVE; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)this); + return; + } + } + } + + standing_flags ^= (1 << 7); + if (!(standing_flags & (1 << 7))) + return; + + time_to_stand--; + if (time_to_stand != 0) + return; + + SetState(PEEP_STATE_WALKING); + peep_update_sprite_type(this); + // Send peep to the centre of current tile. + destination_x = (x & 0xFFE0) + 16; + destination_y = (y & 0xFFE0) + 16; + destination_tolerance = 5; + UpdateCurrentActionSpriteType(); + } +} + +/** + * + * rct2: 0x00691089 + */ +void rct_peep::UpdateUsingBin() +{ + if (sub_state == 0) + { + if (!CheckForPath()) + return; + + uint8 pathingResult; + PerformNextAction(pathingResult); + if (!(pathingResult & PATHING_DESTINATION_REACHED)) + return; + + sub_state = 1; + } + else if (sub_state == 1) + { + + if (action != PEEP_ACTION_NONE_2) + { + sint16 actionX, actionY, xy_distance; + UpdateAction(&actionX, &actionY, &xy_distance); + return; + } + + rct_tile_element * tile_element = map_get_first_element_at(next_x / 32, next_y / 32); + + for (;; tile_element++) + { + if (tile_element->GetType() != TILE_ELEMENT_TYPE_PATH) + { + continue; + } + + if (tile_element->base_height == next_z) + break; + + if (tile_element_is_last_for_tile(tile_element)) + { + StateReset(); + return; + } + } + + if (!footpath_element_has_path_scenery(tile_element)) + { + StateReset(); + return; + } + + rct_scenery_entry * sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(tile_element)); + if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)) + { + StateReset(); + return; + } + + if (tile_element->flags & TILE_ELEMENT_FLAG_BROKEN) + { + StateReset(); + return; + } + + if (footpath_element_path_scenery_is_ghost(tile_element)) + { + StateReset(); + return; + } + + // Bin selection is one of 4 corners + uint8 selected_bin = var_37 * 2; + + // This counts down 2 = No rubbish, 0 = full + uint8 space_left_in_bin = 0x3 & (tile_element->properties.path.addition_status >> selected_bin); + uint32 empty_containers = peep_empty_container_standard_flag(this); + + 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--; + item_standard_flags &= ~(1 << cur_container); + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; + peep_update_sprite_type(this); + continue; + } + uint8 bp = item_standard_litter[cur_container]; + + sint32 litterX = x + (scenario_rand() & 7) - 3; + sint32 litterY = y + (scenario_rand() & 7) - 3; + + litter_create(litterX, litterY, z, scenario_rand() & 3, bp); + item_standard_flags &= ~(1 << cur_container); + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; + + peep_update_sprite_type(this); + } + + // Original bug: This would clear any rubbish placed by the previous function + // space_left_in_bin = 0x3 & (tile_element->properties.path.addition_status >> selected_bin); + empty_containers = peep_empty_container_extra_flag(this); + + 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--; + item_extra_flags &= ~(1 << cur_container); + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; + + peep_update_sprite_type(this); + continue; + } + uint8 bp = item_extra_litter[cur_container]; + + sint32 litterX = x + (scenario_rand() & 7) - 3; + sint32 litterY = y + (scenario_rand() & 7) - 3; + + litter_create(litterX, litterY, z, scenario_rand() & 3, bp); + item_extra_flags &= ~(1 << cur_container); + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; + + peep_update_sprite_type(this); + } + + // Place new amount in bin by first clearing the value + tile_element->properties.path.addition_status &= ~(3 << selected_bin); + // Then placing the new value. + tile_element->properties.path.addition_status |= space_left_in_bin << selected_bin; + + map_invalidate_tile_zoom0(next_x, next_y, tile_element->base_height << 3, + tile_element->clearance_height << 3); + StateReset(); + } +} diff --git a/src/openrct2/peep/Peep.cpp b/src/openrct2/peep/Peep.cpp index ec22e33b8c..49c34cf346 100644 --- a/src/openrct2/peep/Peep.cpp +++ b/src/openrct2/peep/Peep.cpp @@ -94,7 +94,6 @@ static struct } _peepPathFindHistory[16]; static uint8 _unk_F1AEF0; -static uint16 _unk_F1EE18; static rct_tile_element * _peepRideEntranceExitElement; enum @@ -2301,930 +2300,6 @@ void rct_peep::UpdateFalling() SetState(PEEP_STATE_1); } - -enum { - PEEP_FIXING_ENTER_STATION = 1 << 0, - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE = 1 << 1, - PEEP_FIXING_FIX_VEHICLE_CLOSED_RESTRAINTS = 1 << 2, - PEEP_FIXING_FIX_VEHICLE_CLOSED_DOORS = 1 << 3, - PEEP_FIXING_FIX_VEHICLE_OPEN_RESTRAINTS = 1 << 4, - PEEP_FIXING_FIX_VEHICLE_OPEN_DOORS = 1 << 5, - PEEP_FIXING_FIX_VEHICLE_MALFUNCTION = 1 << 6, - PEEP_FIXING_MOVE_TO_STATION_END = 1 << 7, - PEEP_FIXING_FIX_STATION_END = 1 << 8, - PEEP_FIXING_MOVE_TO_STATION_START = 1 << 9, - PEEP_FIXING_FIX_STATION_START = 1 << 10, - PEEP_FIXING_FIX_STATION_BRAKES = 1 << 11, - PEEP_FIXING_MOVE_TO_STATION_EXIT = 1 << 12, - PEEP_FIXING_FINISH_FIX_OR_INSPECT = 1 << 13, - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT = 1 << 14, -}; - -/** - * peep_fixing_sub_state_mask[] defines the applicable peep sub_states for - * mechanics fixing a ride. The array is indexed by breakdown_reason: - * - indexes 0-7 are the 8 breakdown reasons (see BREAKDOWN_* in Ride.h) - * when fixing a broken down ride; - * - index 8 is for inspecting a ride. - */ -static constexpr const uint32 peep_fixing_sub_state_mask[9] = { - ( // BREAKDOWN_SAFETY_CUT_OUT - PEEP_FIXING_MOVE_TO_STATION_END | - PEEP_FIXING_FIX_STATION_END | - PEEP_FIXING_MOVE_TO_STATION_START | - PEEP_FIXING_FIX_STATION_START | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_RESTRAINTS_STUCK_CLOSED - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | - PEEP_FIXING_FIX_VEHICLE_CLOSED_RESTRAINTS | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_RESTRAINTS_STUCK_OPEN - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | - PEEP_FIXING_FIX_VEHICLE_OPEN_RESTRAINTS | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_DOORS_STUCK_CLOSED - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | - PEEP_FIXING_FIX_VEHICLE_CLOSED_DOORS | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_DOORS_STUCK_OPEN - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | - PEEP_FIXING_FIX_VEHICLE_OPEN_DOORS | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_VEHICLE_MALFUNCTION - PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | - PEEP_FIXING_FIX_VEHICLE_MALFUNCTION | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_BRAKES_FAILURE - PEEP_FIXING_MOVE_TO_STATION_START | - PEEP_FIXING_FIX_STATION_BRAKES | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // BREAKDOWN_CONTROL_FAILURE - PEEP_FIXING_MOVE_TO_STATION_END | - PEEP_FIXING_FIX_STATION_END | - PEEP_FIXING_MOVE_TO_STATION_START | - PEEP_FIXING_FIX_STATION_START | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ), - ( // INSPECTION - PEEP_FIXING_MOVE_TO_STATION_END | - PEEP_FIXING_FIX_STATION_END | - PEEP_FIXING_MOVE_TO_STATION_START | - PEEP_FIXING_FIX_STATION_START | - PEEP_FIXING_MOVE_TO_STATION_EXIT | - PEEP_FIXING_FINISH_FIX_OR_INSPECT | - PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT - ) -}; - -/** - * - * rct2: 0x006C0E8B - * Also used by inspecting. - */ -void rct_peep::UpdateFixing(sint32 steps) -{ - Ride * ride = get_ride(current_ride); - - if (ride->type == RIDE_TYPE_NULL) - { - SetState(PEEP_STATE_FALLING); - return; - } - - bool progressToNextSubstate = true; - bool firstRun = true; - - if ((state == PEEP_STATE_INSPECTING) && - (ride->lifecycle_flags & ( RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))) - { - // Ride has broken down since Mechanic was called to inspect it. - // Mechanic identifies the breakdown and switches to fixing it. - state = PEEP_STATE_FIXING; - } - - while (progressToNextSubstate) - { - switch (sub_state) - { - case 0: - progressToNextSubstate = peep_update_fixing_enter_station(ride); - break; - - case 1: - progressToNextSubstate = peep_update_fixing_move_to_broken_down_vehicle(firstRun, this, ride); - break; - - case 2: - case 3: - case 4: - case 5: - progressToNextSubstate = peep_update_fixing_fix_vehicle(firstRun, this, ride); - break; - - case 6: - progressToNextSubstate = peep_update_fixing_fix_vehicle_malfunction(firstRun, this, ride); - break; - - case 7: - progressToNextSubstate = peep_update_fixing_move_to_station_end(firstRun, this, ride); - break; - - case 8: - progressToNextSubstate = peep_update_fixing_fix_station_end(firstRun, this); - break; - - case 9: - progressToNextSubstate = peep_update_fixing_move_to_station_start(firstRun, this, ride); - break; - - case 10: - progressToNextSubstate = peep_update_fixing_fix_station_start(firstRun, this, ride); - break; - - case 11: - progressToNextSubstate = peep_update_fixing_fix_station_brakes(firstRun, this, ride); - break; - - case 12: - progressToNextSubstate = peep_update_fixing_move_to_station_exit(firstRun, this, ride); - break; - - case 13: - progressToNextSubstate = peep_update_fixing_finish_fix_or_inspect(firstRun, steps, this, ride); - break; - - case 14: - progressToNextSubstate = peep_update_fixing_leave_by_entrance_exit(firstRun, this, ride); - break; - - default: - log_error("Invalid substate"); - progressToNextSubstate = false; - } - - firstRun = false; - - if (!progressToNextSubstate) - { - break; - } - - sint32 subState = sub_state; - uint32 sub_state_sequence_mask = peep_fixing_sub_state_mask[8]; - - if (state != PEEP_STATE_INSPECTING) - { - sub_state_sequence_mask = peep_fixing_sub_state_mask[ride->breakdown_reason_pending]; - } - - do - { - subState++; - } while ((sub_state_sequence_mask & (1 << subState)) == 0); - - sub_state = subState & 0xFF; - } -} - -/** - * rct2: 0x006C0EEC - * fixing sub_state: enter_station - applies to fixing all break down reasons and ride inspections. - */ -static bool peep_update_fixing_enter_station(Ride * ride) -{ - ride->mechanic_status = RIDE_MECHANIC_STATUS_FIXING; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE; - - return true; -} - -/** - * rct2: 0x006C0F09 - * fixing sub_state: move_to_broken_down_vehicle - applies to fixing all vehicle specific breakdown reasons - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_move_to_broken_down_vehicle(bool firstRun, rct_peep * peep, Ride * ride) -{ - sint16 x, y, tmp_xy_distance; - - if (!firstRun) - { - rct_vehicle * vehicle = ride_get_broken_vehicle(ride); - if (vehicle == nullptr) - { - 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); - } - - LocationXY16 offset = word_981D6C[peep->direction]; - peep->destination_x = (offset.x * -12) + vehicle->x; - peep->destination_y = (offset.y * -12) + vehicle->y; - peep->destination_tolerance = 2; - } - - invalidate_sprite_2((rct_sprite *)peep); - if (peep->UpdateAction(&x, &y, &tmp_xy_distance)) - { - sprite_move(x, y, peep->z, (rct_sprite *)peep); - invalidate_sprite_2((rct_sprite *)peep); - return false; - } - - return true; -} - -/** - * rct2: 0x006C0FD3 - * fixing sub_state: fix_vehicle - applies to fixing vehicle with: - * 1. restraints stuck closed, - * 2. doors stuck closed, - * 3. restrains stuck open, - * 4. doors stuck open. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_fix_vehicle(bool firstRun, rct_peep * peep, Ride * ride) -{ - 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; - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action == PEEP_ACTION_NONE_2) - { - return true; - } - - peep->UpdateAction(); - - 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 == nullptr) - { - return true; - } - - vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_CAR; - - return false; -} - -/** - * rct2: 0x006C107B - * fixing sub_state: fix_vehicle_malfunction - applies fixing to vehicle malfunction. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_fix_vehicle_malfunction(bool firstRun, rct_peep * peep, Ride * ride) -{ - 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; - - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action == PEEP_ACTION_NONE_2) - { - return true; - } - - peep->UpdateAction(); - if (peep->action_frame != 0x65) - { - return false; - } - - rct_vehicle * vehicle = ride_get_broken_vehicle(ride); - if (vehicle == nullptr) - { - return true; - } - - vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_TRAIN; - - return false; -} - -/** rct2: 0x00992A3C */ -static constexpr const CoordsXY _992A3C[] = { - { -12, 0 }, - { 0, 12 }, - { 12, 0 }, - { 0, -12 }, -}; - -/** - * rct2: 0x006C1114 - * fixing sub_state: move_to_station_end - applies to fixing station specific breakdowns: safety cut-out, control failure, inspection. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_move_to_station_end(bool firstRun, rct_peep * peep, 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; - } - - LocationXY8 stationPosition = ride->station_starts[peep->current_ride_station]; - if (stationPosition.xy == RCT_XY8_UNDEFINED) - { - return true; - } - - uint8 stationZ = ride->station_heights[peep->current_ride_station]; - uint16 stationX = stationPosition.x * 32; - uint16 stationY = stationPosition.y * 32; - - rct_tile_element * tileElement = map_get_track_element_at(stationX, stationY, stationZ); - if (tileElement == nullptr) - { - log_error("Couldn't find tile_element"); - return false; - } - - sint32 direction = tile_element_get_direction(tileElement); - CoordsXY 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_tolerance = 2; - } - - invalidate_sprite_2((rct_sprite *)peep); - if (!peep->UpdateAction(&x, &y, &tmp_distance)) - { - return true; - } - - sprite_move(x, y, peep->z, (rct_sprite *)peep); - invalidate_sprite_2((rct_sprite *)peep); - - return false; -} - -/** - * rct2: 0x006C11F5 - * fixing sub_state: fix_station_end - applies to fixing station specific breakdowns: safety cut-out, control failure, inspection. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_fix_station_end(bool firstRun, rct_peep * peep) -{ - if (!firstRun) - { - peep->sprite_direction = peep->direction << 3; - peep->action = PEEP_ACTION_STAFF_CHECKBOARD; - peep->action_frame = 0; - peep->action_sprite_image_offset = 0; - - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action == PEEP_ACTION_NONE_2) - { - return true; - } - - peep->UpdateAction(); - - return false; -} - -/** - * rct2: 0x006C1239 - * fixing sub_state: move_to_station_start - * 1. applies to fixing station specific breakdowns: safety cut-out, control failure, - * 2. applies to fixing brake failure, - * 3. applies to inspection. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_move_to_station_start(bool firstRun, rct_peep * peep, 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; - } - - LocationXY8 stationPosition = ride->station_starts[peep->current_ride_station]; - if (stationPosition.xy == RCT_XY8_UNDEFINED) - { - return true; - } - - uint8 stationZ = ride->station_heights[peep->current_ride_station]; - - CoordsXYE input; - input.x = stationPosition.x * 32; - input.y = stationPosition.y * 32; - input.element = map_get_track_element_at_from_ride(input.x, input.y, stationZ, peep->current_ride); - if (input.element == nullptr) - { - return true; - } - - uint8 direction = 0; - track_begin_end trackBeginEnd; - while (track_block_get_previous(input.x, input.y, input.element, &trackBeginEnd)) - { - if (track_element_is_station(trackBeginEnd.begin_element)) - { - input.x = trackBeginEnd.begin_x; - input.y = trackBeginEnd.begin_y; - input.element = trackBeginEnd.begin_element; - - direction = tile_element_get_direction(trackBeginEnd.begin_element); - continue; - } - - break; - } - - // loc_6C12ED: - uint16 destinationX = input.x + 16; - uint16 destinationY = input.y + 16; - - CoordsXY 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_tolerance = 2; - } - - invalidate_sprite_2((rct_sprite *)peep); - - if (!peep->UpdateAction(&x, &y, &tmp_xy_distance)) - { - return true; - } - - sprite_move(x, y, peep->z, (rct_sprite *)peep); - invalidate_sprite_2((rct_sprite *)peep); - - return false; -} - -/** - * rct2: 0x006C1368 - * fixing sub_state: fix_station_start - * 1. applies to fixing station specific breakdowns: safety cut-out, control failure, - * 2. applies to inspection. - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_fix_station_start(bool firstRun, rct_peep * peep, Ride * ride) -{ - 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; - - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action == PEEP_ACTION_NONE_2) - { - return true; - } - - peep->UpdateAction(); - - return false; -} - -/** - * rct2: 0x006C13CE - * fixing sub_state: fix_station_brakes - applies to fixing brake failure - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_fix_station_brakes(bool firstRun, rct_peep * peep, Ride * ride) -{ - 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; - - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action == PEEP_ACTION_NONE_2) - { - return true; - } - - peep->UpdateAction(); - if (peep->action_frame == 0x28) - { - ride->mechanic_status = RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES; - 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 - * fixing sub_state: move_to_station_exit - applies to fixing all failures & inspections - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_move_to_station_exit(bool firstRun, rct_peep * peep, Ride * ride) -{ - sint16 x, y, tmp_xy_distance; - - if (!firstRun) - { - TileCoordsXYZD stationPosition = ride_get_exit_location(ride, peep->current_ride_station); - if (stationPosition.isNull()) - { - stationPosition = ride_get_entrance_location(ride, peep->current_ride_station); - - if (stationPosition.isNull()) - { - return true; - } - } - - uint16 stationX = stationPosition.x * 32; - uint16 stationY = stationPosition.y * 32; - - stationX += 16; - stationY += 16; - - LocationXY16 direction = word_981D6C[peep->direction]; - - stationX += direction.x * 20; - stationY += direction.y * 20; - - peep->destination_x = stationX; - peep->destination_y = stationY; - peep->destination_tolerance = 2; - } - - invalidate_sprite_2((rct_sprite *)peep); - if (!peep->UpdateAction(&x, &y, &tmp_xy_distance)) - { - return true; - } - else - { - sprite_move(x, y, peep->z, (rct_sprite *)peep); - invalidate_sprite_2((rct_sprite *)peep); - } - - return false; -} - -/** - * rct2: 0x006C1504 - * fixing sub_state: finish_fix_or_inspect - applies to fixing all failures & inspections - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_finish_fix_or_inspect(bool firstRun, sint32 steps, rct_peep * peep, Ride * ride) -{ - if (!firstRun) - { - ride->mechanic_status = RIDE_MECHANIC_STATUS_UNDEFINED; - - if (peep->state == PEEP_STATE_INSPECTING) - { - peep_update_ride_inspected(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; - - peep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)peep); - } - - if (peep->action != 0xFF) - { - peep->UpdateAction(); - return false; - } - - ride_fix_breakdown(peep->current_ride, steps); - - return true; -} - -/** - * rct2: 0x006C157E - * fixing sub_state: leave_by_entrance_exit - applies to fixing all failures & inspections - * - see peep_fixing_sub_state_mask[] - */ -static bool peep_update_fixing_leave_by_entrance_exit(bool firstRun, rct_peep * peep, Ride * ride) -{ - sint16 x, y, xy_distance; - - if (!firstRun) - { - TileCoordsXYZD exitPosition = ride_get_exit_location(ride, peep->current_ride_station); - if (exitPosition.isNull()) - { - exitPosition = ride_get_entrance_location(ride, peep->current_ride_station); - - if (exitPosition.isNull()) - { - peep->SetState(PEEP_STATE_FALLING); - return false; - } - } - - uint16 exitX = exitPosition.x * 32; - uint16 exitY = exitPosition.y * 32; - - exitX += 16; - exitY += 16; - - LocationXY16 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_tolerance = 2; - } - - invalidate_sprite_2((rct_sprite *)peep); - if (!peep->UpdateAction(&x, &y, &xy_distance)) - { - peep->SetState(PEEP_STATE_FALLING); - 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 peep_update_ride_inspected(sint32 rideIndex) -{ - Ride * ride = get_ride(rideIndex); - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; - - ride->reliability += ((100 - ride->reliability_percentage) / 4) * (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 - */ -void rct_peep::UpdateQueuing() -{ - if (!CheckForPath()) - { - RemoveFromQueue(); - return; - } - Ride * ride = get_ride(current_ride); - if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING) - { - RemoveFromQueue(); - SetState(PEEP_STATE_1); - return; - } - - if (sub_state != 10) - { - bool is_front = true; - if (next_in_queue != SPRITE_INDEX_NULL) - { - // Fix #4819: Occasionally the peep->next_in_queue is incorrectly set - // to prevent this from causing the peeps to enter a loop - // first check if the next in queue is actually nearby - // if they are not then it's safe to assume that this is - // the front of the queue. - rct_peep * next_peep = GET_PEEP(next_in_queue); - if (abs(next_peep->x - x) < 32 && abs(next_peep->y - y) < 32) - { - is_front = false; - } - } - - if (is_front) - { - // Happens every time peep goes onto ride. - destination_tolerance = 0; - SetState(PEEP_STATE_QUEUING_FRONT); - sub_state = PEEP_RIDE_AT_ENTRANCE; - return; - } - - // Give up queueing for the ride - sprite_direction ^= (1 << 4); - invalidate_sprite_2((rct_sprite *)this); - RemoveFromQueue(); - SetState(PEEP_STATE_1); - return; - } - - uint8 pathingResult; - PerformNextAction(pathingResult); - if (action < 0xFE) - return; - if (sprite_type == PEEP_SPRITE_TYPE_NORMAL) - { - if (time_in_queue >= 2000 && (0xFFFF & scenario_rand()) <= 119) - { - // Eat Food/Look at watch - action = PEEP_ACTION_EAT_FOOD; - action_frame = 0; - action_sprite_image_offset = 0; - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - } - if (time_in_queue >= 3500 && (0xFFFF & scenario_rand()) <= 93) - { - // Create the I have been waiting in line ages thought - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_QUEUING_AGES, current_ride); - } - } - else - { - if (!(time_in_queue & 0x3F) && action == 0xFE && next_action_sprite_type == 2) - { - switch (sprite_type) - { - case PEEP_SPRITE_TYPE_ICE_CREAM: - case PEEP_SPRITE_TYPE_CHIPS: - case PEEP_SPRITE_TYPE_BURGER: - case PEEP_SPRITE_TYPE_DRINK: - case PEEP_SPRITE_TYPE_CANDYFLOSS: - case PEEP_SPRITE_TYPE_PIZZA: - case PEEP_SPRITE_TYPE_POPCORN: - case PEEP_SPRITE_TYPE_HOT_DOG: - case PEEP_SPRITE_TYPE_TENTACLE: - case PEEP_SPRITE_TYPE_TOFFEE_APPLE: - case PEEP_SPRITE_TYPE_DOUGHNUT: - case PEEP_SPRITE_TYPE_COFFEE: - case PEEP_SPRITE_TYPE_CHICKEN: - case PEEP_SPRITE_TYPE_LEMONADE: - case PEEP_SPRITE_TYPE_PRETZEL: - case PEEP_SPRITE_TYPE_SU_JONGKWA: - case PEEP_SPRITE_TYPE_JUICE: - case PEEP_SPRITE_TYPE_FUNNEL_CAKE: - case PEEP_SPRITE_TYPE_NOODLES: - case PEEP_SPRITE_TYPE_SAUSAGE: - case PEEP_SPRITE_TYPE_SOUP: - case PEEP_SPRITE_TYPE_SANDWICH: - // Eat food - action = PEEP_ACTION_EAT_FOOD; - action_frame = 0; - action_sprite_image_offset = 0; - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - break; - } - } - } - if (time_in_queue < 4300) - return; - - if (happiness <= 65 && (0xFFFF & scenario_rand()) < 2184) - { - // Give up queueing for the ride - sprite_direction ^= (1 << 4); - invalidate_sprite_2((rct_sprite *)this); - RemoveFromQueue(); - SetState(PEEP_STATE_1); - } -} - /** * * rct2: 0x6902A2 @@ -3271,186 +2346,6 @@ void rct_peep::UpdatePicked() } } -/** - * - * rct2: 0x6914CD - */ -void rct_peep::UpdateLeavingPark() -{ - if (var_37 != 0) - { - uint8 pathingResult; - PerformNextAction(pathingResult); - if (!(pathingResult & PATHING_OUTSIDE_PARK)) - return; - peep_sprite_remove(this); - return; - } - - sint16 actionX = 0; - sint16 actionY = 0; - sint16 xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) - { - invalidate_sprite_2((rct_sprite *)this); - sprite_move(actionX, actionY, z, (rct_sprite *)this); - invalidate_sprite_2((rct_sprite *)this); - return; - } - - outside_of_park = 1; - destination_tolerance = 5; - decrement_guests_in_park(); - auto intent = Intent(INTENT_ACTION_UPDATE_GUEST_COUNT); - context_broadcast_intent(&intent); - var_37 = 1; - - window_invalidate_by_class(WC_GUEST_LIST); - uint8 pathingResult; - PerformNextAction(pathingResult); - if (!(pathingResult & PATHING_OUTSIDE_PARK)) - return; - peep_sprite_remove(this); -} - -/** - * - * rct2: 0x6916D6 - */ -void rct_peep::UpdateWatching() -{ - if (sub_state == 0) - { - if (!CheckForPath()) - return; - uint8 pathingResult; - PerformNextAction(pathingResult); - if (!(pathingResult & PATHING_DESTINATION_REACHED)) - return; - - destination_x = x; - destination_y = y; - - sprite_direction = (var_37 & 3) * 8; - invalidate_sprite_2((rct_sprite *)this); - - action = 0xFE; - next_action_sprite_type = 2; - - SwitchNextActionSpriteType(); - - sub_state++; - - time_to_stand = Math::Clamp(0, ((129 - energy) * 16 + 50) / 2, 255); - peep_update_sprite_type(this); - } - else if (sub_state == 1) - { - if (action < 0xFE) - { - // 6917F6 - sint16 actionX = 0; - sint16 actionY = 0; - sint16 xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); - - if (action != 0xFF) - return; - action = 0xFE; - } - else - { - if (HasFood()) - { - if ((scenario_rand() & 0xFFFF) <= 1310) - { - action = PEEP_ACTION_EAT_FOOD; - action_frame = 0; - action_sprite_image_offset = 0; - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - return; - } - } - - if ((scenario_rand() & 0xFFFF) <= 655) - { - action = PEEP_ACTION_TAKE_PHOTO; - action_frame = 0; - action_sprite_image_offset = 0; - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - return; - } - - if ((standing_flags & 1)) - { - if ((scenario_rand() & 0xFFFF) <= 655) - { - action = PEEP_ACTION_WAVE; - action_frame = 0; - action_sprite_image_offset = 0; - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - return; - } - } - } - - standing_flags ^= (1 << 7); - if (!(standing_flags & (1 << 7))) - return; - - time_to_stand--; - if (time_to_stand != 0) - return; - - SetState(PEEP_STATE_WALKING); - peep_update_sprite_type(this); - // Send peep to the centre of current tile. - destination_x = (x & 0xFFE0) + 16; - destination_y = (y & 0xFFE0) + 16; - destination_tolerance = 5; - UpdateCurrentActionSpriteType(); - } -} - -/** - * rct2: 0x691451 - */ -void rct_peep::UpdateEnteringPark() -{ - if (var_37 != 1) - { - uint8 pathingResult; - PerformNextAction(pathingResult); - if ((pathingResult & PATHING_OUTSIDE_PARK)) - { - decrement_guests_heading_for_park(); - peep_sprite_remove(this); - } - return; - } - sint16 actionX = 0; - sint16 actionY = 0; - sint16 xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) - { - invalidate_sprite_2((rct_sprite *)this); - sprite_move(actionX, actionY, z, (rct_sprite *)this); - invalidate_sprite_2((rct_sprite *)this); - return; - } - SetState(PEEP_STATE_FALLING); - - outside_of_park = 0; - time_in_park = gScenarioTicks; - increment_guests_in_park(); - decrement_guests_heading_for_park(); - auto intent = Intent(INTENT_ACTION_UPDATE_GUEST_COUNT); - context_broadcast_intent(&intent); -} - /** * * rct2: 0x00690582 @@ -3788,159 +2683,6 @@ static constexpr const uint8 item_extra_litter[32] = { LITTER_TYPE_EMPTY_BOWL_BLUE, // PEEP_ITEM_EMPTY_BOWL_BLUE }; -/** - * - * rct2: 0x00691089 - */ -void rct_peep::UpdateUsingBin() -{ - if (sub_state == 0) - { - if (!CheckForPath()) - return; - - uint8 pathingResult; - PerformNextAction(pathingResult); - if (!(pathingResult & PATHING_DESTINATION_REACHED)) - return; - - sub_state = 1; - } - else if (sub_state == 1) - { - - if (action != PEEP_ACTION_NONE_2) - { - sint16 actionX, actionY, xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); - return; - } - - rct_tile_element * tile_element = map_get_first_element_at(next_x / 32, next_y / 32); - - for (;; tile_element++) - { - if (tile_element->GetType() != TILE_ELEMENT_TYPE_PATH) - { - continue; - } - - if (tile_element->base_height == next_z) - break; - - if (tile_element_is_last_for_tile(tile_element)) - { - StateReset(); - return; - } - } - - if (!footpath_element_has_path_scenery(tile_element)) - { - StateReset(); - return; - } - - rct_scenery_entry * sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(tile_element)); - if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)) - { - StateReset(); - return; - } - - if (tile_element->flags & TILE_ELEMENT_FLAG_BROKEN) - { - StateReset(); - return; - } - - if (footpath_element_path_scenery_is_ghost(tile_element)) - { - StateReset(); - return; - } - - // Bin selection is one of 4 corners - uint8 selected_bin = var_37 * 2; - - // This counts down 2 = No rubbish, 0 = full - uint8 space_left_in_bin = 0x3 & (tile_element->properties.path.addition_status >> selected_bin); - uint32 empty_containers = peep_empty_container_standard_flag(this); - - 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--; - item_standard_flags &= ~(1 << cur_container); - window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; - peep_update_sprite_type(this); - continue; - } - uint8 bp = item_standard_litter[cur_container]; - - sint32 litterX = x + (scenario_rand() & 7) - 3; - sint32 litterY = y + (scenario_rand() & 7) - 3; - - litter_create(litterX, litterY, z, scenario_rand() & 3, bp); - item_standard_flags &= ~(1 << cur_container); - window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; - - peep_update_sprite_type(this); - } - - // Original bug: This would clear any rubbish placed by the previous function - // space_left_in_bin = 0x3 & (tile_element->properties.path.addition_status >> selected_bin); - empty_containers = peep_empty_container_extra_flag(this); - - 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--; - item_extra_flags &= ~(1 << cur_container); - window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; - - peep_update_sprite_type(this); - continue; - } - uint8 bp = item_extra_litter[cur_container]; - - sint32 litterX = x + (scenario_rand() & 7) - 3; - sint32 litterY = y + (scenario_rand() & 7) - 3; - - litter_create(litterX, litterY, z, scenario_rand() & 3, bp); - item_extra_flags &= ~(1 << cur_container); - window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; - - peep_update_sprite_type(this); - } - - // Place new amount in bin by first clearing the value - tile_element->properties.path.addition_status &= ~(3 << selected_bin); - // Then placing the new value. - tile_element->properties.path.addition_status |= space_left_in_bin << selected_bin; - - map_invalidate_tile_zoom0(next_x, next_y, tile_element->base_height << 3, - tile_element->clearance_height << 3); - StateReset(); - } -} - // clang-format off /** rct2: 0x00981F4C, 0x00981F4E */ static constexpr const LocationXY16 _981F4C[] = { @@ -3979,285 +2721,6 @@ static constexpr const LocationXY16 _981F4C[] = { }; // clang-format on -/** - * - * rct2: 0x0069030A - */ -void rct_peep::UpdateWalking() -{ - if (!CheckForPath()) - return; - - if (peep_flags & PEEP_FLAGS_WAVING) - { - if (action >= PEEP_ACTION_NONE_1) - { - if ((0xFFFF & scenario_rand()) < 936) - { - invalidate_sprite_2((rct_sprite *)this); - - action = PEEP_ACTION_WAVE_2; - action_frame = 0; - action_sprite_image_offset = 0; - - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - } - } - } - - if (peep_flags & PEEP_FLAGS_PHOTO) - { - if (action >= PEEP_ACTION_NONE_1) - { - if ((0xFFFF & scenario_rand()) < 936) - { - invalidate_sprite_2((rct_sprite *)this); - - action = PEEP_ACTION_TAKE_PHOTO; - action_frame = 0; - action_sprite_image_offset = 0; - - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - } - } - } - - if (peep_flags & PEEP_FLAGS_PAINTING) - { - if (action >= PEEP_ACTION_NONE_1) - { - if ((0xFFFF & scenario_rand()) < 936) - { - invalidate_sprite_2((rct_sprite *)this); - - action = PEEP_ACTION_DRAW_PICTURE; - action_frame = 0; - action_sprite_image_offset = 0; - - UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite *)this); - } - } - } - - if (peep_flags & PEEP_FLAGS_LITTER) - { - if (!(next_var_29 & 0x18)) - { - if ((0xFFFF & scenario_rand()) <= 4096) - { - static constexpr const uint8 litter_types[] = { - LITTER_TYPE_EMPTY_CAN, - LITTER_TYPE_RUBBISH, - LITTER_TYPE_EMPTY_BURGER_BOX, - LITTER_TYPE_EMPTY_CUP, - }; - sint32 ebp = litter_types[scenario_rand() & 0x3]; - sint32 litterX = x + (scenario_rand() & 0x7) - 3; - sint32 litterY = y + (scenario_rand() & 0x7) - 3; - sint32 litterDirection = (scenario_rand() & 0x3); - - litter_create(litterX, litterY, z, litterDirection, ebp); - } - } - } - else if (peep_has_empty_container(this)) - { - if ((!(next_var_29 & 0x18)) && ((uint32)(sprite_index & 0x1FF) == (gCurrentTicks & 0x1FF)) && - ((0xFFFF & scenario_rand()) <= 4096)) - { - - uint8 pos_stnd = 0; - for (sint32 container = peep_empty_container_standard_flag(this); pos_stnd < 32; pos_stnd++) - if (container & (1u << pos_stnd)) - break; - - sint32 bp = 0; - - if (pos_stnd != 32) - { - item_standard_flags &= ~(1u << pos_stnd); - bp = item_standard_litter[pos_stnd]; - } - else - { - uint8 pos_extr = 0; - for (sint32 container = peep_empty_container_extra_flag(this); pos_extr < 32; pos_extr++) - if (container & (1u << pos_extr)) - break; - item_extra_flags &= ~(1u << pos_extr); - bp = item_extra_litter[pos_extr]; - } - - window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; - peep_update_sprite_type(this); - - sint32 litterX = x + (scenario_rand() & 0x7) - 3; - sint32 litterY = y + (scenario_rand() & 0x7) - 3; - sint32 litterDirection = (scenario_rand() & 0x3); - - litter_create(litterX, litterY, z, litterDirection, bp); - } - } - - uint8 pathingResult; - PerformNextAction(pathingResult); - if (!(pathingResult & PATHING_DESTINATION_REACHED)) - return; - - if ((next_var_29 & 0x18) == 8) - { - rct_tile_element * tile_element = map_get_surface_element_at({next_x, next_y}); - - sint32 water_height = surface_get_water_height(tile_element); - if (water_height) - { - invalidate_sprite_2((rct_sprite *)this); - water_height *= 16; - sprite_move(x, y, water_height, (rct_sprite *)this); - invalidate_sprite_2((rct_sprite *)this); - - SetState(PEEP_STATE_FALLING); - return; - } - } - - peep_check_if_lost(this); - peep_check_cant_find_ride(this); - peep_check_cant_find_exit(this); - - if (peep_update_walking_find_bench(this)) - return; - - if (peep_update_walking_find_bin(this)) - return; - - peep_update_walking_break_scenery(this); - - if (state != PEEP_STATE_WALKING) - return; - - if (peep_flags & PEEP_FLAGS_LEAVING_PARK) - return; - - if (nausea > 140) - return; - - if (happiness < 120) - return; - - if (toilet > 140) - return; - - uint16 chance = HasFoodExtraFlag() ? 13107 : 2849; - - if ((scenario_rand() & 0xFFFF) > chance) - return; - - if (next_var_29 & 0x1C) - return; - - rct_tile_element * tile_element = map_get_first_element_at(next_x / 32, next_y / 32); - - for (;; tile_element++) - { - if (tile_element->GetType() == TILE_ELEMENT_TYPE_PATH) - { - if (next_z == tile_element->base_height) - break; - } - if (tile_element_is_last_for_tile(tile_element)) - { - return; - } - } - - sint32 positions_free = 15; - - if (footpath_element_has_path_scenery(tile_element)) - { - if (!footpath_element_path_scenery_is_ghost(tile_element)) - { - rct_scenery_entry * sceneryEntry = get_footpath_item_entry(footpath_element_get_path_scenery_index(tile_element)); - if (sceneryEntry == nullptr) - { - return; - } - - if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BENCH)) - positions_free = 9; - } - } - - sint32 edges = (tile_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(this, chosen_edge, &ride_to_view, &ride_seat_to_view)) - return; - - // Check if there is a peep watching (and if there is place for us) - uint16 sprite_id = sprite_get_first_in_quadrant(x, 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 (z != sprite->peep.z) - continue; - - if ((sprite->peep.var_37 & 0x3) != chosen_edge) - continue; - - positions_free &= ~(1 << ((sprite->peep.var_37 & 0x1C) >> 2)); - } - - if (!positions_free) - return; - - uint8 chosen_position = scenario_rand() & 0x3; - - for (; !(positions_free & (1 << chosen_position));) - chosen_position = (chosen_position + 1) & 3; - - current_ride = ride_to_view; - current_seat = ride_seat_to_view; - var_37 = chosen_edge | (chosen_position << 2); - - SetState(PEEP_STATE_WATCHING); - sub_state = 0; - - sint32 ebx = var_37 & 0x1F; - sint32 destX = (x & 0xFFE0) + _981F4C[ebx].x; - sint32 destY = (y & 0xFFE0) + _981F4C[ebx].y; - - destination_x = destX; - destination_y = destY; - destination_tolerance = 3; - - if (current_seat & 1) - { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NEW_RIDE, PEEP_THOUGHT_ITEM_NONE); - } - if (current_ride == 0xFF) - { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SCENERY, PEEP_THOUGHT_ITEM_NONE); - } -} - /* From peep_update */ static void peep_update_thoughts(rct_peep * peep) { diff --git a/src/openrct2/peep/Staff.cpp b/src/openrct2/peep/Staff.cpp index bdff0be1e6..bef085dbb2 100644 --- a/src/openrct2/peep/Staff.cpp +++ b/src/openrct2/peep/Staff.cpp @@ -2357,3 +2357,801 @@ void rct_peep::UpdatePatrolling() peep_update_patrolling_find_watering(this); } + +enum { + PEEP_FIXING_ENTER_STATION = 1 << 0, + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE = 1 << 1, + PEEP_FIXING_FIX_VEHICLE_CLOSED_RESTRAINTS = 1 << 2, + PEEP_FIXING_FIX_VEHICLE_CLOSED_DOORS = 1 << 3, + PEEP_FIXING_FIX_VEHICLE_OPEN_RESTRAINTS = 1 << 4, + PEEP_FIXING_FIX_VEHICLE_OPEN_DOORS = 1 << 5, + PEEP_FIXING_FIX_VEHICLE_MALFUNCTION = 1 << 6, + PEEP_FIXING_MOVE_TO_STATION_END = 1 << 7, + PEEP_FIXING_FIX_STATION_END = 1 << 8, + PEEP_FIXING_MOVE_TO_STATION_START = 1 << 9, + PEEP_FIXING_FIX_STATION_START = 1 << 10, + PEEP_FIXING_FIX_STATION_BRAKES = 1 << 11, + PEEP_FIXING_MOVE_TO_STATION_EXIT = 1 << 12, + PEEP_FIXING_FINISH_FIX_OR_INSPECT = 1 << 13, + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT = 1 << 14, +}; + +/** + * peep_fixing_sub_state_mask[] defines the applicable peep sub_states for + * mechanics fixing a ride. The array is indexed by breakdown_reason: + * - indexes 0-7 are the 8 breakdown reasons (see BREAKDOWN_* in Ride.h) + * when fixing a broken down ride; + * - index 8 is for inspecting a ride. + */ +static constexpr const uint32 peep_fixing_sub_state_mask[9] = { + ( // BREAKDOWN_SAFETY_CUT_OUT + PEEP_FIXING_MOVE_TO_STATION_END | + PEEP_FIXING_FIX_STATION_END | + PEEP_FIXING_MOVE_TO_STATION_START | + PEEP_FIXING_FIX_STATION_START | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_RESTRAINTS_STUCK_CLOSED + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | + PEEP_FIXING_FIX_VEHICLE_CLOSED_RESTRAINTS | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_RESTRAINTS_STUCK_OPEN + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | + PEEP_FIXING_FIX_VEHICLE_OPEN_RESTRAINTS | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_DOORS_STUCK_CLOSED + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | + PEEP_FIXING_FIX_VEHICLE_CLOSED_DOORS | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_DOORS_STUCK_OPEN + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | + PEEP_FIXING_FIX_VEHICLE_OPEN_DOORS | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_VEHICLE_MALFUNCTION + PEEP_FIXING_MOVE_TO_BROKEN_DOWN_VEHICLE | + PEEP_FIXING_FIX_VEHICLE_MALFUNCTION | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_BRAKES_FAILURE + PEEP_FIXING_MOVE_TO_STATION_START | + PEEP_FIXING_FIX_STATION_BRAKES | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // BREAKDOWN_CONTROL_FAILURE + PEEP_FIXING_MOVE_TO_STATION_END | + PEEP_FIXING_FIX_STATION_END | + PEEP_FIXING_MOVE_TO_STATION_START | + PEEP_FIXING_FIX_STATION_START | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ), + ( // INSPECTION + PEEP_FIXING_MOVE_TO_STATION_END | + PEEP_FIXING_FIX_STATION_END | + PEEP_FIXING_MOVE_TO_STATION_START | + PEEP_FIXING_FIX_STATION_START | + PEEP_FIXING_MOVE_TO_STATION_EXIT | + PEEP_FIXING_FINISH_FIX_OR_INSPECT | + PEEP_FIXING_LEAVE_BY_ENTRANCE_EXIT + ) +}; + +/** + * + * rct2: 0x006C0E8B + * Also used by inspecting. + */ +void rct_peep::UpdateFixing(sint32 steps) +{ + Ride * ride = get_ride(current_ride); + + if (ride->type == RIDE_TYPE_NULL) + { + SetState(PEEP_STATE_FALLING); + return; + } + + bool progressToNextSubstate = true; + bool firstRun = true; + + if ((state == PEEP_STATE_INSPECTING) && + (ride->lifecycle_flags & ( RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))) + { + // Ride has broken down since Mechanic was called to inspect it. + // Mechanic identifies the breakdown and switches to fixing it. + state = PEEP_STATE_FIXING; + } + + while (progressToNextSubstate) + { + switch (sub_state) + { + case 0: + progressToNextSubstate = peep_update_fixing_enter_station(ride); + break; + + case 1: + progressToNextSubstate = peep_update_fixing_move_to_broken_down_vehicle(firstRun, this, ride); + break; + + case 2: + case 3: + case 4: + case 5: + progressToNextSubstate = peep_update_fixing_fix_vehicle(firstRun, this, ride); + break; + + case 6: + progressToNextSubstate = peep_update_fixing_fix_vehicle_malfunction(firstRun, this, ride); + break; + + case 7: + progressToNextSubstate = peep_update_fixing_move_to_station_end(firstRun, this, ride); + break; + + case 8: + progressToNextSubstate = peep_update_fixing_fix_station_end(firstRun, this); + break; + + case 9: + progressToNextSubstate = peep_update_fixing_move_to_station_start(firstRun, this, ride); + break; + + case 10: + progressToNextSubstate = peep_update_fixing_fix_station_start(firstRun, this, ride); + break; + + case 11: + progressToNextSubstate = peep_update_fixing_fix_station_brakes(firstRun, this, ride); + break; + + case 12: + progressToNextSubstate = peep_update_fixing_move_to_station_exit(firstRun, this, ride); + break; + + case 13: + progressToNextSubstate = peep_update_fixing_finish_fix_or_inspect(firstRun, steps, this, ride); + break; + + case 14: + progressToNextSubstate = peep_update_fixing_leave_by_entrance_exit(firstRun, this, ride); + break; + + default: + log_error("Invalid substate"); + progressToNextSubstate = false; + } + + firstRun = false; + + if (!progressToNextSubstate) + { + break; + } + + sint32 subState = sub_state; + uint32 sub_state_sequence_mask = peep_fixing_sub_state_mask[8]; + + if (state != PEEP_STATE_INSPECTING) + { + sub_state_sequence_mask = peep_fixing_sub_state_mask[ride->breakdown_reason_pending]; + } + + do + { + subState++; + } while ((sub_state_sequence_mask & (1 << subState)) == 0); + + sub_state = subState & 0xFF; + } +} + +/** + * rct2: 0x006C0EEC + * fixing sub_state: enter_station - applies to fixing all break down reasons and ride inspections. + */ +static bool peep_update_fixing_enter_station(Ride * ride) +{ + ride->mechanic_status = RIDE_MECHANIC_STATUS_FIXING; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE; + + return true; +} + +/** + * rct2: 0x006C0F09 + * fixing sub_state: move_to_broken_down_vehicle - applies to fixing all vehicle specific breakdown reasons + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_move_to_broken_down_vehicle(bool firstRun, rct_peep * peep, Ride * ride) +{ + sint16 x, y, tmp_xy_distance; + + if (!firstRun) + { + rct_vehicle * vehicle = ride_get_broken_vehicle(ride); + if (vehicle == nullptr) + { + 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); + } + + LocationXY16 offset = word_981D6C[peep->direction]; + peep->destination_x = (offset.x * -12) + vehicle->x; + peep->destination_y = (offset.y * -12) + vehicle->y; + peep->destination_tolerance = 2; + } + + invalidate_sprite_2((rct_sprite *)peep); + if (peep->UpdateAction(&x, &y, &tmp_xy_distance)) + { + sprite_move(x, y, peep->z, (rct_sprite *)peep); + invalidate_sprite_2((rct_sprite *)peep); + return false; + } + + return true; +} + +/** + * rct2: 0x006C0FD3 + * fixing sub_state: fix_vehicle - applies to fixing vehicle with: + * 1. restraints stuck closed, + * 2. doors stuck closed, + * 3. restrains stuck open, + * 4. doors stuck open. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_fix_vehicle(bool firstRun, rct_peep * peep, Ride * ride) +{ + 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; + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action == PEEP_ACTION_NONE_2) + { + return true; + } + + peep->UpdateAction(); + + 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 == nullptr) + { + return true; + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_CAR; + + return false; +} + +/** + * rct2: 0x006C107B + * fixing sub_state: fix_vehicle_malfunction - applies fixing to vehicle malfunction. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_fix_vehicle_malfunction(bool firstRun, rct_peep * peep, Ride * ride) +{ + 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; + + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action == PEEP_ACTION_NONE_2) + { + return true; + } + + peep->UpdateAction(); + if (peep->action_frame != 0x65) + { + return false; + } + + rct_vehicle * vehicle = ride_get_broken_vehicle(ride); + if (vehicle == nullptr) + { + return true; + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_BROKEN_TRAIN; + + return false; +} + +/** rct2: 0x00992A3C */ +static constexpr const CoordsXY _992A3C[] = { + { -12, 0 }, + { 0, 12 }, + { 12, 0 }, + { 0, -12 }, +}; + +/** + * rct2: 0x006C1114 + * fixing sub_state: move_to_station_end - applies to fixing station specific breakdowns: safety cut-out, control failure, inspection. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_move_to_station_end(bool firstRun, rct_peep * peep, 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; + } + + LocationXY8 stationPosition = ride->station_starts[peep->current_ride_station]; + if (stationPosition.xy == RCT_XY8_UNDEFINED) + { + return true; + } + + uint8 stationZ = ride->station_heights[peep->current_ride_station]; + uint16 stationX = stationPosition.x * 32; + uint16 stationY = stationPosition.y * 32; + + rct_tile_element * tileElement = map_get_track_element_at(stationX, stationY, stationZ); + if (tileElement == nullptr) + { + log_error("Couldn't find tile_element"); + return false; + } + + sint32 direction = tile_element_get_direction(tileElement); + CoordsXY 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_tolerance = 2; + } + + invalidate_sprite_2((rct_sprite *)peep); + if (!peep->UpdateAction(&x, &y, &tmp_distance)) + { + return true; + } + + sprite_move(x, y, peep->z, (rct_sprite *)peep); + invalidate_sprite_2((rct_sprite *)peep); + + return false; +} + +/** + * rct2: 0x006C11F5 + * fixing sub_state: fix_station_end - applies to fixing station specific breakdowns: safety cut-out, control failure, inspection. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_fix_station_end(bool firstRun, rct_peep * peep) +{ + if (!firstRun) + { + peep->sprite_direction = peep->direction << 3; + peep->action = PEEP_ACTION_STAFF_CHECKBOARD; + peep->action_frame = 0; + peep->action_sprite_image_offset = 0; + + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action == PEEP_ACTION_NONE_2) + { + return true; + } + + peep->UpdateAction(); + + return false; +} + +/** + * rct2: 0x006C1239 + * fixing sub_state: move_to_station_start + * 1. applies to fixing station specific breakdowns: safety cut-out, control failure, + * 2. applies to fixing brake failure, + * 3. applies to inspection. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_move_to_station_start(bool firstRun, rct_peep * peep, 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; + } + + LocationXY8 stationPosition = ride->station_starts[peep->current_ride_station]; + if (stationPosition.xy == RCT_XY8_UNDEFINED) + { + return true; + } + + uint8 stationZ = ride->station_heights[peep->current_ride_station]; + + CoordsXYE input; + input.x = stationPosition.x * 32; + input.y = stationPosition.y * 32; + input.element = map_get_track_element_at_from_ride(input.x, input.y, stationZ, peep->current_ride); + if (input.element == nullptr) + { + return true; + } + + uint8 direction = 0; + track_begin_end trackBeginEnd; + while (track_block_get_previous(input.x, input.y, input.element, &trackBeginEnd)) + { + if (track_element_is_station(trackBeginEnd.begin_element)) + { + input.x = trackBeginEnd.begin_x; + input.y = trackBeginEnd.begin_y; + input.element = trackBeginEnd.begin_element; + + direction = tile_element_get_direction(trackBeginEnd.begin_element); + continue; + } + + break; + } + + // loc_6C12ED: + uint16 destinationX = input.x + 16; + uint16 destinationY = input.y + 16; + + CoordsXY 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_tolerance = 2; + } + + invalidate_sprite_2((rct_sprite *)peep); + + if (!peep->UpdateAction(&x, &y, &tmp_xy_distance)) + { + return true; + } + + sprite_move(x, y, peep->z, (rct_sprite *)peep); + invalidate_sprite_2((rct_sprite *)peep); + + return false; +} + +/** + * rct2: 0x006C1368 + * fixing sub_state: fix_station_start + * 1. applies to fixing station specific breakdowns: safety cut-out, control failure, + * 2. applies to inspection. + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_fix_station_start(bool firstRun, rct_peep * peep, Ride * ride) +{ + 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; + + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action == PEEP_ACTION_NONE_2) + { + return true; + } + + peep->UpdateAction(); + + return false; +} + +/** + * rct2: 0x006C13CE + * fixing sub_state: fix_station_brakes - applies to fixing brake failure + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_fix_station_brakes(bool firstRun, rct_peep * peep, Ride * ride) +{ + 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; + + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action == PEEP_ACTION_NONE_2) + { + return true; + } + + peep->UpdateAction(); + if (peep->action_frame == 0x28) + { + ride->mechanic_status = RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES; + 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 + * fixing sub_state: move_to_station_exit - applies to fixing all failures & inspections + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_move_to_station_exit(bool firstRun, rct_peep * peep, Ride * ride) +{ + sint16 x, y, tmp_xy_distance; + + if (!firstRun) + { + TileCoordsXYZD stationPosition = ride_get_exit_location(ride, peep->current_ride_station); + if (stationPosition.isNull()) + { + stationPosition = ride_get_entrance_location(ride, peep->current_ride_station); + + if (stationPosition.isNull()) + { + return true; + } + } + + uint16 stationX = stationPosition.x * 32; + uint16 stationY = stationPosition.y * 32; + + stationX += 16; + stationY += 16; + + LocationXY16 direction = word_981D6C[peep->direction]; + + stationX += direction.x * 20; + stationY += direction.y * 20; + + peep->destination_x = stationX; + peep->destination_y = stationY; + peep->destination_tolerance = 2; + } + + invalidate_sprite_2((rct_sprite *)peep); + if (!peep->UpdateAction(&x, &y, &tmp_xy_distance)) + { + return true; + } + else + { + sprite_move(x, y, peep->z, (rct_sprite *)peep); + invalidate_sprite_2((rct_sprite *)peep); + } + + return false; +} + +/** + * rct2: 0x006C1504 + * fixing sub_state: finish_fix_or_inspect - applies to fixing all failures & inspections + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_finish_fix_or_inspect(bool firstRun, sint32 steps, rct_peep * peep, Ride * ride) +{ + if (!firstRun) + { + ride->mechanic_status = RIDE_MECHANIC_STATUS_UNDEFINED; + + if (peep->state == PEEP_STATE_INSPECTING) + { + peep_update_ride_inspected(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; + + peep->UpdateCurrentActionSpriteType(); + invalidate_sprite_2((rct_sprite *)peep); + } + + if (peep->action != 0xFF) + { + peep->UpdateAction(); + return false; + } + + ride_fix_breakdown(peep->current_ride, steps); + + return true; +} + +/** + * rct2: 0x006C157E + * fixing sub_state: leave_by_entrance_exit - applies to fixing all failures & inspections + * - see peep_fixing_sub_state_mask[] + */ +static bool peep_update_fixing_leave_by_entrance_exit(bool firstRun, rct_peep * peep, Ride * ride) +{ + sint16 x, y, xy_distance; + + if (!firstRun) + { + TileCoordsXYZD exitPosition = ride_get_exit_location(ride, peep->current_ride_station); + if (exitPosition.isNull()) + { + exitPosition = ride_get_entrance_location(ride, peep->current_ride_station); + + if (exitPosition.isNull()) + { + peep->SetState(PEEP_STATE_FALLING); + return false; + } + } + + uint16 exitX = exitPosition.x * 32; + uint16 exitY = exitPosition.y * 32; + + exitX += 16; + exitY += 16; + + LocationXY16 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_tolerance = 2; + } + + invalidate_sprite_2((rct_sprite *)peep); + if (!peep->UpdateAction(&x, &y, &xy_distance)) + { + peep->SetState(PEEP_STATE_FALLING); + 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 peep_update_ride_inspected(sint32 rideIndex) +{ + Ride * ride = get_ride(rideIndex); + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; + + ride->reliability += ((100 - ride->reliability_percentage) / 4) * (scenario_rand() & 0xFF); + ride->last_inspection = 0; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE | RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; +}