/***************************************************************************** * Copyright (c) 2014-2020 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #include "Supports.h" #include "../interface/Viewport.h" #include "../object/FootpathRailingsObject.h" #include "../sprites.h" #include "../util/Math.hpp" #include "../world/Surface.h" #include "Paint.h" #include "tile_element/Paint.TileElement.h" /** rct2: 0x0097AF20, 0x0097AF21 */ // clang-format off static constexpr const CoordsXY SupportBoundBoxes[] = { { 4, 4 }, { 28, 4 }, { 4, 28 }, { 28, 28 }, { 16, 16 }, { 16, 4 }, { 4, 16 }, { 28, 16 }, { 16, 28 }, }; /** rct2: 0x0097AF32 */ static constexpr const uint8_t _97AF32[] = { 5, 2, 5, 2, 5, 2, 5, 2, 7, 1, 7, 1, 7, 1, 7, 1, 6, 3, 6, 3, 6, 3, 6, 3, 8, 0, 8, 0, 8, 0, 8, 0, 5, 3, 6, 0, 8, 1, 7, 2, 1, 2, 1, 2, 1, 2, 1, 2, 0, 3, 0, 3, 0, 3, 0, 3, 3, 1, 3, 1, 3, 1, 3, 1, 2, 0, 2, 0, 2, 0, 2, 0, 6, 1, 6, 1, 6, 1, 6, 1, 5, 0, 5, 0, 5, 0, 5, 0, 8, 2, 8, 2, 8, 2, 8, 2, 7, 3, 7, 3, 7, 3, 7, 3, 6, 0, 8, 1, 7, 2, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 1, 3, 1, 3, 1, 3, 1, 3, 3, 2, 3, 2, 3, 2, 3, 2, 1, 6, 1, 6, 1, 6, 1, 6, 3, 5, 3, 5, 3, 5, 3, 5, 0, 7, 0, 7, 0, 7, 0, 7, 2, 4, 2, 4, 2, 4, 2, 4, 8, 1, 7, 2, 5, 3, 6, 0, 4, 1, 4, 1, 4, 1, 4, 1, 4, 2, 4, 2, 4, 2, 4, 2, 4, 0, 4, 0, 4, 0, 4, 0, 4, 3, 4, 3, 4, 3, 4, 3, 2, 5, 2, 5, 2, 5, 2, 5, 0, 4, 0, 4, 0, 4, 0, 4, 3, 6, 3, 6, 3, 6, 3, 6, 1, 7, 1, 7, 1, 7, 1, 7, 7, 2, 5, 3, 6, 0, 8, 1, 8, 5, 8, 5, 8, 5, 8, 5, 7, 6, 7, 6, 7, 6, 7, 6, 6, 4, 6, 4, 6, 4, 6, 4, 5, 7, 5, 7, 5, 7, 5, 7, }; /** rct2: 0x0097B052, 0x0097B053 */ static constexpr const CoordsXY loc_97B052[] = { { -15, -1 }, { 0, -2 }, { -2, -1 }, { -1, -15 }, { -26, -1 }, { 0, -2 }, { -2, -1 }, { -1, -26 }, }; /** rct2: 0x0097B062, 0x0097B063 */ static constexpr const CoordsXY _97B062[] = { { 18, 3 }, { 3, 18 }, { 18, 3 }, { 3, 18 }, { 32, 3 }, { 3, 32 }, { 32, 3 }, { 3, 32 } }; /** rct2: 0x0097B072 */ static constexpr const uint32_t _metalSupportTypeToCrossbeamImages[][8] = { { 3370, 3371, 3370, 3371, 3372, 3373, 3372, 3373 }, // METAL_SUPPORTS_TUBES { 3374, 3375, 3374, 3375, 3376, 3377, 3376, 3377 }, // METAL_SUPPORTS_FORK { 3374, 3375, 3374, 3375, 3376, 3377, 3376, 3377 }, // METAL_SUPPORTS_FORK_ALT { 3370, 3371, 3370, 3371, 3372, 3373, 3372, 3373 }, // METAL_SUPPORTS_BOXED { 3374, 3375, 3374, 3375, 3376, 3377, 3376, 3377 }, // METAL_SUPPORTS_STICK { 3374, 3375, 3374, 3375, 3376, 3377, 3376, 3377 }, // METAL_SUPPORTS_STICK_ALT { 3378, 3383, 3378, 3383, 3380, 3385, 3380, 3385 }, // METAL_SUPPORTS_THICK_CENTRED { 3378, 3383, 3378, 3383, 3380, 3385, 3380, 3385 }, // METAL_SUPPORTS_THICK { 3382, 3379, 3382, 3379, 3384, 3381, 3384, 3381 }, // METAL_SUPPORTS_THICK_ALT { 3382, 3379, 3382, 3379, 3384, 3381, 3384, 3381 }, // METAL_SUPPORTS_THICK_ALT_CENTRED { 3378, 3379, 3378, 3379, 3380, 3381, 3380, 3381 }, // METAL_SUPPORTS_TRUSS { 3386, 3387, 3386, 3387, 3388, 3389, 3388, 3389 }, // METAL_SUPPORTS_TUBES_INVERTED { 3370, 3371, 3370, 3371, 3372, 3373, 3372, 3373 }, // METAL_SUPPORTS_BOXED_COATED }; /** rct2: 0x0097B142 */ static constexpr const uint8_t supportTypeToHeight[] = { 6, // METAL_SUPPORTS_TUBES 3, // METAL_SUPPORTS_FORK 3, // METAL_SUPPORTS_FORK_ALT 6, // METAL_SUPPORTS_BOXED 3, // METAL_SUPPORTS_STICK 3, // METAL_SUPPORTS_STICK_ALT 6, // METAL_SUPPORTS_THICK_CENTRED 6, // METAL_SUPPORTS_THICK 6, // METAL_SUPPORTS_THICK_ALT 6, // METAL_SUPPORTS_THICK_ALT_CENTRED 4, // METAL_SUPPORTS_TRUSS 3, // METAL_SUPPORTS_TUBES_INVERTED 6, // METAL_SUPPORTS_BOXED_COATED }; struct metal_supports_images { uint16_t base_id; uint16_t beam_id; }; /** rct2: 0x0097B15C */ static constexpr const metal_supports_images _97B15C[] = { { 3243, 3209 }, // METAL_SUPPORTS_TUBES { 3279, 3262 }, // METAL_SUPPORTS_FORK { 3298, 3262 }, // METAL_SUPPORTS_FORK_ALT { 3334, 3317 }, // METAL_SUPPORTS_BOXED { 0, 3658 }, // METAL_SUPPORTS_STICK { 0, 3658 }, // METAL_SUPPORTS_STICK_ALT { 0, 3141 }, // METAL_SUPPORTS_THICK_CENTRED { 0, 3158 }, // METAL_SUPPORTS_THICK { 0, 3175 }, // METAL_SUPPORTS_THICK_ALT { 0, 3192 }, // METAL_SUPPORTS_THICK_ALT_CENTRED { 0, 3124 }, // METAL_SUPPORTS_TRUSS { 3243, 3209 }, // METAL_SUPPORTS_TUBES_INVERTED { 3334, 3353 }, // METAL_SUPPORTS_BOXED_COATED }; /** rct2: 0x0097B190 */ static constexpr const metal_supports_images _97B190[] = { { 3243, 3226 }, // 3209 { 3279, 3262 }, { 3298, 3262 }, { 3334, 3317 }, { 0, 3658 }, { 0, 3658 }, { 0, 3141 }, { 0, 3158 }, { 0, 3175 }, { 0, 3192 }, { 0, 3124 }, { 3243, 3226 }, // 3209 { 3334, 3353 }, }; /** rct2: 0x0097B404 */ static constexpr const uint8_t metal_supports_slope_image_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0, 17, 18, 0, }; struct supports_id_desc { uint16_t full; uint16_t half; uint16_t flat; uint16_t slope; }; /* 0x0097B1C4 */ static constexpr const supports_id_desc WoodenSupportImageIds[] = { { 3392, 3393, 3394, 3536 }, { 3390, 3391, 3394, 3514 }, { 3558, 3559, 3560, 3570 }, { 3561, 3562, 3563, 3592 }, { 3564, 3565, 3566, 3614 }, { 3567, 3568, 3569, 3636 }, { 3677, 3678, 3680, 3739 }, { 3675, 3676, 3679, 3717 }, { 3761, 3762, 3763, 3773 }, { 3764, 3765, 3766, 3795 }, { 3767, 3768, 3769, 3817 }, { 3770, 3771, 3772, 3839 }, }; static constexpr const uint16_t WoodenCurveSupportImageIds0[81] = { 3465, 3466, 3467, 3468, 3469, 3470, 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3478, 3479, 3480, 3481, 3482, 3483, 3484, 3485, 3486, 3487, 3488, 3489, 3490, 3491, 3492, 3493, 3494, 3495, 3496, 3497, 3498, 3499, 3500, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508, 3509, 3510, 3511, 3512, 3513, SPR_G2_SUPPORT_BEGIN, SPR_G2_SUPPORT_BEGIN + 1, SPR_G2_SUPPORT_BEGIN + 2, SPR_G2_SUPPORT_BEGIN + 3, SPR_G2_SUPPORT_BEGIN + 4, SPR_G2_SUPPORT_BEGIN + 5, SPR_G2_SUPPORT_BEGIN + 6, SPR_G2_SUPPORT_BEGIN + 7, SPR_G2_SUPPORT_BEGIN + 8, SPR_G2_SUPPORT_BEGIN + 9, SPR_G2_SUPPORT_BEGIN + 10, SPR_G2_SUPPORT_BEGIN + 11, SPR_G2_SUPPORT_BEGIN + 12, SPR_G2_SUPPORT_BEGIN + 13, SPR_G2_SUPPORT_BEGIN + 14, SPR_G2_SUPPORT_BEGIN + 15, SPR_G2_SUPPORT_BEGIN + 16, SPR_G2_SUPPORT_BEGIN + 17, SPR_G2_SUPPORT_BEGIN + 18, SPR_G2_SUPPORT_BEGIN + 19, SPR_G2_SUPPORT_BEGIN + 20, SPR_G2_SUPPORT_BEGIN + 21, SPR_G2_SUPPORT_BEGIN + 22, SPR_G2_SUPPORT_BEGIN + 23, SPR_G2_SUPPORT_BEGIN + 24, SPR_G2_SUPPORT_BEGIN + 25, SPR_G2_SUPPORT_BEGIN + 26, SPR_G2_SUPPORT_BEGIN + 27, SPR_G2_SUPPORT_BEGIN + 28, SPR_G2_SUPPORT_BEGIN + 29, SPR_G2_SUPPORT_BEGIN + 30, SPR_G2_SUPPORT_BEGIN + 31, }; static constexpr const uint16_t WoodenCurveSupportImageIds1[81] = { 3681, 3682, 3683, 3684, 3685, 3686, 3687, 3688, 3689, 3690, 3691, 3692, 3693, 3694, 3695, 3696, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708, 3709, 3710, 3711, 3712, 3713, 3714, 3715, 3716, 3717, 3718, 3719, 3720, 3721, 3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, }; /* 0x0097B224 */ static constexpr const uint16_t* WoodenCurveSupportImageIds[12] = { WoodenCurveSupportImageIds0, WoodenCurveSupportImageIds0, nullptr, nullptr, nullptr, nullptr, WoodenCurveSupportImageIds1, WoodenCurveSupportImageIds1, nullptr, nullptr, nullptr, nullptr, }; struct unk_supports_desc_bound_box { CoordsXYZ offset; CoordsXYZ length; }; struct unk_supports_desc { unk_supports_desc_bound_box bounding_box; uint8_t var_6; uint8_t var_7; }; /* 0x0097B23C */ static constexpr const unk_supports_desc byte_97B23C[] = { {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to gentle {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Gentle to flat {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Gentle slope {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Gentle to steep {{{10, 0, 0}, {10, 32, 44}}, 1, 1}, {{{0, 10, 0}, {32, 10, 44}}, 1, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to gentle {{{10, 0, 0}, {10, 32, 44}}, 1, 1}, {{{0, 10, 0}, {32, 10, 44}}, 1, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep slope {{{10, 0, 2}, {10, 32, 76}}, 1, 1}, {{{0, 10, 2}, {32, 10, 76}}, 1, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, // Slope {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to steep small {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to flat small {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{2, 2, 1}, {28, 28, 2}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to steep large 1 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to steep large 2 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to steep large 3 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Flat to steep large 4 {{{10, 0, 2}, {10, 32, 52}}, 1, 1}, {{{0, 10, 2}, {32, 10, 52}}, 1, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to flat large 1 {{{10, 0, 2}, {10, 32, 76}}, 1, 1}, {{{0, 10, 2}, {32, 10, 76}}, 1, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to flat large 2 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to flat large 3 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, // Steep to flat large 4 {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, {{{0, 0, 0}, {1, 1, 8}}, 0, 1}, }; /* 0x0098D8D4 */ static constexpr const unk_supports_desc byte_98D8D4[] = { {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, {{{0, 0, 0}, {1, 1, 4}}, 0, 1}, }; /* 0x0097B3C4 */ static constexpr const uint16_t word_97B3C4[] = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 17, 0, 16, 15, 0, }; // clang-format on /** * Adds paint structs for wooden supports. * rct2: 0x006629BC * @param supportType (edi) Type and direction of supports. * @param special (ax) Used for curved supports. * @param height (dx) The height of the supports. * @param imageColourFlags (ebp) The colour and palette flags for the support sprites. * @param[out] underground (Carry flag) true if underground. * @returns (al) true if any supports have been drawn, otherwise false. */ bool wooden_a_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags) { if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; } int32_t z = floor2(session->Support.height + 15, 16); height -= z; if (height < 0) { return false; } height /= 16; bool hasSupports = false; bool drawFlatPiece = false; // Draw base support (usually shaped to the slope) int32_t slope = session->Support.slope; if (slope & SUPPORTS_SLOPE_5) { // Above scenery (just put a base piece above it) drawFlatPiece = true; } else if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { // Steep diagonal (place the correct shaped support for the slope) height -= 2; if (height < 0) { return false; } int32_t imageId = WoodenSupportImageIds[supportType].slope; if (imageId == 0) { drawFlatPiece = true; } else { imageId += word_97B3C4[slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; imageId |= imageColourFlags; PaintAddImageAsParent(session, imageId, { 0, 0, z }, { 32, 32, 11 }, { 0, 0, z + 2 }); PaintAddImageAsParent(session, imageId + 4, { 0, 0, z + 16 }, { 32, 32, 11 }, { 0, 0, z + 16 + 2 }); hasSupports = true; } z += 32; } else if ((slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) { // 1 to 3 quarters up height--; if (height < 0) { return false; } int32_t imageId = WoodenSupportImageIds[supportType].slope; if (imageId == 0) { drawFlatPiece = true; } else { imageId += word_97B3C4[slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; imageId |= imageColourFlags; PaintAddImageAsParent(session, imageId, { 0, 0, z }, { 32, 32, 11 }, { 0, 0, z + 2 }); hasSupports = true; } z += 16; } // Draw flat base support if (drawFlatPiece) { int32_t imageId = WoodenSupportImageIds[supportType].flat | imageColourFlags; PaintAddImageAsParent(session, imageId, { 0, 0, z - 2 }, { 32, 32, 0 }); hasSupports = true; } // Draw repeated supports for left over space while (height != 0) { if ((z & 16) == 0 && height >= 2 && z + 16 != session->WaterHeight) { // Full support int32_t imageId = WoodenSupportImageIds[supportType].full | imageColourFlags; uint8_t ah = height == 2 ? 23 : 28; PaintAddImageAsParent(session, imageId, { 0, 0, z }, { 32, 32, ah }); hasSupports = true; z += 32; height -= 2; } else { // Half support int32_t imageId = WoodenSupportImageIds[supportType].half | imageColourFlags; uint8_t ah = height == 1 ? 7 : 12; PaintAddImageAsParent(session, imageId, { 0, 0, z }, { 32, 32, ah }); hasSupports = true; z += 16; height -= 1; } } // Draw special pieces, e.g. curved supports if (special != 0) { special = (special - 1) & 0xFFFF; if (WoodenCurveSupportImageIds[supportType] != nullptr && WoodenCurveSupportImageIds[supportType][special] != 0 && byte_97B23C[special].var_7 != 0) { uint32_t imageId = WoodenCurveSupportImageIds[supportType][special]; imageId |= imageColourFlags; unk_supports_desc_bound_box bBox = byte_97B23C[special].bounding_box; if (byte_97B23C[special].var_6 == 0 || session->WoodenSupportsPrependTo == nullptr) { PaintAddImageAsParent( session, imageId, { 0, 0, z }, bBox.length, { bBox.offset.x, bBox.offset.y, bBox.offset.z + z }); hasSupports = true; } else { hasSupports = true; paint_struct* ps = PaintAddImageAsOrphan( session, ImageId::FromUInt32(imageId), { 0, 0, z }, bBox.length, { bBox.offset.x, bBox.offset.y, bBox.offset.z + z }); if (ps != nullptr) { session->WoodenSupportsPrependTo->children = ps; } } } } return hasSupports; } /** * Wooden supports * rct2: 0x00662D5C * * @param supportType (edi) * @param special (ax) * @param height (dx) * @param imageColourFlags (ebp) * @param[out] underground (Carry Flag) * * @return (al) whether supports have been drawn */ bool wooden_b_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags) { bool _9E32B1 = false; if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; } uint16_t baseHeight = ceil2(session->Support.height, 16); int16_t supportLength = height - baseHeight; if (supportLength < 0) { return false; } int16_t heightSteps = supportLength / 16; bool goTo662E8B = false; if (session->Support.slope & 0x20) { goTo662E8B = true; } else if (session->Support.slope & 0x10) { heightSteps -= 2; if (heightSteps < 0) { return false; } uint32_t imageId = WoodenSupportImageIds[supportType].slope; if (imageId == 0) { baseHeight += 32; goTo662E8B = true; } else { imageId += word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); baseHeight += 16; PaintAddImageAsParent( session, (imageId + 4) | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 3 }, { 0, 0, baseHeight + 2 }); baseHeight += 16; _9E32B1 = true; } } else if ((session->Support.slope & 0x0F) != 0) { heightSteps -= 1; if (heightSteps < 0) { return false; } uint32_t imageId = WoodenSupportImageIds[supportType].slope; if (imageId == 0) { baseHeight += 16; goTo662E8B = true; } else { imageId += word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 3 }, { 0, 0, baseHeight + 2 }); baseHeight += 16; _9E32B1 = true; } } bool skipTo663004 = false; if (goTo662E8B) { if (heightSteps == 0) { skipTo663004 = true; } else { PaintAddImageAsParent( session, WoodenSupportImageIds[supportType].flat | imageColourFlags, { 0, 0, baseHeight - 2 }, { 32, 32, 0 }); _9E32B1 = true; } } if (!skipTo663004) { while (heightSteps > 0) { if (baseHeight & 0x10 || heightSteps == 1 || baseHeight + 16 == session->WaterHeight) { PaintAddImageAsParent( session, WoodenSupportImageIds[supportType].half | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 1) ? 7 : 12) }); heightSteps -= 1; baseHeight += 16; _9E32B1 = true; } else { PaintAddImageAsParent( session, WoodenSupportImageIds[supportType].full | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 2) ? 23 : 28) }); heightSteps -= 2; baseHeight += 32; _9E32B1 = true; } } } if (special != 0) { uint16_t specialIndex = (special - 1) & 0xFFFF; const unk_supports_desc& supportsDesc = byte_97B23C[specialIndex]; if (WoodenCurveSupportImageIds[supportType] != nullptr && WoodenCurveSupportImageIds[supportType][specialIndex] != 0 && supportsDesc.var_7 != 0) { // byte_97B23C[special].var_7 is never 0 uint32_t imageId = WoodenCurveSupportImageIds[supportType][specialIndex]; imageId |= imageColourFlags; const unk_supports_desc_bound_box& boundBox = supportsDesc.bounding_box; if (supportsDesc.var_6 == 0 || session->WoodenSupportsPrependTo == nullptr) { PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, boundBox.length, { boundBox.offset.x, boundBox.offset.y, boundBox.offset.z + baseHeight }); _9E32B1 = true; } else { paint_struct* paintStruct = PaintAddImageAsOrphan( session, ImageId::FromUInt32(imageId | imageColourFlags), { 0, 0, baseHeight }, boundBox.length, { boundBox.offset.x, boundBox.offset.y, boundBox.offset.z + baseHeight }); _9E32B1 = true; if (paintStruct != nullptr) { session->WoodenSupportsPrependTo->children = paintStruct; } } } } return _9E32B1; } /** * Metal pole supports * @param supportType (edi) * @param segment (ebx) * @param special (ax) * @param height (edx) * @param imageColourFlags (ebp) * rct2: 0x00663105 */ bool metal_a_supports_paint_setup( paint_session* session, uint8_t supportType, uint8_t segment, int32_t special, int32_t height, uint32_t imageColourFlags) { support_height* supportSegments = session->SupportSegments; if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; } int16_t originalHeight = height; int32_t originalSegment = segment; const uint8_t rotation = session->CurrentRotation; int16_t unk9E3294 = -1; if (height < supportSegments[segment].height) { unk9E3294 = height; height -= supportTypeToHeight[supportType]; if (height < 0) return false; const uint8_t* esi = &(_97AF32[rotation * 2]); uint8_t newSegment = esi[segment * 8]; if (height <= supportSegments[newSegment].height) { esi += 72; newSegment = esi[segment * 8]; if (height <= supportSegments[newSegment].height) { esi += 72; newSegment = esi[segment * 8]; if (height <= supportSegments[newSegment].height) { esi += 72; newSegment = esi[segment * 8]; if (height <= supportSegments[newSegment].height) { return false; } } } } uint8_t ebp = esi[segment * 8 + 1]; int8_t xOffset = SupportBoundBoxes[segment].x; int8_t yOffset = SupportBoundBoxes[segment].y; xOffset += loc_97B052[ebp].x; yOffset += loc_97B052[ebp].y; int16_t boundBoxLengthX = _97B062[ebp].x; int16_t boundBoxLengthY = _97B062[ebp].y; uint32_t image_id = _metalSupportTypeToCrossbeamImages[supportType][ebp]; image_id |= imageColourFlags; PaintAddImageAsParent(session, image_id, { xOffset, yOffset, height }, { boundBoxLengthX, boundBoxLengthY, 1 }); segment = newSegment; } int16_t si = height; if (supportSegments[segment].slope & SUPPORTS_SLOPE_5 || height - supportSegments[segment].height < 6 || _97B15C[supportType].base_id == 0) { height = supportSegments[segment].height; } else { int8_t xOffset = SupportBoundBoxes[segment].x; int8_t yOffset = SupportBoundBoxes[segment].y; uint32_t image_id = _97B15C[supportType].base_id; image_id += metal_supports_slope_image_map[supportSegments[segment].slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; image_id |= imageColourFlags; PaintAddImageAsParent(session, image_id, { xOffset, yOffset, supportSegments[segment].height }, { 0, 0, 5 }); height = supportSegments[segment].height + 6; } // Work out if a small support segment required to bring support to normal // size (aka floor2(x, 16)) int16_t heightDiff = floor2(height + 16, 16); if (heightDiff > si) { heightDiff = si; } heightDiff -= height; if (heightDiff > 0) { int8_t xOffset = SupportBoundBoxes[segment].x; int8_t yOffset = SupportBoundBoxes[segment].y; uint32_t image_id = _97B15C[supportType].beam_id; image_id += heightDiff - 1; image_id |= imageColourFlags; PaintAddImageAsParent(session, image_id, { xOffset, yOffset, height }, { 0, 0, heightDiff - 1 }); } height += heightDiff; // 6632e6 for (uint8_t count = 0;; count++) { if (count >= 4) count = 0; int16_t z = height + 16; if (z > si) { z = si; } z -= height; if (z <= 0) break; int8_t xOffset = SupportBoundBoxes[segment].x; int8_t yOffset = SupportBoundBoxes[segment].y; uint32_t image_id = _97B15C[supportType].beam_id; image_id += z - 1; image_id |= imageColourFlags; if (count == 3 && z == 0x10) image_id++; PaintAddImageAsParent(session, image_id, { xOffset, yOffset, height }, { 0, 0, z - 1 }); height += z; } supportSegments[segment].height = unk9E3294; supportSegments[segment].slope = 0x20; height = originalHeight; segment = originalSegment; if (special == 0) return true; if (special < 0) { special = -special; height--; } CoordsXYZ boundBoxOffset = CoordsXYZ(SupportBoundBoxes[segment], height); si = height + special; while (1) { int16_t z = height + 16; if (z > si) { z = si; } z -= height; if (z <= 0) break; int8_t xOffset = SupportBoundBoxes[segment].x; int8_t yOffset = SupportBoundBoxes[segment].y; uint32_t image_id = _97B190[supportType].beam_id; image_id += z - 1; image_id |= imageColourFlags; PaintAddImageAsParent(session, image_id, { xOffset, yOffset, height }, { 0, 0, 0 }, boundBoxOffset); height += z; } return true; } /** * Metal pole supports * rct2: 0x00663584 * * @param supportType (edi) * @param segment (ebx) * @param special (ax) * @param height (edx) * @param imageColourFlags (ebp) * * @return (Carry Flag) */ bool metal_b_supports_paint_setup( paint_session* session, uint8_t supportType, uint8_t segment, int32_t special, int32_t height, uint32_t imageColourFlags) { support_height* supportSegments = session->SupportSegments; uint8_t originalSegment = segment; if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; // AND } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; // AND } uint16_t _9E3294 = 0xFFFF; int32_t baseHeight = height; if (height < supportSegments[segment].height) { _9E3294 = height; baseHeight -= supportTypeToHeight[supportType]; if (baseHeight < 0) { return false; // AND } uint16_t baseIndex = session->CurrentRotation * 2; uint8_t ebp = _97AF32[baseIndex + segment * 8]; if (baseHeight <= supportSegments[ebp].height) { baseIndex += 9 * 4 * 2; // 9 segments, 4 directions, 2 values uint8_t ebp2 = _97AF32[baseIndex + segment * 8]; if (baseHeight <= supportSegments[ebp2].height) { baseIndex += 9 * 4 * 2; uint8_t ebp3 = _97AF32[baseIndex + segment * 8]; if (baseHeight <= supportSegments[ebp3].height) { baseIndex += 9 * 4 * 2; uint8_t ebp4 = _97AF32[baseIndex + segment * 8]; if (baseHeight <= supportSegments[ebp4].height) { return true; // STC } } } } ebp = _97AF32[baseIndex + segment * 8 + 1]; if (ebp >= 4) { return true; // STC } PaintAddImageAsParent( session, _metalSupportTypeToCrossbeamImages[supportType][ebp] | imageColourFlags, { SupportBoundBoxes[originalSegment] + loc_97B052[ebp], baseHeight }, { _97B062[ebp], 1 }); } int32_t si = baseHeight; if ((supportSegments[segment].slope & 0x20) || (baseHeight - supportSegments[segment].height < 6) || (_97B15C[supportType].base_id == 0)) { baseHeight = supportSegments[segment].height; } else { uint32_t imageOffset = metal_supports_slope_image_map[supportSegments[segment].slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; uint32_t imageId = _97B15C[supportType].base_id + imageOffset; PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[segment], supportSegments[segment].height }, { 0, 0, 5 }); baseHeight = supportSegments[segment].height + 6; } int16_t heightDiff = floor2(baseHeight + 16, 16); if (heightDiff > si) { heightDiff = si; } heightDiff -= baseHeight; if (heightDiff > 0) { PaintAddImageAsParent( session, (_97B15C[supportType].beam_id + (heightDiff - 1)) | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, heightDiff - 1 }); } baseHeight += heightDiff; int16_t endHeight; int32_t i = 1; while (true) { endHeight = baseHeight + 16; if (endHeight > si) { endHeight = si; } int16_t beamLength = endHeight - baseHeight; if (beamLength <= 0) { break; } uint32_t imageId = _97B15C[supportType].beam_id + (beamLength - 1); if (i % 4 == 0) { // Each fourth run, draw a special image if (beamLength == 16) { imageId += 1; } } PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, beamLength - 1 }); baseHeight += beamLength; i++; } supportSegments[segment].height = _9E3294; supportSegments[segment].slope = 0x20; if (special != 0) { baseHeight = height; si = height + special; while (true) { endHeight = baseHeight + 16; if (endHeight > si) { endHeight = si; } int16_t beamLength = endHeight - baseHeight; if (beamLength <= 0) { break; } uint32_t imageId = _97B15C[supportType].beam_id + (beamLength - 1); PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[originalSegment], baseHeight }, { 0, 0, 0 }, { SupportBoundBoxes[originalSegment], height }); baseHeight += beamLength; } } return false; // AND } /** * rct2: 0x006A2ECC * * @param supportType (edi) * @param special (ax) * @param height (dx) * @param imageColourFlags (ebp) * @param railingsDescriptor (0x00F3EF6C) * @param[out] underground (Carry Flag) * * @return Whether supports were drawn */ bool path_a_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags, const FootpathPaintInfo& pathPaintInfo, bool* underground) { if (underground != nullptr) { *underground = false; // AND } if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; } uint16_t baseHeight = ceil2(session->Support.height, 16); int32_t supportLength = height - baseHeight; if (supportLength < 0) { if (underground != nullptr) *underground = true; // STC return false; } bool hasSupports = false; int16_t heightSteps = supportLength / 16; if (session->Support.slope & 0x20) { // save dx2 PaintAddImageAsParent( session, (pathPaintInfo.BridgeImageId + 48) | imageColourFlags, { 0, 0, baseHeight - 2 }, { 32, 32, 0 }); hasSupports = true; } else if (session->Support.slope & 0x10) { heightSteps -= 2; if (heightSteps < 0) { if (underground != nullptr) *underground = true; // STC return false; } uint32_t imageId = (supportType * 24) + word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK] + pathPaintInfo.BridgeImageId; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); baseHeight += 16; PaintAddImageAsParent( session, (imageId + 4) | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); baseHeight += 16; hasSupports = true; } else if (session->Support.slope & 0x0F) { heightSteps -= 1; if (heightSteps < 0) { if (underground != nullptr) *underground = true; // STC return false; } uint32_t ebx = (supportType * 24) + word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK] + pathPaintInfo.BridgeImageId; PaintAddImageAsParent(session, ebx | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); hasSupports = true; baseHeight += 16; } while (heightSteps > 0) { if (baseHeight & 0x10 || heightSteps == 1 || baseHeight + 16 == session->WaterHeight) { uint32_t imageId = (supportType * 24) + pathPaintInfo.BridgeImageId + 23; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 1) ? 7 : 12) }); heightSteps -= 1; baseHeight += 16; hasSupports = true; } else { uint32_t imageId = (supportType * 24) + pathPaintInfo.BridgeImageId + 22; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 2) ? 23 : 28) }); heightSteps -= 2; baseHeight += 32; hasSupports = true; } } if (special != 0) { uint16_t specialIndex = (special - 1) & 0xFFFF; uint32_t imageId = pathPaintInfo.BridgeImageId + 55 + specialIndex; const unk_supports_desc& supportsDesc = byte_98D8D4[specialIndex]; const unk_supports_desc_bound_box& boundBox = supportsDesc.bounding_box; if (supportsDesc.var_6 == 0 || session->WoodenSupportsPrependTo == nullptr) { PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, boundBox.length, { boundBox.offset.x, boundBox.offset.y, baseHeight + boundBox.offset.z }); hasSupports = true; } else { paint_struct* paintStruct = PaintAddImageAsOrphan( session, ImageId::FromUInt32(imageId | imageColourFlags), { 0, 0, baseHeight }, boundBox.length, { boundBox.offset.x, boundBox.offset.y, baseHeight + boundBox.offset.z }); hasSupports = true; if (paintStruct != nullptr) { session->WoodenSupportsPrependTo->children = paintStruct; } } } if (underground != nullptr) *underground = false; // AND return hasSupports; } /** * * rct2: 0x006A326B * * @param segment (ebx) * @param special (ax) * @param height (dx) * @param imageColourFlags (ebp) * @param railingsDescriptor (0x00F3EF6C) * * @return Whether supports were drawn */ bool path_b_supports_paint_setup( paint_session* session, int32_t segment, int32_t special, int32_t height, uint32_t imageColourFlags, const FootpathPaintInfo& pathPaintInfo) { support_height* supportSegments = session->SupportSegments; if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; // AND } if (!(session->Unk141E9DB & PaintSessionFlags::IsPassedSurface)) { return false; // AND } if (height < supportSegments[segment].height) { return true; // STC } uint16_t baseHeight; if ((supportSegments[segment].slope & 0x20) || (height - supportSegments[segment].height < 6) || !(pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE)) { baseHeight = supportSegments[segment].height; } else { uint8_t imageOffset = metal_supports_slope_image_map[supportSegments[segment].slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; baseHeight = supportSegments[segment].height; PaintAddImageAsParent( session, (pathPaintInfo.BridgeImageId + 37 + imageOffset) | imageColourFlags, { SupportBoundBoxes[segment].x, SupportBoundBoxes[segment].y, baseHeight }, { 0, 0, 5 }); baseHeight += 6; } // si = height // dx = baseHeight int16_t heightDiff = floor2(baseHeight + 16, 16); if (heightDiff > height) { heightDiff = height; } heightDiff -= baseHeight; if (heightDiff > 0) { PaintAddImageAsParent( session, (pathPaintInfo.BridgeImageId + 20 + (heightDiff - 1)) | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, heightDiff - 1 }); } baseHeight += heightDiff; bool keepGoing = true; while (keepGoing) { int16_t z; for (int32_t i = 0; i < 4; ++i) { z = baseHeight + 16; if (z > height) { z = height; } z -= baseHeight; if (z <= 0) { keepGoing = false; break; } if (i == 3) { // Only do the z check in the fourth run. break; } PaintAddImageAsParent( session, (pathPaintInfo.BridgeImageId + 20 + (z - 1)) | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, (z - 1) }); baseHeight += z; } if (!keepGoing) { break; } uint32_t imageId = pathPaintInfo.BridgeImageId + 20 + (z - 1); if (z == 16) { imageId += 1; } PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, (z - 1) }); baseHeight += z; } // loc_6A34D8 supportSegments[segment].height = 0xFFFF; supportSegments[segment].slope = 0x20; if (special != 0) { int16_t si = special + baseHeight; while (true) { int16_t z = baseHeight + 16; if (z > si) { z = si; } z -= baseHeight; if (z <= 0) { break; } uint32_t imageId = pathPaintInfo.BridgeImageId + 20 + (z - 1); PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, 0 }, { SupportBoundBoxes[segment], baseHeight }); baseHeight += z; } } return false; // AND }