/***************************************************************************** * Copyright (c) 2014 Ted John * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * This file is part of OpenRCT2. * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ #include "../addresses.h" #include "../config.h" #include "../drawing/drawing.h" #include "../drawing/supports.h" #include "../game.h" #include "../input.h" #include "../localisation/localisation.h" #include "../ride/ride_data.h" #include "../ride/track_data.h" #include "../sprites.h" #include "../world/climate.h" #include "../world/map.h" #include "../world/sprite.h" #include "../world/banner.h" #include "../world/entrance.h" #include "../world/footpath.h" #include "../world/scenery.h" #include "paint_surface.h" //#define DEBUG_SHOW_DIRTY_BOX rct_viewport g_viewport_list[MAX_VIEWPORT_COUNT]; /** * This is not a viewport function. It is used to setup many variables for * multiple things. * rct2: 0x006E6EAC */ void viewport_init_all() { colours_init_maps(); // Setting up windows gWindowNextSlot = g_window_list; RCT2_GLOBAL(0x01423604, sint32) = 0; // Setting up viewports for (int i = 0; i < MAX_VIEWPORT_COUNT; i++) { g_viewport_list[i].width = 0; } // ? gInputFlags = 0; gInputState = INPUT_STATE_RESET; gPressedWidget.window_classification = 255; gPickupPeepImage = UINT32_MAX; gTooltipNotShownTicks = -1; RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) = 0; RCT2_GLOBAL(0x009DEA50, sint16) = -1; textinput_cancel(); format_string((char*)0x0141FA44, STR_CANCEL, NULL); format_string((char*)0x0141F944, STR_OK, NULL); } /** * Converts between 3d point of a sprite to 2d coordinates for centering on that * sprite * rct2: 0x006EB0C1 * x : ax * y : bx * z : cx * out_x : ax * out_y : bx */ void center_2d_coordinates(int x, int y, int z, int* out_x, int* out_y, rct_viewport* viewport){ int start_x = x; rct_xyz16 coord_3d = { .x = x, .y = y, .z = z }; rct_xy16 coord_2d = coordinate_3d_to_2d(&coord_3d, get_current_rotation()); // If the start location was invalid // propagate the invalid location to the output. // This fixes a bug that caused the game to enter an infinite loop. if (start_x == (sint16)0x8000){ *out_x = (sint16)0x8000; *out_y = 0; return; } *out_x = coord_2d.x - viewport->view_width / 2; *out_y = coord_2d.y - viewport->view_height / 2; } /** * Viewport will look at sprite or at coordinates as specified in flags 0b_1X * for sprite 0b_0X for coordinates * * rct2: 0x006EB009 * x: ax * y: eax (top 16) * width: bx * height: ebx (top 16) * zoom: cl (8 bits) * center_x: edx lower 16 bits * center_y: edx upper 16 bits * center_z: ecx upper 16 bits * sprite: edx lower 16 bits * flags: edx top most 2 bits 0b_X1 for zoom clear see below for 2nd bit. * w: esi */ void viewport_create(rct_window *w, int x, int y, int width, int height, int zoom, int center_x, int center_y, int center_z, char flags, sint16 sprite) { rct_viewport* viewport = NULL; for (int i = 0; i < MAX_VIEWPORT_COUNT; i++) { if (g_viewport_list[i].width == 0) { viewport = &g_viewport_list[i]; break; } } if (viewport == NULL) { log_error("No more viewport slots left to allocate."); return; } viewport->x = x; viewport->y = y; viewport->width = width; viewport->height = height; if (!(flags & VIEWPORT_FOCUS_TYPE_COORDINATE)){ zoom = 0; } viewport->view_width = width << zoom; viewport->view_height = height << zoom; viewport->zoom = zoom; viewport->flags = 0; if (gConfigGeneral.always_show_gridlines) viewport->flags |= VIEWPORT_FLAG_GRIDLINES; w->viewport = viewport; if (flags & VIEWPORT_FOCUS_TYPE_SPRITE){ w->viewport_target_sprite = sprite; rct_sprite* center_sprite = &g_sprite_list[sprite]; center_x = center_sprite->unknown.x; center_y = center_sprite->unknown.y; center_z = center_sprite->unknown.z; } else{ w->viewport_target_sprite = SPR_NONE; } int view_x, view_y; center_2d_coordinates(center_x, center_y, center_z, &view_x, &view_y, viewport); w->saved_view_x = view_x; w->saved_view_y = view_y; viewport->view_x = view_x; viewport->view_y = view_y; viewport_update_pointers(); } /** * * rct2: 0x006EE510 */ void viewport_update_pointers() { } /** * * rct2: 0x00689174 * edx is assumed to be (and always is) the current rotation, so it is not * needed as parameter. */ void sub_689174(sint16* x, sint16* y, sint16 *z) { sint16 start_x = *x; sint16 start_y = *y; sint16 height = 0; rct_xy16 pos; for (int i = 0; i < 6; i++) { pos = viewport_coord_to_map_coord(start_x, start_y, height); height = map_element_height((0xFFFF) & pos.x, (0xFFFF) & pos.y); // HACK: This is to prevent the x and y values being set to values outside // of the map. This can happen when the height is larger than the map size. sint16 max = gMapSizeMinus2; if (pos.x > max && pos.y > max) { int x_corr[] = { -1, 1, 1, -1 }; int y_corr[] = { -1, -1, 1, 1 }; uint32 rotation = get_current_rotation(); pos.x += x_corr[rotation] * height; pos.y += y_corr[rotation] * height; } } *x = pos.x; *y = pos.y; *z = height; } void sub_683326(int left, int top, int right, int bottom) { RCT2_CALLPROC_X(0x00683359, left, top, right, bottom, 0, 0, 0); } /** * shifts pixels from the region in a direction. Used when a viewport moves; * consider putting in src/drawing/drawing.c or src/drawing/rect.c * * rct2: 0x00683359 * ax = x * bx = y; * cx = width; * dx = height; * di = dx; * si = dy; */ void gfx_move_screen_rect(int x, int y, int width, int height, int dx, int dy) { // nothing to do if (dx == 0 && dy == 0) return; // get screen info rct_drawpixelinfo *screenDPI = &gScreenDPI; // adjust for move off screen // NOTE: when zooming, there can be x, y, dx, dy combinations that go off the // screen; hence the checks. This code should ultimately not be called when // zooming because this function is specific to updating the screen on move int lmargin = min(x - dx, 0); int rmargin = min(gScreenWidth - (x - dx + width), 0); int tmargin = min(y - dy, 0); int bmargin = min(gScreenHeight - (y - dy + height), 0); x -= lmargin; y -= tmargin; width += lmargin + rmargin; height += tmargin + bmargin; sint32 stride = screenDPI->width + screenDPI->pitch; uint8* to = screenDPI->bits + y * stride + x; uint8* from = screenDPI->bits + (y - dy) * stride + x - dx; if (dy > 0) { // if positive dy, reverse directions to += (height - 1) * stride; from += (height - 1) * stride; stride = -stride; } // move bits for (int i = 0; i < height; i++, to += stride, from += stride) memmove(to, from, width); } void sub_6E7FF3(rct_window *window, rct_viewport *viewport, int x, int y) { // sub-divide by intersecting windows if (window < gWindowNextSlot) { // skip current window and non-intersecting windows if (viewport == window->viewport || viewport->x + viewport->width <= window->x || viewport->x >= window->x + window->width || viewport->y + viewport->height <= window->y || viewport->y >= window->y + window->height){ sub_6E7FF3(window + 1, viewport, x, y); return; } // save viewport rct_viewport view_copy; memcpy(&view_copy, viewport, sizeof(rct_viewport)); if (viewport->x < window->x) { viewport->width = window->x - viewport->x; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); viewport->x += viewport->width; viewport->view_x += viewport->width << viewport->zoom; viewport->width = view_copy.width - viewport->width; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); } else if (viewport->x + viewport->width > window->x + window->width) { viewport->width = window->x + window->width - viewport->x; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); viewport->x += viewport->width; viewport->view_x += viewport->width << viewport->zoom; viewport->width = view_copy.width - viewport->width; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); } else if (viewport->y < window->y) { viewport->height = window->y - viewport->y; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); viewport->y += viewport->height; viewport->view_y += viewport->height << viewport->zoom; viewport->height = view_copy.height - viewport->height; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); } else if (viewport->y + viewport->height > window->y + window->height) { viewport->height = window->y + window->height - viewport->y; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); viewport->y += viewport->height; viewport->view_y += viewport->height << viewport->zoom; viewport->height = view_copy.height - viewport->height; viewport->view_width = viewport->width << viewport->zoom; sub_6E7FF3(window, viewport, x, y); } // restore viewport memcpy(viewport, &view_copy, sizeof(rct_viewport)); } else { sint16 left = viewport->x; sint16 right = viewport->x + viewport->width; sint16 top = viewport->y; sint16 bottom = viewport->y + viewport->height; // if moved more than the viewport size if (abs(x) < viewport->width && abs(y) < viewport->height) { // update whole block ? gfx_move_screen_rect(viewport->x, viewport->y, viewport->width, viewport->height, x, y); if (x > 0) { // draw left sint16 _right = viewport->x + x; gfx_redraw_screen_rect(left, top, _right, bottom); left += x; } else if (x < 0) { // draw right sint16 _left = viewport->x + viewport->width + x; gfx_redraw_screen_rect(_left, top, right, bottom); right += x; } if (y > 0) { // draw top bottom = viewport->y + y; gfx_redraw_screen_rect(left, top, right, bottom); } else if (y < 0) { // draw bottom top = viewport->y + viewport->height + y; gfx_redraw_screen_rect(left, top, right, bottom); } } else { // redraw whole viewport gfx_redraw_screen_rect(left, top, right, bottom); } } } void sub_6E7F34(rct_window* w, rct_viewport* viewport, sint16 x_diff, sint16 y_diff){ rct_window* orignal_w = w; int left = 0, right = 0, top = 0, bottom = 0; for (; w < gWindowNextSlot; w++){ if (!(w->flags & WF_TRANSPARENT)) continue; if (w->viewport == viewport) continue; if (viewport->x + viewport->width <= w->x)continue; if (w->x + w->width <= viewport->x) continue; if (viewport->y + viewport->height <= w->y)continue; if (w->y + w->height <= viewport->y) continue; left = w->x; right = w->x + w->width; top = w->y; bottom = w->y + w->height; if (left < viewport->x)left = viewport->x; if (right > viewport->x + viewport->width) right = viewport->x + viewport->width; if (top < viewport->y)top = viewport->y; if (bottom > viewport->y + viewport->height) bottom = viewport->y + viewport->height; if (left >= right) continue; if (top >= bottom) continue; gfx_redraw_screen_rect(left, top, right, bottom); } w = orignal_w; sub_6E7FF3(w, viewport, x_diff, y_diff); } void sub_6E7DE1(sint16 x, sint16 y, rct_window* w, rct_viewport* viewport){ uint8 zoom = (1 << viewport->zoom); // Note: do not do the subtraction and then divide! // Note: Due to arithmatic shift != /zoom a shift will have to be used // hopefully when 0x006E7FF3 is finished this can be converted to /zoom. sint16 x_diff = (viewport->view_x >> viewport->zoom) - (x >> viewport->zoom); sint16 y_diff = (viewport->view_y >> viewport->zoom) - (y >> viewport->zoom); viewport->view_x = x; viewport->view_y = y; // If no change in viewing area if ((!x_diff) && (!y_diff))return; if (w->flags & WF_7){ int left = max(viewport->x, 0); int top = max(viewport->y, 0); int right = min(viewport->x + viewport->width, gScreenWidth); int bottom = min(viewport->y + viewport->height, gScreenHeight); if (left >= right) return; if (top >= bottom) return; gfx_redraw_screen_rect(left, top, right, bottom); return; } rct_viewport view_copy; memcpy(&view_copy, viewport, sizeof(rct_viewport)); if (viewport->x < 0){ viewport->width += viewport->x; viewport->view_width += viewport->x * zoom; viewport->view_x -= viewport->x * zoom; viewport->x = 0; } int eax = viewport->x + viewport->width - gScreenWidth; if (eax > 0){ viewport->width -= eax; viewport->view_width -= eax * zoom; } if (viewport->width <= 0){ memcpy(viewport, &view_copy, sizeof(rct_viewport)); return; } if (viewport->y < 0){ viewport->height += viewport->y; viewport->view_height += viewport->y * zoom; viewport->view_y -= viewport->y * zoom; viewport->y = 0; } eax = viewport->y + viewport->height - gScreenHeight; if (eax > 0){ viewport->height -= eax; viewport->view_height -= eax * zoom; } if (viewport->height <= 0){ memcpy(viewport, &view_copy, sizeof(rct_viewport)); return; } sub_6E7F34(w, viewport, x_diff, y_diff); memcpy(viewport, &view_copy, sizeof(rct_viewport)); } //rct2: 0x006E7A15 void viewport_set_underground_flag(int underground, rct_window* window, rct_viewport* viewport) { if (window->classification != WC_MAIN_WINDOW) { if (!underground) { int bit = viewport->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE; viewport->flags &= ~VIEWPORT_FLAG_UNDERGROUND_INSIDE; if (!bit) return; } else { int bit = viewport->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE; viewport->flags |= VIEWPORT_FLAG_UNDERGROUND_INSIDE; if (bit) return; } window_invalidate(window); } } /** * * rct2: 0x006E7A3A */ void viewport_update_position(rct_window *window) { window_event_resize_call(window); rct_viewport* viewport = window->viewport; if (!viewport)return; if (window->viewport_target_sprite != -1) { viewport_update_sprite_follow(window); return; } sint16 x = viewport->view_width / 2 + window->saved_view_x; sint16 y = viewport->view_height / 2 + window->saved_view_y; sint16 z; sub_689174(&x, &y, &z); viewport_set_underground_flag(0, window, viewport); //Clamp to the map minimum value int at_map_edge = 0; if (x < MAP_MINIMUM_X_Y){ x = MAP_MINIMUM_X_Y; at_map_edge = 1; } if (y < MAP_MINIMUM_X_Y){ y = MAP_MINIMUM_X_Y; at_map_edge = 1; } //Clamp to the map maximum value (scenario specific) if (x > gMapSizeMinus2){ x = gMapSizeMinus2; at_map_edge = 1; } if (y > gMapSizeMinus2){ y = gMapSizeMinus2; at_map_edge = 1; } if (at_map_edge) { // The &0xFFFF is to prevent the sign extension messing the // function up. int z = map_element_height(x & 0xFFFF, y & 0xFFFF); int _2d_x, _2d_y; center_2d_coordinates(x, y, z, &_2d_x, &_2d_y, viewport); if (window->saved_view_x > 0){ _2d_x = min(_2d_x, window->saved_view_x); } else{ _2d_x = max(_2d_x, window->saved_view_x); } if (window->saved_view_y > 0){ _2d_y = min(_2d_y, window->saved_view_y); } else{ _2d_y = max(_2d_y, window->saved_view_y); } window->saved_view_x = _2d_x; window->saved_view_y = _2d_y; } x = window->saved_view_x; y = window->saved_view_y; if (window->flags & WF_SCROLLING_TO_LOCATION){ // Moves the viewport if focusing in on an item uint8 flags = 0; x -= viewport->view_x; if (x < 0){ x = -x; flags |= 1; } y -= viewport->view_y; if (y < 0){ y = -y; flags |= 2; } x = (x + 7) / 8; y = (y + 7) / 8; //If we are at the final zoom position if (!x && !y){ window->flags &= ~WF_SCROLLING_TO_LOCATION; } if (flags & 1){ x = -x; } if (flags & 2){ y = -y; } x += viewport->view_x; y += viewport->view_y; } sub_6E7DE1(x, y, window, viewport); } void viewport_update_sprite_follow(rct_window *window) { if (window->viewport_target_sprite != -1 && window->viewport){ rct_sprite* sprite = &g_sprite_list[window->viewport_target_sprite]; int height = (map_element_height(0xFFFF & sprite->unknown.x, 0xFFFF & sprite->unknown.y) & 0xFFFF) - 16; int underground = sprite->unknown.z < height; viewport_set_underground_flag(underground, window, window->viewport); int center_x, center_y; center_2d_coordinates(sprite->unknown.x, sprite->unknown.y, sprite->unknown.z, ¢er_x, ¢er_y, window->viewport); sub_6E7DE1(center_x, center_y, window, window->viewport); } } /** * * rct2: 0x00685C02 * ax: left * bx: top * dx: right * esi: viewport * edi: dpi * ebp: bottom */ void viewport_render(rct_drawpixelinfo *dpi, rct_viewport *viewport, int left, int top, int right, int bottom) { if (right <= viewport->x) return; if (bottom <= viewport->y) return; if (left >= viewport->x + viewport->width)return; if (top >= viewport->y + viewport->height)return; int l = left, t = top, r = right, b = bottom; left = max(left - viewport->x, 0); right = min(right - viewport->x, viewport->width); top = max(top - viewport->y, 0); bottom = min(bottom - viewport->y, viewport->height); left <<= viewport->zoom; right <<= viewport->zoom; top <<= viewport->zoom; bottom <<= viewport->zoom; left += viewport->view_x; right += viewport->view_x; top += viewport->view_y; bottom += viewport->view_y; int height = bottom - top; if (height > 384){ //Paint viewport_paint(viewport, dpi, left, top, right, top + 384); top += 384; } //Paint viewport_paint(viewport, dpi, left, top, right, bottom); #ifdef DEBUG_SHOW_DIRTY_BOX if (viewport != g_viewport_list){ gfx_fill_rect_inset(dpi, l, t, r-1, b-1, 0x2, 0x30); return; } #endif } /** * * rct2: 0x0068615B */ void painter_setup(){ RCT2_GLOBAL(0xEE7888, uint32) = 0x00EE788C; RCT2_GLOBAL(0xF1AD28, uint32) = 0; RCT2_GLOBAL(0xF1AD2C, uint32) = 0; uint8* edi = RCT2_ADDRESS(0xF1A50C, uint8); memset(edi, 0, 2048); RCT2_GLOBAL(0xF1AD0C, sint32) = -1; RCT2_GLOBAL(0xF1AD10, uint32) = 0; RCT2_GLOBAL(0xF1AD20, uint32) = 0; RCT2_GLOBAL(0xF1AD24, uint32) = 0; } /** * * rct2: 0x00688596 * Part of 0x688485 */ void paint_attached_ps(paint_struct* ps, paint_struct* attached_ps, rct_drawpixelinfo* dpi){ for (; attached_ps; attached_ps = attached_ps->next_attached_ps){ sint16 x = attached_ps->attached_x + ps->x; sint16 y = attached_ps->attached_y + ps->y; int image_id = attached_ps->image_id; if (gCurrentViewportFlags & VIEWPORT_FLAG_SEETHROUGH_RIDES) { if (ps->sprite_type == 3){ if (image_id & 0x40000000){ image_id &= 0x7FFFF; image_id |= 0x41880000; } } } if (gCurrentViewportFlags & VIEWPORT_FLAG_SEETHROUGH_SCENERY) { if (ps->sprite_type == 5){ if (image_id & 0x40000000){ image_id &= 0x7FFFF; image_id |= 0x41880000; } } } if (attached_ps->var_0C & 1) { gfx_draw_sprite_raw_masked(dpi, x, y, image_id, attached_ps->var_04); } else { gfx_draw_sprite(dpi, image_id, x, y, ps->var_04); } } } void sub_688485(){ rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); paint_struct* ps = RCT2_GLOBAL(0xEE7884, paint_struct*); paint_struct* previous_ps = ps->next_quadrant_ps; for (ps = ps->next_quadrant_ps; ps;){ sint16 x = ps->x; sint16 y = ps->y; if (ps->sprite_type == 2){ if (dpi->zoom_level >= 1){ x &= 0xFFFE; y &= 0xFFFE; if (dpi->zoom_level >= 2){ x &= 0xFFFC; y &= 0xFFFC; } } } int image_id = ps->image_id; if (gCurrentViewportFlags & VIEWPORT_FLAG_SEETHROUGH_RIDES) { if (ps->sprite_type == 3){ if (!(image_id & 0x40000000)){ image_id &= 0x7FFFF; image_id |= 0x41880000; } } } if (gCurrentViewportFlags & VIEWPORT_FLAG_UNDERGROUND_INSIDE) { if (ps->sprite_type == 9){ if (!(image_id & 0x40000000)){ image_id &= 0x7FFFF; image_id |= 0x41880000; } } } if (gCurrentViewportFlags & VIEWPORT_FLAG_SEETHROUGH_SCENERY) { if (ps->sprite_type == 10 || ps->sprite_type == 12 || ps->sprite_type == 9 || ps->sprite_type == 5){ if (!(image_id & 0x40000000)){ image_id &= 0x7FFFF; image_id |= 0x41880000; } } } if (ps->var_1A & 1) gfx_draw_sprite_raw_masked(dpi, x, y, image_id, ps->var_04); else gfx_draw_sprite(dpi, image_id, x, y, ps->var_04); if (ps->var_20 != 0){ ps = ps->var_20; continue; } paint_attached_ps(ps, ps->attached_ps, dpi); ps = previous_ps->next_quadrant_ps; previous_ps = ps; } } /** * * rct2: 0x006874B0, 0x00687618, 0x0068778C, 0x00687902, 0x0098199C * * @param image_id (ebx) * @param x_offset (al) * @param y_offset (cl) * @param bound_box_length_x (di) * @param bound_box_length_y (si) * @param bound_box_length_z (ah) * @param z_offset (dx) * @param bound_box_offset_x (0x009DEA52) * @param bound_box_offset_y (0x009DEA54) * @param bound_box_offset_z (0x009DEA56) * @param rotation (ebp) * @return (!CF) success */ bool sub_98199C( uint32 image_id, sint8 x_offset, sint8 y_offset, sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, uint16 z_offset, sint16 bound_box_offset_x, uint16 bound_box_offset_y, sint16 bound_box_offset_z, uint32 rotation ) { RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X, uint16) = bound_box_offset_x; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y, uint16) = bound_box_offset_y; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z, uint16) = bound_box_offset_z; int flags = RCT2_CALLPROC_X(RCT2_ADDRESS(0x98199C, uint32_t)[rotation], x_offset | (bound_box_length_z << 8), image_id, y_offset, z_offset, bound_box_length_y, bound_box_length_x, rotation); return !(flags & (1 << 8)); } /** * rct2: 0x006861AC, 0x00686337, 0x006864D0, 0x0068666B, 0x0098196C * * @param image_id (ebx) * @param x_offset (al) * @param y_offset (cl) * @param bound_box_length_x (di) * @param bound_box_length_y (si) * @param bound_box_length_z (ah) * @param z_offset (dx) * @param rotation (ebp) * @return (!CF) success */ bool sub_98196C( uint32 image_id, sint8 x_offset, sint8 y_offset, sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, uint16 z_offset, uint32 rotation ) { int flags = RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098196C, uint32)[rotation], x_offset | (bound_box_length_z << 8), image_id, y_offset, z_offset, bound_box_length_y, bound_box_length_x, rotation ); return !(flags & (1 << 8)); } /** * rct2: 0x00686806, 0x006869B2, 0x00686B6F, 0x00686D31, 0x0098197C * * @param image_id (ebx) * @param x_offset (al) * @param y_offset (cl) * @param bound_box_length_x (di) * @param bound_box_length_y (si) * @param bound_box_length_z (ah) * @param z_offset (dx) * @param bound_box_offset_x (0x009DEA52) * @param bound_box_offset_y (0x009DEA54) * @param bound_box_offset_z (0x009DEA56) * @param rotation (ebp) * @return (!CF) success */ bool sub_98197C( uint32 image_id, sint8 x_offset, sint8 y_offset, sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, uint16 z_offset, sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, uint32 rotation ) { int ebp = bound_box_length_z + bound_box_offset_z; RCT2_GLOBAL(0xF1AD28, paint_struct*) = 0; RCT2_GLOBAL(0xF1AD2C, uint32) = 0; //Not a paint struct but something similar paint_struct* ps = RCT2_GLOBAL(0xEE7888, paint_struct*); if ((uint32)ps >= RCT2_GLOBAL(0xEE7880, uint32))return false; ps->image_id = image_id; uint32 image_element = image_id & 0x7FFFF; rct_g1_element *g1Element = gfx_get_g1_element(image_element); rct_xyz16 coord_3d = { .x = x_offset, .y = y_offset, .z = z_offset }; switch (rotation) { case 0: rotate_map_coordinates(&coord_3d.x, &coord_3d.y, 0); break; case 1: rotate_map_coordinates(&coord_3d.x, &coord_3d.y, 3); break; case 2: rotate_map_coordinates(&coord_3d.x, &coord_3d.y, 2); break; case 3: rotate_map_coordinates(&coord_3d.x, &coord_3d.y, 1); break; } coord_3d.x += RCT2_GLOBAL(0x9DE568, sint16); coord_3d.y += RCT2_GLOBAL(0x9DE56C, sint16); rct_xy16 map = coordinate_3d_to_2d(&coord_3d, rotation); ps->x = map.x; ps->y = map.y; int left = map.x + g1Element->x_offset; int bottom = map.y + g1Element->y_offset; int right = left + g1Element->width; int top = bottom + g1Element->height; RCT2_GLOBAL(0xF1AD1C, uint16) = left; RCT2_GLOBAL(0xF1AD1E, uint16) = bottom; rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); if (right <= dpi->x)return false; if (top <= dpi->y)return false; if (left > dpi->x + dpi->width)return false; if (bottom > dpi->y + dpi->height)return false; rct_xy16 boundBox = { .x = bound_box_length_x, .y = bound_box_length_y }; rct_xy16 boundBoxOffset = { .x = bound_box_offset_x, .y = bound_box_offset_y }; // Unsure why rots 1 and 3 need to swap switch (rotation){ case 0: boundBox.x--; boundBox.y--; rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 0); rotate_map_coordinates(&boundBox.x, &boundBox.y, 0); break; case 1: boundBox.x--; rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 3); rotate_map_coordinates(&boundBox.x, &boundBox.y, 3); break; case 2: rotate_map_coordinates(&boundBox.x, &boundBox.y, 2); rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 2); break; case 3: boundBox.y--; rotate_map_coordinates(&boundBox.x, &boundBox.y, 1); rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 1); break; } ps->attached_x_end = boundBox.x + boundBoxOffset.x + RCT2_GLOBAL(0x9DE568, sint16); ps->attached_z = bound_box_offset_z; ps->attached_z_end = ebp; ps->attached_y_end = boundBox.y + boundBoxOffset.y + RCT2_GLOBAL(0x009DE56C, sint16); ps->var_1A = 0; ps->attached_x = boundBoxOffset.x + RCT2_GLOBAL(0x9DE568, sint16); ps->attached_y = boundBoxOffset.y + RCT2_GLOBAL(0x009DE56C, sint16); ps->attached_ps = NULL; ps->var_20 = NULL; ps->sprite_type = RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8); ps->var_29 = RCT2_GLOBAL(0x9DE571, uint8); ps->map_x = RCT2_GLOBAL(0x9DE574, uint16); ps->map_y = RCT2_GLOBAL(0x9DE576, uint16); ps->mapElement = RCT2_GLOBAL(0x9DE578, rct_map_element*); RCT2_GLOBAL(0xF1AD28, paint_struct*) = ps; rct_xy16 attach = { .x = ps->attached_x, .y = ps->attached_y }; rotate_map_coordinates(&attach.x, &attach.y, rotation); switch (rotation){ case 0: break; case 1: case 3: attach.x += 0x2000; break; case 2: attach.x += 0x4000; break; } sint16 di = attach.x + attach.y; if (di < 0) di = 0; di /= 32; if (di > 511) di = 511; ps->var_18 = di; paint_struct* old_ps = RCT2_ADDRESS(0x00F1A50C, paint_struct*)[di]; RCT2_ADDRESS(0x00F1A50C, paint_struct*)[di] = ps; ps->next_quadrant_ps = old_ps; if ((uint16)di < RCT2_GLOBAL(0x00F1AD0C, uint32)){ RCT2_GLOBAL(0x00F1AD0C, uint32) = di; } if ((uint16)di > RCT2_GLOBAL(0x00F1AD10, uint32)){ RCT2_GLOBAL(0x00F1AD10, uint32) = di; } RCT2_GLOBAL(0xEE7888, paint_struct*) += 1; return true; } /** * * rct2: 0x006D5DA9 */ static void vehicle_visual_roto_drop(int x, int imageDirection, int y, int z, rct_vehicle *vehicle, int rct2VehiclePtrFormat) { const rct_ride_entry_vehicle *vehicleEntry = (const rct_ride_entry_vehicle *)(rct2VehiclePtrFormat + 0x1A); int image_id; int baseImage_id = (vehicleEntry->base_image_id + 4) + ((vehicle->var_C5 / 4) & 0x3); if (vehicle->restraints_position >= 64) { baseImage_id += 7; baseImage_id += (vehicle->restraints_position / 64); } // Draw back: image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) | 0xA0000000; sub_98197C(image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1, get_current_rotation()); // Draw front: image_id = (baseImage_id + 4) | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) | 0xA0000000; sub_98197C(image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1, get_current_rotation()); uint8 riding_peep_sprites[64]; memset(riding_peep_sprites, 0xFF, sizeof(riding_peep_sprites)); for (int i = 0; i < vehicle->num_peeps; i++) { uint8 cl = (i & 3) * 16; cl += (i & 0xFC); cl += vehicle->var_C5 / 4; cl += (imageDirection / 8) * 16; cl &= 0x3F; riding_peep_sprites[cl] = vehicle->peep_tshirt_colours[i]; } // Draw riding peep sprites in back to front order: for (int j = 0; j <= 48; j++) { int i = (j % 2) ? (48 - (j / 2)) : (j / 2); if (riding_peep_sprites[i] != 0xFF) { baseImage_id = vehicleEntry->base_image_id + 20 + i; if (vehicle->restraints_position >= 64) { baseImage_id += 64; baseImage_id += vehicle->restraints_position / 64; } image_id = baseImage_id | (riding_peep_sprites[i] << 19) | 0x20000000; sub_98199C(image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1, get_current_rotation()); } }; assert(vehicleEntry->pad_5E == 1); // 0x5E is treated as another car_visual paint setup jump table like in viewport_vehicle_paint_setup // in the original code right here but appears to only ever be 1 which goes to a nullsub so it was taken out. } /** * * rct2: 0x00686EF0, 0x00687056, 0x006871C8, 0x0068733C, 0x0098198C * * @param image_id (ebx) * @param x_offset (al) * @param y_offset (cl) * @param bound_box_length_x (di) * @param bound_box_length_y (si) * @param bound_box_length_z (ah) * @param z_offset (dx) * @param bound_box_offset_x (0x009DEA52) * @param bound_box_offset_y (0x009DEA54) * @param bound_box_offset_z (0x009DEA56) * @param rotation * @return (!CF) success */ bool sub_98198C( uint32 image_id, sint8 x_offset, sint8 y_offset, sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, uint16 z_offset, sint16 bound_box_offset_x, uint16 bound_box_offset_y, sint16 bound_box_offset_z, uint32 rotation ) { RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X, uint16) = bound_box_offset_x; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y, uint16) = bound_box_offset_y; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z, uint16) = bound_box_offset_z; int flags = RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098198C, uint32)[rotation], x_offset | (bound_box_length_z << 8), image_id, y_offset, z_offset, bound_box_length_y, bound_box_length_x, rotation ); return !(flags & (1 << 8)); } /** * rct2: 68818E * * @param image_id (ebx) * @param x (ax) * @param y (cx) * @param[out] paint (ebp) * @return (!CF) success */ bool sub_68818E(uint32 image_id, uint8 x, uint8 y, paint_struct ** paint) { //Not a paint struct but something similar paint_struct * ps = RCT2_GLOBAL(0xEE7888, paint_struct *); if ((uint32) ps >= RCT2_GLOBAL(0xEE7880, uint32)) { return false; } ps->image_id = image_id; ps->attached_x = x; ps->attached_y = y; ps->var_0C = 0; paint_struct * ebx2 = RCT2_GLOBAL(0xF1AD28, paint_struct *); if (ebx2 == NULL) { return false; } RCT2_GLOBAL(0x00EE7888, uint32) += 0x12; paint_struct * edi = ebx2->attached_ps; ebx2->attached_ps = ps; ps->next_attached_ps = edi; RCT2_GLOBAL(0xF1AD2C, paint_struct *) = ps; if (paint != NULL) { *paint = ps; } return true; } /** * * rct2: 0x006D4244 */ void viewport_vehicle_paint_setup(rct_vehicle *vehicle, int imageDirection) { rct_ride_entry *rideEntry; const rct_ride_entry_vehicle *vehicleEntry; int x = vehicle->x; int y = vehicle->y; int z = vehicle->z; if (vehicle->flags & SPRITE_FLAGS_IS_CRASHED_VEHICLE_SPRITE) { uint32 ebx = 22965 + vehicle->var_C5; sub_98197C(ebx, 0, 0, 1, 1, 0, z, 0, 0, z + 2, get_current_rotation()); return; } if (vehicle->ride_subtype == 0xFF) { vehicleEntry = &CableLiftVehicle; } else { rideEntry = get_ride_entry(vehicle->ride_subtype); vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_11) { vehicleEntry++; z += 16; } } uint32 rct2VehiclePtrFormat = ((uint32)vehicleEntry) - offsetof(rct_ride_entry, vehicles); RCT2_GLOBAL(0x00F64DFC, uint32) = rct2VehiclePtrFormat; switch (vehicleEntry->car_visual) { case VEHICLE_VISUAL_DEFAULT: RCT2_CALLPROC_X(0x006D45F8, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_LAUNCHED_FREEFALL: RCT2_CALLPROC_X(0x006D5FAB, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_OBSERVATION_TOWER: RCT2_CALLPROC_X(0x006D6258, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_RIVER_RAPIDS: RCT2_CALLPROC_X(0x006D5889, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_MINI_GOLF_PLAYER: RCT2_CALLPROC_X(0x006D42F0, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_MINI_GOLF_BALL: RCT2_CALLPROC_X(0x006D43C6, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_REVERSER: RCT2_CALLPROC_X(0x006D4453, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_SPLASH_BOATS_OR_WATER_COASTER: RCT2_CALLPROC_X(0x006D4295, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_ROTO_DROP: vehicle_visual_roto_drop(x, imageDirection, y, z, vehicle, rct2VehiclePtrFormat); break; case 10: RCT2_CALLPROC_X(0x006D5600, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case 11: RCT2_CALLPROC_X(0x006D5696, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case 12: RCT2_CALLPROC_X(0x006D57EE, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case 13: RCT2_CALLPROC_X(0x006D5783, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case 14: RCT2_CALLPROC_X(0x006D5701, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_VIRGINIA_REEL: RCT2_CALLPROC_X(0x006D5B48, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; case VEHICLE_VISUAL_SUBMARINE: RCT2_CALLPROC_X(0x006D44D5, x, imageDirection, y, z, (int)vehicle, rct2VehiclePtrFormat, 0); break; } } /** * * rct2: 0x0068F0FB */ void viewport_peep_paint_setup(rct_peep *peep, int imageDirection) { RCT2_CALLPROC_X(0x0068F0FB, peep->x, imageDirection, peep->y, peep->z, (int)peep, 0, 0); } /** * * rct2: 0x00672AC9 */ void viewport_misc_paint_setup(rct_sprite *misc, int imageDirection) { RCT2_CALLPROC_X(0x00672AC9, misc->unknown.x, imageDirection, misc->unknown.y, misc->unknown.z, (int)misc, 0, 0); } /** * Litter Paint Setup * rct2: 0x006736FC */ void viewport_litter_paint_setup(rct_litter *litter, int imageDirection) { rct_drawpixelinfo *dpi; dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); if (dpi->zoom_level != 0) return; // If zoomed at all no litter drawn // litter has no sprite direction so remove that imageDirection >>= 3; // Some litter types have only 1 direction so remove // anything that isn't required. imageDirection &= RCT2_ADDRESS(0x97EF6C, uint32)[litter->type * 2 + 1]; uint32 image_id = imageDirection + RCT2_ADDRESS(0x97EF6C, uint32)[litter->type * 2]; sub_98197C(image_id, 0, 0, 4, 4, -1, litter->z, -4, -4, litter->z + 2, get_current_rotation()); } /** * Paint Quadrant * rct2: 0x0069E8B0 */ void sprite_paint_setup(const uint16 eax, const uint16 ecx){ rct_drawpixelinfo* dpi; if ((eax & 0xe000) | (ecx & 0xe000)) return; const int idx = ((eax << 3) & 0xff00) | (ecx >> 5); int sprite_idx = RCT2_ADDRESS(0xF1EF60, uint16)[idx]; if (sprite_idx == SPRITE_INDEX_NULL) return; if (RCT2_GLOBAL(0x9DEA6F, uint8) & 1) return; if (gCurrentViewportFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) return; dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); if (dpi->zoom_level > 2) return; for (rct_sprite* spr = &g_sprite_list[sprite_idx]; sprite_idx != SPRITE_INDEX_NULL; sprite_idx = spr->unknown.next_in_quadrant){ spr = &g_sprite_list[sprite_idx]; dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); if (dpi->y + dpi->height <= spr->unknown.sprite_top) continue; if (spr->unknown.sprite_bottom <= dpi->y)continue; if (dpi->x + dpi->width <= spr->unknown.sprite_left)continue; if (spr->unknown.sprite_right <= dpi->x)continue; int image_direction = get_current_rotation(); image_direction <<= 3; image_direction += spr->unknown.sprite_direction; image_direction &= 0x1F; RCT2_GLOBAL(0x9DE578, uint32) = (uint32)spr; RCT2_GLOBAL(0x9DE568, sint16) = spr->unknown.x; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_SPRITE; RCT2_GLOBAL(0x9DE56C, sint16) = spr->unknown.y; switch (spr->unknown.sprite_identifier){ case SPRITE_IDENTIFIER_VEHICLE: viewport_vehicle_paint_setup((rct_vehicle*)spr, image_direction); break; case SPRITE_IDENTIFIER_PEEP: viewport_peep_paint_setup((rct_peep*)spr, image_direction); break; case SPRITE_IDENTIFIER_MISC: viewport_misc_paint_setup(spr, image_direction); break; case SPRITE_IDENTIFIER_LITTER: viewport_litter_paint_setup((rct_litter*)spr, image_direction); break; default: assert(false); break; } } } /** * * rct2: 0x0066508C, 0x00665540 */ void viewport_ride_entrance_exit_paint_setup(uint8 direction, int height, rct_map_element* map_element) { rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); uint8 is_exit = map_element->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT; if (RCT2_GLOBAL(0x9DEA6F, uint8_t) & 1){ if (map_element->properties.entrance.ride_index != RCT2_GLOBAL(0x00F64DE8, uint8)) return; } rct_ride* ride = get_ride(map_element->properties.entrance.ride_index); if (ride->entrance_style == RIDE_ENTRANCE_STYLE_NONE) return; const rct_ride_entrance_definition *style = &RideEntranceDefinitions[ride->entrance_style]; uint8 colour_1, colour_2; uint32 transparant_image_id = 0, image_id = 0; if (style->flags & (1 << 30)) { colour_1 = ride->track_colour_main[0] + 0x70; transparant_image_id = (colour_1 << 19) | 0x40000000; } colour_1 = ride->track_colour_main[0]; colour_2 = ride->track_colour_additional[0]; image_id = (colour_1 << 19) | (colour_2 << 24) | 0xA0000000; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_RIDE; RCT2_GLOBAL(0x009E32BC, uint32) = 0; if (map_element->flags & MAP_ELEMENT_FLAG_GHOST){ RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_NONE; image_id = RCT2_ADDRESS(0x993CC4, uint32_t)[gConfigGeneral.construction_marker_colour]; RCT2_GLOBAL(0x009E32BC, uint32) = image_id; if (transparant_image_id) transparant_image_id = image_id; } if (is_exit){ image_id |= style->sprite_index + direction + 8; } else{ image_id |= style->sprite_index + direction; } // Format modifed to stop repeated code // Each entrance is split into 2 images for drawing // Certain entrance styles have another 2 images to draw for coloured windows sint8 ah = is_exit ? 0x23 : 0x33; sint16 lengthY = (direction & 1) ? 28 : 2; sint16 lengthX = (direction & 1) ? 2 : 28; sub_98197C(image_id, 0, 0, lengthX, lengthY, ah, height, 2, 2, height, get_current_rotation()); if (transparant_image_id){ if (is_exit){ transparant_image_id |= style->sprite_index + direction + 24; } else{ transparant_image_id |= style->sprite_index + direction + 16; } sub_98199C(transparant_image_id, 0, 0, lengthX, lengthY, ah, height, 2, 2, height, get_current_rotation()); } image_id += 4; sub_98197C(image_id, 0, 0, lengthX, lengthY, ah, height, (direction & 1) ? 28 : 2, (direction & 1) ? 2 : 28, height, get_current_rotation()); if (transparant_image_id){ transparant_image_id += 4; sub_98199C(transparant_image_id, 0, 0, lengthX, lengthY, ah, height, (direction & 1) ? 28 : 2, (direction & 1) ? 2 : 28, height, get_current_rotation()); } uint32 eax = 0xFFFF0600 | ((height / 16) & 0xFF); if (direction & 1){ RCT2_ADDRESS(0x009E30B6, uint32)[RCT2_GLOBAL(0x141F56B, uint8) / 2] = eax; RCT2_GLOBAL(0x141F56B, uint8)++; } else{ RCT2_ADDRESS(0x009E3138, uint32)[RCT2_GLOBAL(0x141F56A, uint8) / 2] = eax; RCT2_GLOBAL(0x141F56A, uint8)++; } if (!is_exit && !(map_element->flags & MAP_ELEMENT_FLAG_GHOST) && map_element->properties.entrance.ride_index != 0xFF){ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) = 0; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 4, uint32) = 0; rct_string_id string_id = STR_RIDE_ENTRANCE_CLOSED; if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)){ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, rct_string_id) = ride->name; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint32) = ride->name_arguments; string_id = STR_RIDE_ENTRANCE_NAME; } utf8 entrance_string[MAX_PATH]; if (gConfigGeneral.upper_case_banners) { format_string_to_upper(entrance_string, string_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } else { format_string(entrance_string, string_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; uint16 string_width = gfx_get_string_width(entrance_string); uint16 scroll = (gCurrentTicks / 2) % string_width; sub_98199C(scrolling_text_setup(string_id, scroll, style->scrolling_mode), 0, 0, 0x1C, 0x1C, 0x33, height + style->height, 2, 2, height + style->height, get_current_rotation()); } image_id = RCT2_GLOBAL(0x009E32BC, uint32); if (image_id == 0) { image_id = SPRITE_ID_PALETTE_COLOUR_1(COLOUR_SATURATED_BROWN); } wooden_a_supports_paint_setup(direction & 1, 0, height, image_id, NULL); RCT2_GLOBAL(0x141E9B4, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9B8, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9BC, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C0, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C4, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C8, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9CC, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9D0, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9D4, uint16) = 0xFFFF; height += is_exit ? 40 : 56; if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) < height){ RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) = height; RCT2_GLOBAL(0x141E9DA, uint8) = 32; } } /** * * rct2: 0x006658ED */ void viewport_park_entrance_paint_setup(uint8 direction, int height, rct_map_element* map_element){ if (RCT2_GLOBAL(0x9DEA6F, uint8_t) & 1) return; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_PARK; RCT2_GLOBAL(0x009E32BC, uint32) = 0; uint32 image_id, ghost_id = 0; if (map_element->flags & MAP_ELEMENT_FLAG_GHOST){ RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_NONE; ghost_id = RCT2_ADDRESS(0x993CC4, uint32)[gConfigGeneral.construction_marker_colour]; RCT2_GLOBAL(0x009E32BC, uint32) = ghost_id; } rct_path_type* path_entry = g_pathTypeEntries[map_element->properties.entrance.path_type]; // Index to which part of the entrance // Middle, left, right uint8 part_index = map_element->properties.entrance.index & 0xF; rct_entrance_type* entrance; uint8 di = (direction / 2 + part_index / 2) & 1 ? 0x1A : 0x20; switch (part_index){ case 0: image_id = (path_entry->image + 5 * (1 + (direction & 1))) | ghost_id; sub_98197C(image_id, 0, 0, 32, 0x1C, 0, height, 0, 2, height, get_current_rotation()); entrance = (rct_entrance_type*)object_entry_groups[OBJECT_TYPE_PARK_ENTRANCE].chunks[0]; image_id = (entrance->image_id + direction * 3) | ghost_id; sub_98197C(image_id, 0, 0, 0x1C, 0x1C, 0x2F, height, 2, 2, height + 32, get_current_rotation()); if ((direction + 1) & (1 << 1)) break; if (ghost_id != 0) break; rct_string_id park_text_id = 1730; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) = 0; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 4, uint32) = 0; if (gParkFlags & PARK_FLAGS_PARK_OPEN){ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, rct_string_id) = gParkName; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint32) = gParkNameArgs; park_text_id = 1731; } utf8 park_name[MAX_PATH]; if (gConfigGeneral.upper_case_banners) { format_string_to_upper(park_name, park_text_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } else { format_string(park_name, park_text_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; uint16 string_width = gfx_get_string_width(park_name); uint16 scroll = (gCurrentTicks / 2) % string_width; if (entrance->scrolling_mode == 0xFF) break; sub_98199C(scrolling_text_setup(park_text_id, scroll, entrance->scrolling_mode + direction / 2), 0, 0, 0x1C, 0x1C, 0x2F, height + entrance->text_height, 2, 2, height + entrance->text_height, get_current_rotation()); break; case 1: case 2: entrance = (rct_entrance_type*)object_entry_groups[OBJECT_TYPE_PARK_ENTRANCE].chunks[0]; image_id = (entrance->image_id + part_index + direction * 3) | ghost_id; sub_98197C(image_id, 0, 0, 0x1A, di, 0x4F, height, 3, 3, height, get_current_rotation()); break; } image_id = ghost_id; if (image_id == 0) { image_id = SPRITE_ID_PALETTE_COLOUR_1(COLOUR_SATURATED_BROWN); } wooden_a_supports_paint_setup(direction & 1, 0, height, image_id, NULL); RCT2_GLOBAL(0x141E9B4, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9B8, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9BC, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C0, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C4, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9C8, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9CC, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9D0, uint16) = 0xFFFF; RCT2_GLOBAL(0x141E9D4, uint16) = 0xFFFF; height += 80; if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) < height){ RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) = height; RCT2_GLOBAL(0x141E9DA, uint8) = 32; } } /** * * rct2: 0x006C4794 */ void viewport_track_paint_setup(uint8 direction, int height, rct_map_element *mapElement) { rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); rct_ride *ride; int rideIndex, trackType, trackColourScheme, trackSequence; rideIndex = mapElement->properties.track.ride_index; ride = get_ride(rideIndex); if (ride->type == RIDE_TYPE_NULL) { log_error("Attempted to paint invalid ride: %d", rideIndex); return; } // HACK Set entrance style to plain if none to stop glitch until entrance track piece is implemented bool isEntranceStyleNone = false; if (ride->entrance_style == RIDE_ENTRANCE_STYLE_NONE) { isEntranceStyleNone = true; ride->entrance_style = RIDE_ENTRANCE_STYLE_PLAIN; } if (!(RCT2_GLOBAL(0x009DEA6F, uint8) & 1) || rideIndex == RCT2_GLOBAL(0x00F64DE8, uint8)) { trackType = mapElement->properties.track.type; trackSequence = mapElement->properties.track.sequence & 0x0F; trackColourScheme = mapElement->properties.track.colour & 3; if ((gCurrentViewportFlags & VIEWPORT_FLAG_TRACK_HEIGHTS) && dpi->zoom_level == 0) { RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = 0; if (RCT2_ADDRESS(0x00999694, uint32)[trackType] & (1 << trackSequence)) { uint16 ax = RideData5[ride->type].z_offset; uint32 ebx = 0x20381689 + (height + 8) / 16; ebx += RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_HEIGHT_MARKERS, uint16); ebx -= RCT2_GLOBAL(0x01359208, uint16); sub_98197C(ebx, 16, 16, 1, 1, 0, height + ax + 3, 1000, 1000, 2047, get_current_rotation()); } } RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = 3; RCT2_GLOBAL(0x00F44198, uint32) = (ride->track_colour_main[trackColourScheme] << 19) | (ride->track_colour_additional[trackColourScheme] << 24) | 0xA0000000; RCT2_GLOBAL(0x00F441A0, uint32) = 0x20000000; RCT2_GLOBAL(0x00F441A4, uint32) = 0x20C00000; RCT2_GLOBAL(0x00F4419C, uint32) = (ride->track_colour_supports[trackColourScheme] << 19) | 0x20000000; if (mapElement->type & 0x40) { RCT2_GLOBAL(0x00F44198, uint32) = 0x21600000; RCT2_GLOBAL(0x00F4419C, uint32) = 0x21600000; RCT2_GLOBAL(0x00F441A0, uint32) = 0x21600000; RCT2_GLOBAL(0x00F441A4, uint32) = 0x21600000; } if (mapElement->flags & MAP_ELEMENT_FLAG_GHOST) { uint32 ghost_id = RCT2_ADDRESS(0x00993CC4, uint32)[gConfigGeneral.construction_marker_colour]; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = 0; RCT2_GLOBAL(0x00F44198, uint32) = ghost_id; RCT2_GLOBAL(0x00F4419C, uint32) = ghost_id; RCT2_GLOBAL(0x00F441A0, uint32) = ghost_id; RCT2_GLOBAL(0x00F441A4, uint32) = ghost_id; } int rideType = ride->type; if (rideType == RIDE_TYPE_JUNIOR_ROLLER_COASTER) { switch (trackType) { case TRACK_ELEM_60_DEG_UP: case TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP: case TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP: case TRACK_ELEM_60_DEG_DOWN: case TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN: case TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN: rideType = RIDE_TYPE_WATER_COASTER; break; case TRACK_ELEM_FLAT_TO_60_DEG_UP: case TRACK_ELEM_60_DEG_UP_TO_FLAT: case TRACK_ELEM_FLAT_TO_60_DEG_DOWN: case TRACK_ELEM_60_DEG_DOWN_TO_FLAT: return; } } TRACK_PAINT_FUNCTION **trackTypeList = (TRACK_PAINT_FUNCTION**)RideTypeTrackPaintFunctionsOld[rideType]; if (trackTypeList == NULL) { TRACK_PAINT_FUNCTION_GETTER paintFunctionGetter = RideTypeTrackPaintFunctions[rideType]; TRACK_PAINT_FUNCTION paintFunction = paintFunctionGetter(trackType, direction); if (paintFunction != NULL) { paintFunction(rideIndex, trackSequence, direction, height, mapElement); } } else { uint32 *trackDirectionList = (uint32*)trackTypeList[trackType]; // Have to call from this point as it pushes esi and expects callee to pop it RCT2_CALLPROC_X( 0x006C4934, rideType, (int)trackDirectionList, direction, height, (int)mapElement, rideIndex * sizeof(rct_ride), trackSequence ); } } if (isEntranceStyleNone) { ride->entrance_style = RIDE_ENTRANCE_STYLE_NONE; } } /** * * rct2: 0x00664FD4 */ void viewport_entrance_paint_setup(uint8 direction, int height, rct_map_element* map_element){ RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8_t) = VIEWPORT_INTERACTION_ITEM_LABEL; rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); if (gCurrentViewportFlags & VIEWPORT_FLAG_PATH_HEIGHTS && dpi->zoom_level == 0){ uint32 ebx = (map_element->properties.entrance.type << 4) | (map_element->properties.entrance.index & 0xF); if (RCT2_ADDRESS(0x0097B974, uint8)[ebx] & 0xF){ int z = map_element->base_height * 8 + 3; uint32 image_id = z / 16 + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_HEIGHT_MARKERS,sint16) + 0x20101689; image_id -= RCT2_GLOBAL(0x01359208, sint16); sub_98197C(image_id, 16, 16, 1, 1, 0, height, 31, 31, z + 64, get_current_rotation()); } } switch (map_element->properties.entrance.type){ case ENTRANCE_TYPE_RIDE_ENTRANCE: case ENTRANCE_TYPE_RIDE_EXIT: viewport_ride_entrance_exit_paint_setup(direction, height, map_element); break; case ENTRANCE_TYPE_PARK_ENTRANCE: viewport_park_entrance_paint_setup(direction, height, map_element); break; } } /** * * rct2: 0x006B9CC4 */ void viewport_banner_paint_setup(uint8 direction, int height, rct_map_element* map_element) { uint16 boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ; rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8_t) = VIEWPORT_INTERACTION_ITEM_BANNER; if (dpi->zoom_level > 1 || RCT2_GLOBAL(0x9DEA6F, uint8_t) & 1) return; height -= 16; rct_scenery_entry* banner_scenery = g_bannerSceneryEntries[gBanners[map_element->properties.banner.index].type]; direction += map_element->properties.banner.position; direction &= 3; boundBoxOffsetX = RCT2_ADDRESS(0x98D884, uint16)[direction * 4]; boundBoxOffsetY = RCT2_ADDRESS(0x98D884 + 2, uint16)[direction * 4]; boundBoxOffsetZ = height + 2; uint32 base_id = (direction << 1) + banner_scenery->image; uint32 image_id = base_id; if (map_element->flags & MAP_ELEMENT_FLAG_GHOST)//if being placed { RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8_t) = VIEWPORT_INTERACTION_ITEM_NONE; image_id |= RCT2_ADDRESS(0x993CC4, uint32_t)[gConfigGeneral.construction_marker_colour]; } else{ image_id |= (gBanners[map_element->properties.banner.index].colour << 19) | 0x20000000; } sub_98197C(image_id, 0, 0, 1, 1, 0x15, height, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ, get_current_rotation()); boundBoxOffsetX = RCT2_ADDRESS(0x98D888, uint16)[direction * 4]; boundBoxOffsetY = RCT2_ADDRESS(0x98D888 + 2, uint16)[direction * 4]; image_id++; sub_98197C(image_id, 0, 0, 1, 1, 0x15, height, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ, get_current_rotation()); // Opposite direction direction ^= 2; direction--; // If text not showing / ghost if (direction >= 2 || (map_element->flags & MAP_ELEMENT_FLAG_GHOST)) return; uint16 scrollingMode = banner_scenery->banner.scrolling_mode; scrollingMode += direction; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) = 0; RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 4, uint32_t) = 0; rct_string_id string_id = STR_NO_ENTRY; if (!(gBanners[map_element->properties.banner.index].flags & BANNER_FLAG_NO_ENTRY)) { RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = gBanners[map_element->properties.banner.index].string_idx; string_id = STR_BANNER_TEXT; } if (gConfigGeneral.upper_case_banners) { format_string_to_upper(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char), string_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } else { format_string(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char), string_id, RCT2_ADDRESS(RCT2_ADDRESS_COMMON_FORMAT_ARGS, void)); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; uint16 string_width = gfx_get_string_width(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char)); uint16 scroll = (gCurrentTicks / 2) % string_width; sub_98199C(scrolling_text_setup(string_id, scroll, scrollingMode), 0, 0, 1, 1, 0x15, height + 22, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ, get_current_rotation()); } /** * * rct2: 0x0068B3FB */ static void sub_68B3FB(int x, int y) { rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); RCT2_GLOBAL(0x141F56A, uint16_t) = 0; RCT2_GLOBAL(0x9E3138, uint8_t) = 0xFF; RCT2_GLOBAL(0x9E30B6, uint8_t) = 0xFF; RCT2_GLOBAL(0x9E323C, uint8_t) = 0xFF; RCT2_GLOBAL(0x9DE56A, uint16_t) = x; RCT2_GLOBAL(0x9DE56E, uint16_t) = y; RCT2_GLOBAL(0x9DE574, uint16_t) = x; RCT2_GLOBAL(0x9DE576, uint16_t) = y; rct_map_element* map_element = map_get_first_element_at(x >> 5, y >> 5); uint8 rotation = get_current_rotation(); int dx = 0; switch (rotation) { case 0: dx = x + y; break; case 1: x += 32; dx = y - x; break; case 2: x += 32; y += 32; dx = -(x + y); break; case 3: y += 32; dx = x - y; break; } dx >>= 1; // Display little yellow arrow when building footpaths? if ((RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint16) & 4) && RCT2_GLOBAL(0x9DE56A, uint16) == RCT2_GLOBAL(RCT2_ADDRESS_MAP_ARROW_X, uint16) && RCT2_GLOBAL(0x9DE56E, uint16) == RCT2_GLOBAL(RCT2_ADDRESS_MAP_ARROW_Y, uint16)){ uint8 arrowRotation = (rotation + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_ARROW_DIRECTION, uint8) & 3)) & 3; uint32 imageId = arrowRotation + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_ARROW_DIRECTION, uint8) & 0xFC) + 0x20900C27; int arrowZ = RCT2_GLOBAL(RCT2_ADDRESS_MAP_ARROW_Z, uint16); RCT2_GLOBAL(0x9DE568, sint16) = x; RCT2_GLOBAL(0x9DE56C, sint16) = y; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8) = VIEWPORT_INTERACTION_ITEM_NONE; sub_98197C(imageId, 0, 0, 32, 32, 0xFF, arrowZ, 0, 0, arrowZ + 18, rotation); } int bx = dx + 52; if (bx <= dpi->y) return; const rct_map_element* element = map_element;//push map_element sint16 max_height = 0; do{ max_height = max(max_height, element->clearance_height); } while (!map_element_is_last_for_tile(element++)); element--; if (map_element_get_type(element) == MAP_ELEMENT_TYPE_SURFACE && (element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK) != 0){ max_height = (element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK) << 1; } max_height *= 8; dx -= max_height + 32; element = map_element;//pop map_element dx -= dpi->height; if (dx >= dpi->y) return; RCT2_GLOBAL(0x9DE568, sint16) = x; RCT2_GLOBAL(0x9DE56C, sint16) = y; RCT2_GLOBAL(0x9DE57C, uint16) = 0; do { int direction = (map_element->type + rotation) & MAP_ELEMENT_DIRECTION_MASK; int height = map_element->base_height * 8; uint32_t dword_9DE574 = RCT2_GLOBAL(0x9DE574, uint32_t); RCT2_GLOBAL(0x9DE578, rct_map_element*) = map_element; //setup the painting of for example: the underground, signs, rides, scenery, etc. switch (map_element_get_type(map_element)) { case MAP_ELEMENT_TYPE_SURFACE: viewport_surface_paint_setup(direction, height, map_element); break; case MAP_ELEMENT_TYPE_PATH: RCT2_CALLPROC_X(0x6A3590, 0, 0, direction, height, (int)map_element, 0, 0); break; case MAP_ELEMENT_TYPE_TRACK: viewport_track_paint_setup(direction, height, map_element); break; case MAP_ELEMENT_TYPE_SCENERY: //RCT2_CALLPROC_X(0x6DFF47, 0, 0, direction, height, (int)map_element, 0, 0); break; case MAP_ELEMENT_TYPE_ENTRANCE: viewport_entrance_paint_setup(direction, height, map_element); break; case MAP_ELEMENT_TYPE_FENCE: //RCT2_CALLPROC_X(0x6E44B0, 0, 0, direction, height, (int)map_element, 0, 0); break; case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE: RCT2_CALLPROC_X(0x6B7F0C, 0, 0, direction, height, (int)map_element, 0, 0); break; case MAP_ELEMENT_TYPE_BANNER: viewport_banner_paint_setup(direction, height, map_element); break; // A corrupt element inserted by OpenRCT2 itself, which skips the drawing of the next element only. case MAP_ELEMENT_TYPE_CORRUPT: if (map_element_is_last_for_tile(map_element)) return; map_element++; break; default: // An undefined map element is most likely a corrupt element inserted by 8 cars' MOM feature to skip drawing of all elements after it. return; } RCT2_GLOBAL(0x9DE574, uint32_t) = dword_9DE574; } while (!map_element_is_last_for_tile(map_element++)); } /** * * rct2: 0x0068B60E */ static void viewport_blank_tiles_paint_setup(int x, int y) { rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); int dx; switch (get_current_rotation()) { case 0: dx = x + y; break; case 1: x += 32; dx = y - x; break; case 2: x += 32; y += 32; dx = -(x + y); break; case 3: y += 32; dx = x - y; break; } dx /= 2; dx -= 16; int bx = dx + 32; if (bx <= dpi->y) return; dx -= 20; dx -= dpi->height; if (dx >= dpi->y) return; RCT2_GLOBAL(0x9DE568, sint16) = x; RCT2_GLOBAL(0x9DE56C, sint16) = y; RCT2_GLOBAL(RCT2_ADDRESS_PAINT_SETUP_CURRENT_TYPE, uint8_t) = VIEWPORT_INTERACTION_ITEM_NONE; RCT2_CALLPROC_X( (int)RCT2_ADDRESS(0x98196C, uint32_t*)[get_current_rotation()], 0xFF00, 3123, y & 0xFF00, 16, 32, 32, get_current_rotation() ); } /** * * rct2: 0x0068B2B7 */ void sub_68B2B7(int x, int y) { if ( x < gMapSizeUnits && y < gMapSizeUnits && x >= 32 && y >= 32 ) { RCT2_GLOBAL(0x0141E9B4, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9B8, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9BC, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C0, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C4, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C8, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9CC, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9D0, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9D4, uint32) = 0xFFFF; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) = -1; RCT2_GLOBAL(0x0141E9DA, uint16) = 0; RCT2_GLOBAL(0x0141E9DC, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9DB, uint8) |= 2; sub_68B3FB(x, y); } else { viewport_blank_tiles_paint_setup(x, y); } } /** * * rct2: 0x0068B35F */ void map_element_paint_setup(int x, int y) { rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); if ( x < gMapSizeUnits && y < gMapSizeUnits && x >= 32 && y >= 32 ) { RCT2_GLOBAL(0x0141E9B4, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9B8, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9BC, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C0, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C4, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9C8, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9CC, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9D0, uint32) = 0xFFFF; RCT2_GLOBAL(0x0141E9D4, uint32) = 0xFFFF; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PAINT_TILE_MAX_HEIGHT, sint16) = -1; RCT2_GLOBAL(0x0141E9DA, sint16) = 0; RCT2_GLOBAL(0x0141E9DC, uint32) = 0xFFFF; sub_68B3FB(x, y); } else { viewport_blank_tiles_paint_setup(x, y); } } /** * * rct2: 0x0068B6C2 */ void viewport_paint_setup() { rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*); rct_xy16 mapTile = { .x = dpi->x & 0xFFE0, .y = (dpi->y - 16) & 0xFFE0 }; sint16 half_x = mapTile.x >> 1; uint16 num_vertical_quadrants = (dpi->height + 2128) >> 5; switch (get_current_rotation()){ case 0: mapTile.x = mapTile.y - half_x; mapTile.y = mapTile.y + half_x; mapTile.x &= 0xFFE0; mapTile.y &= 0xFFE0; for (; num_vertical_quadrants > 0; --num_vertical_quadrants){ map_element_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x - 32, mapTile.y + 32); map_element_paint_setup(mapTile.x, mapTile.y + 32); sprite_paint_setup(mapTile.x, mapTile.y + 32); mapTile.x += 32; sprite_paint_setup(mapTile.x, mapTile.y); mapTile.y += 32; } break; case 1: mapTile.x = -mapTile.y - half_x; mapTile.y = mapTile.y - half_x - 16; mapTile.x &= 0xFFE0; mapTile.y &= 0xFFE0; for (; num_vertical_quadrants > 0; --num_vertical_quadrants){ map_element_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x - 32, mapTile.y - 32); map_element_paint_setup(mapTile.x - 32, mapTile.y); sprite_paint_setup(mapTile.x - 32, mapTile.y); mapTile.y += 32; sprite_paint_setup(mapTile.x, mapTile.y); mapTile.x -= 32; } break; case 2: mapTile.x = -mapTile.y + half_x; mapTile.y = -mapTile.y - half_x; mapTile.x &= 0xFFE0; mapTile.y &= 0xFFE0; for (; num_vertical_quadrants > 0; --num_vertical_quadrants){ map_element_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x + 32, mapTile.y - 32); map_element_paint_setup(mapTile.x, mapTile.y - 32); sprite_paint_setup(mapTile.x, mapTile.y - 32); mapTile.x -= 32; sprite_paint_setup(mapTile.x, mapTile.y); mapTile.y -= 32; } break; case 3: mapTile.x = mapTile.y + half_x; mapTile.y = -mapTile.y + half_x - 16; mapTile.x &= 0xFFE0; mapTile.y &= 0xFFE0; for (; num_vertical_quadrants > 0; --num_vertical_quadrants){ map_element_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x, mapTile.y); sprite_paint_setup(mapTile.x + 32, mapTile.y + 32); map_element_paint_setup(mapTile.x + 32, mapTile.y); sprite_paint_setup(mapTile.x + 32, mapTile.y); mapTile.y -= 32; sprite_paint_setup(mapTile.x, mapTile.y); mapTile.x += 32; } break; } } void sub_688217_helper(uint16 ax, uint8 flag) { paint_struct *ps, *ps_temp; paint_struct *ps_next = RCT2_GLOBAL(0x00EE7884, paint_struct*); do { ps = ps_next; ps_next = ps_next->next_quadrant_ps; if (ps_next == NULL) return; } while (ax > ps_next->var_18); ps_temp = ps; do { ps = ps->next_quadrant_ps; if (ps == NULL) break; if (ps->var_18 > ax + 1) { ps->var_1B = 1 << 7; } else if (ps->var_18 == ax + 1) { ps->var_1B = (1 << 1) | (1 << 0); } else if (ps->var_18 == ax) { ps->var_1B = flag | (1 << 0); } } while (ps->var_18 <= ax + 1); ps = ps_temp; uint8 rotation = get_current_rotation(); while (true) { while (true) { ps_next = ps->next_quadrant_ps; if (ps_next == NULL) return; if (ps_next->var_1B & (1 << 7)) return; if (ps_next->var_1B & (1 << 0)) break; ps = ps_next; } ps_next->var_1B &= ~(1 << 0); ps_temp = ps; uint16 my_attached_x = ps_next->attached_x; uint16 my_attached_y = ps_next->attached_y; uint16 my_some_x = ps_next->attached_z; uint16 my_some_y = ps_next->attached_z_end; uint16 my_other_x = ps_next->attached_x_end; uint16 my_other_y = ps_next->attached_y_end; while (true) { ps = ps_next; ps_next = ps_next->next_quadrant_ps; if (ps_next == NULL) break; if (ps_next->var_1B & (1 << 7)) break; if (!(ps_next->var_1B & (1 << 1))) continue; int yes = 0; switch (rotation) { case 0: if (my_some_y >= ps_next->attached_z && my_other_y >= ps_next->attached_y && my_other_x >= ps_next->attached_x && !(my_some_x < ps_next->attached_z_end && my_attached_y < ps_next->attached_y_end && my_attached_x < ps_next->attached_x_end)) yes = 1; break; case 1: if (my_some_y >= ps_next->attached_z && my_other_y >= ps_next->attached_y && my_other_x < ps_next->attached_x && !(my_some_x < ps_next->attached_z_end && my_attached_y < ps_next->attached_y_end && my_attached_x >= ps_next->attached_x_end)) yes = 1; break; case 2: if (my_some_y >= ps_next->attached_z && my_other_y < ps_next->attached_y && my_other_x < ps_next->attached_x && !(my_some_x < ps_next->attached_z_end && my_attached_y >= ps_next->attached_y_end && my_attached_x >= ps_next->attached_x_end)) yes = 1; break; case 3: if (my_some_y >= ps_next->attached_z && my_other_y < ps_next->attached_y && my_other_x >= ps_next->attached_x && !(my_some_x < ps_next->attached_z_end && my_attached_y >= ps_next->attached_y_end && my_attached_x < ps_next->attached_x_end)) yes = 1; break; } if (yes) { ps->next_quadrant_ps = ps_next->next_quadrant_ps; paint_struct *ps_temp2 = ps_temp->next_quadrant_ps; ps_temp->next_quadrant_ps = ps_next; ps_next->next_quadrant_ps = ps_temp2; ps_next = ps; } } ps = ps_temp; } } /** * * rct2: 0x00688217 */ void sub_688217() { paint_struct *ps = RCT2_GLOBAL(0x00EE7888, paint_struct*); paint_struct *ps_next; RCT2_GLOBAL(0x00EE7888, uint32) += 0x34; // 0x34 is size of paint_struct? RCT2_GLOBAL(0x00EE7884, paint_struct*) = ps; ps->next_quadrant_ps = NULL; uint32 edi = RCT2_GLOBAL(0x00F1AD0C, uint32); if (edi == -1) return; do { ps_next = RCT2_GLOBAL(0x00F1A50C + 4 * edi, paint_struct*); if (ps_next != NULL) { ps->next_quadrant_ps = ps_next; do { ps = ps_next; ps_next = ps_next->next_quadrant_ps; } while (ps_next != NULL); } } while (++edi <= RCT2_GLOBAL(0x00F1AD10, uint32)); uint32 eax = RCT2_GLOBAL(0x00F1AD0C, uint32); sub_688217_helper(eax & 0xFFFF, 1 << 1); eax = RCT2_GLOBAL(0x00F1AD0C, uint32); while (++eax < RCT2_GLOBAL(0x00F1AD10, uint32)) sub_688217_helper(eax & 0xFFFF, 0); } typedef struct paint_string_struct paint_string_struct; struct paint_string_struct { rct_string_id string_id; // 0x00 paint_string_struct *next; // 0x02 uint16 x; // 0x06 uint16 y; // 0x08 uint8 args[16]; // 0x0A uint8 *y_offsets; // 0x1A }; static void draw_pixel_info_crop_by_zoom(rct_drawpixelinfo *dpi) { int zoom = dpi->zoom_level; dpi->zoom_level = 0; dpi->x >>= zoom; dpi->y >>= zoom; dpi->width >>= zoom; dpi->height >>= zoom; } /** * * rct2: 0x006860C3 */ static void viewport_draw_money_effects() { utf8 buffer[256]; paint_string_struct *ps = RCT2_GLOBAL(0x00F1AD20, paint_string_struct*); if (ps == NULL) return; rct_drawpixelinfo dpi = *(RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*)); draw_pixel_info_crop_by_zoom(&dpi); do { format_string(buffer, ps->string_id, &ps->args); gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; bool forceSpriteFont = false; const currency_descriptor *currencyDesc = &CurrencyDescriptors[gConfigGeneral.currency_format]; if (gUseTrueTypeFont && font_supports_string_sprite(currencyDesc->symbol_unicode)) { forceSpriteFont = true; } gfx_draw_string_with_y_offsets(&dpi, buffer, 0, ps->x, ps->y, (sint8 *)ps->y_offsets, forceSpriteFont); } while ((ps = ps->next) != NULL); } /** * * rct2: 0x00685CBF * eax: left * ebx: top * edx: right * esi: viewport * edi: dpi * ebp: bottom */ void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int left, int top, int right, int bottom){ gCurrentViewportFlags = viewport->flags; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16) = viewport->zoom; uint16 width = right - left; uint16 height = bottom - top; uint16 bitmask = 0xFFFF & (0xFFFF << viewport->zoom); width &= bitmask; height &= bitmask; left &= bitmask; top &= bitmask; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, sint16) = left; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, sint16) = top; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_WIDTH, uint16) = width; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_HEIGHT, uint16) = height; width >>= viewport->zoom; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_PITCH, uint16) = (dpi->width + dpi->pitch) - width; sint16 x = (sint16)(left - (sint16)(viewport->view_x & bitmask)); x >>= viewport->zoom; x += viewport->x; sint16 y = (sint16)(top - (sint16)(viewport->view_y & bitmask)); y >>= viewport->zoom; y += viewport->y; uint8* bits_pointer = x - dpi->x + (y - dpi->y)*(dpi->width + dpi->pitch) + dpi->bits; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_BITS_PTR, uint8*) = bits_pointer; rct_drawpixelinfo* dpi2 = RCT2_ADDRESS(RCT2_ADDRESS_VIEWPORT_DPI, rct_drawpixelinfo); dpi2->y = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, sint16); dpi2->height = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_HEIGHT, uint16); dpi2->zoom_level = (uint8)RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16); //Splits the screen into 32 pixel columns and renders them. for (x = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, sint16) & 0xFFFFFFE0; x < RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, sint16) + RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_WIDTH, uint16); x += 32){ int start_x = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, sint16); int width_col = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_WIDTH, uint16); bits_pointer = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_BITS_PTR, uint8*); int pitch = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_PITCH, uint16); int zoom = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16); if (x >= start_x){ int left_pitch = x - start_x; width_col -= left_pitch; bits_pointer += left_pitch >> zoom; pitch += left_pitch >> zoom; start_x = x; } int paint_right = start_x + width_col; if (paint_right >= x + 32){ int right_pitch = paint_right - x - 32; paint_right -= right_pitch; pitch += right_pitch >> zoom; } width_col = paint_right - start_x; dpi2->x = start_x; dpi2->width = width_col; dpi2->bits = bits_pointer; dpi2->pitch = pitch; if (gCurrentViewportFlags & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE)){ uint8 colour = 0x0A; if (gCurrentViewportFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES){ colour = 0; } gfx_clear(dpi2, colour); } RCT2_GLOBAL(0xEE7880, uint32) = 0xF1A4CC; RCT2_GLOBAL(0x140E9A8, uint32) = (int)dpi2; int ebp = 0, ebx = 0, esi = 0, ecx = 0; painter_setup(); viewport_paint_setup(); sub_688217(); sub_688485(); int weather_colour = RCT2_ADDRESS(0x98195C, uint32)[gClimateCurrentWeatherGloom]; if ((weather_colour != -1) && (!(gCurrentViewportFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES)) && (!(RCT2_GLOBAL(0x9DEA6F, uint8) & 1))){ gfx_fill_rect(dpi2, dpi2->x, dpi2->y, dpi2->width + dpi2->x - 1, dpi2->height + dpi2->y - 1, weather_colour); } viewport_draw_money_effects(); } } /** * * rct2: 0x0068958D */ void screen_pos_to_map_pos(sint16 *x, sint16 *y, int *direction) { screen_get_map_xy(*x, *y, x, y, NULL); if (*x == (sint16)0x8000) return; int my_direction; int dist_from_center_x = abs(*x % 32); int dist_from_center_y = abs(*y % 32); if (dist_from_center_x > 8 && dist_from_center_x < 24 && dist_from_center_y > 8 && dist_from_center_y < 24) { my_direction = 4; } else { sint16 mod_x = *x & 0x1F; sint16 mod_y = *y & 0x1F; if (mod_x <= 16) { if (mod_y < 16) { my_direction = 2; } else { my_direction = 3; } } else { if (mod_y < 16) { my_direction = 1; } else { my_direction = 0; } } } *x = *x & ~0x1F; *y = *y & ~0x1F; if (direction != NULL) *direction = my_direction; } rct_xy16 screen_coord_to_viewport_coord(rct_viewport *viewport, uint16 x, uint16 y) { rct_xy16 ret; ret.x = ((x - viewport->x) << viewport->zoom) + viewport->view_x; ret.y = ((y - viewport->y) << viewport->zoom) + viewport->view_y; return ret; } rct_xy16 viewport_coord_to_map_coord(int x, int y, int z) { rct_xy16 ret; switch (get_current_rotation()) { case 0: ret.x = -x / 2 + y + z; ret.y = x / 2 + y + z; break; case 1: ret.x = -x / 2 - y - z; ret.y = -x / 2 + y + z; break; case 2: ret.x = x / 2 - y - z; ret.y = -x / 2 - y - z; break; case 3: ret.x = x / 2 + y + z; ret.y = x / 2 - y - z; break; } return ret; } /** * * rct2: 0x00664689 */ void show_gridlines() { rct_window *mainWindow; if (RCT2_GLOBAL(0x009E32B0, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_GRIDLINES)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_GRIDLINES; window_invalidate(mainWindow); } } } RCT2_GLOBAL(0x009E32B0, uint8)++; } /** * * rct2: 0x006646B4 */ void hide_gridlines() { rct_window *mainWindow; RCT2_GLOBAL(0x009E32B0, uint8)--; if (RCT2_GLOBAL(0x009E32B0, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (!gConfigGeneral.always_show_gridlines) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_GRIDLINES; window_invalidate(mainWindow); } } } } /** * * rct2: 0x00664E8E */ void show_land_rights() { rct_window *mainWindow; if (RCT2_GLOBAL(0x009E32B2, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_LAND_OWNERSHIP)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_LAND_OWNERSHIP; window_invalidate(mainWindow); } } } RCT2_GLOBAL(0x009E32B2, uint8)++; } /** * * rct2: 0x00664EB9 */ void hide_land_rights() { rct_window *mainWindow; RCT2_GLOBAL(0x009E32B2, uint8)--; if (RCT2_GLOBAL(0x009E32B2, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (mainWindow->viewport->flags & VIEWPORT_FLAG_LAND_OWNERSHIP) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_LAND_OWNERSHIP; window_invalidate(mainWindow); } } } } /** * * rct2: 0x00664EDD */ void show_construction_rights() { rct_window *mainWindow; if (RCT2_GLOBAL(0x009E32B3, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_CONSTRUCTION_RIGHTS)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_CONSTRUCTION_RIGHTS; window_invalidate(mainWindow); } } } RCT2_GLOBAL(0x009E32B3, uint8)++; } /** * * rct2: 0x00664F08 */ void hide_construction_rights() { rct_window *mainWindow; RCT2_GLOBAL(0x009E32B3, uint8)--; if (RCT2_GLOBAL(0x009E32B3, uint8) == 0) { if ((mainWindow = window_get_main()) != NULL) { if (mainWindow->viewport->flags & VIEWPORT_FLAG_CONSTRUCTION_RIGHTS) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_CONSTRUCTION_RIGHTS; window_invalidate(mainWindow); } } } } /** * * rct2: 0x006CB70A */ void viewport_set_visibility(uint8 mode) { rct_window* window = window_get_main(); if (window != NULL) { rct_viewport* edi = window->viewport; uint32 invalidate = 0; switch (mode) { case 0: { //Set all these flags to 0, and invalidate if any were active uint16 mask = VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_SEETHROUGH_RIDES | VIEWPORT_FLAG_SEETHROUGH_SCENERY | VIEWPORT_FLAG_INVISIBLE_SUPPORTS | VIEWPORT_FLAG_LAND_HEIGHTS | VIEWPORT_FLAG_TRACK_HEIGHTS | VIEWPORT_FLAG_PATH_HEIGHTS | VIEWPORT_FLAG_INVISIBLE_PEEPS | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL; invalidate += edi->flags & mask; edi->flags &= ~mask; break; } case 1: //6CB79D case 4: //6CB7C4 //Set underground on, invalidate if it was off invalidate += !(edi->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE); edi->flags |= VIEWPORT_FLAG_UNDERGROUND_INSIDE; break; case 2: //6CB7EB //Set track heights on, invalidate if off invalidate += !(edi->flags & VIEWPORT_FLAG_TRACK_HEIGHTS); edi->flags |= VIEWPORT_FLAG_TRACK_HEIGHTS; break; case 3: //6CB7B1 case 5: //6CB7D8 //Set underground off, invalidate if it was on invalidate += edi->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE; edi->flags &= ~((uint16)VIEWPORT_FLAG_UNDERGROUND_INSIDE); break; } if (invalidate != 0) window_invalidate(window); } } /** * Stores some info about the element pointed at, if requested for this particular type through the interaction mask. * rct2: 0x00688697 */ void store_interaction_info(paint_struct *ps) { if (RCT2_GLOBAL(0x0141F569, uint8) == 0) return; if (ps->sprite_type == VIEWPORT_INTERACTION_ITEM_NONE || ps->sprite_type == 11 // 11 as a type seems to not exist, maybe part of the typo mentioned later on. || ps->sprite_type > VIEWPORT_INTERACTION_ITEM_BANNER) return; uint16 mask; if (ps->sprite_type == VIEWPORT_INTERACTION_ITEM_BANNER) // I think CS made a typo here. Let's replicate the original behaviour. mask = 1 << (ps->sprite_type - 3); else mask = 1 << (ps->sprite_type - 1); if (!(RCT2_GLOBAL(0x009AC154, uint16) & mask)) { RCT2_GLOBAL(0x009AC148, uint8) = ps->sprite_type; RCT2_GLOBAL(0x009AC149, uint8) = ps->var_29; RCT2_GLOBAL(0x009AC14C, uint32) = ps->map_x; RCT2_GLOBAL(0x009AC14E, uint32) = ps->map_y; RCT2_GLOBAL(0x009AC150, rct_map_element*) = ps->mapElement; } } /** * rct2: 0x00679236, 0x00679662, 0x00679B0D, 0x00679FF1 */ static bool sub_679236_679662_679B0D_679FF1(uint32 ebx, rct_g1_element *image, uint8 *esi) { // Probably used to check for corruption if (!(image->flags & G1_FLAG_BMP)) { return false; } if (ebx & 0x20000000) { uint8 *ebx_palette = RCT2_GLOBAL(0x009ABDA4, uint8*); uint8 al = *esi; uint8 al2 = *(al + ebx_palette); return (al2 != 0); } if (ebx & 0x40000000) { return false; } return (*esi != 0); } /** * rct2: 0x0067933B, 0x00679788, 0x00679C4A, 0x0067A117 */ static bool sub_67933B_679788_679C4A_67A117(uint8 *esi, sint16 x_start_point, sint16 y_start_point, int round) { const uint8 *ebx = esi + ((uint16 *) esi)[y_start_point]; uint8 last_data_line = 0; while (!last_data_line) { int no_pixels = *ebx++; uint8 gap_size = *ebx++; last_data_line = no_pixels & 0x80; no_pixels &= 0x7F; ebx += no_pixels; if (round > 1) { if (gap_size % 2) { gap_size++; no_pixels--; if (no_pixels == 0) { continue; } } } if (round == 4) { if (gap_size % 4) { gap_size += 2; no_pixels -= 2; if (no_pixels <= 0) { continue; } } } int x_start = gap_size - x_start_point; if (x_start <= 0) { no_pixels += x_start; if (no_pixels <= 0) { continue; } x_start = 0; } else { // Do nothing? } x_start += no_pixels; x_start--; if (x_start > 0) { no_pixels -= x_start; if (no_pixels <= 0) { continue; } } if (round > 1) { // This matches the original implementation, but allows empty lines to cause false postives on zoom 0 if (ceil2(no_pixels, round) == 0) continue; } return true; } return false; } /** * rct2: 0x00679074 * * @param dpi (edi) * @param imageId (ebx) * @param x (cx) * @param y (dx) * @return value originally stored in 0x00141F569 */ static bool new_sub_679074(rct_drawpixelinfo *dpi, int imageId, sint16 x, sint16 y) { rct_g1_element *image = gfx_get_g1_element(imageId & 0x7FFFF); if (dpi->zoom_level != 0) { if (image->flags & 0x20) { return false; } if (image->flags & 0x10) { // TODO: SAR in dpi done with `>> 1`, in coordinates with `/ 2` rct_drawpixelinfo zoomed_dpi = { .bits = dpi->bits, .x = dpi->x >> 1, .y = dpi->y >> 1, .height = dpi->height, .width = dpi->width, .pitch = dpi->pitch, .zoom_level = dpi->zoom_level - 1 }; return new_sub_679074(&zoomed_dpi, imageId - image->zoomed_offset, x / 2, y / 2); } } int round = 1 << dpi->zoom_level; if (image->flags & G1_FLAG_RLE_COMPRESSION) { y -= (round - 1); } y += image->y_offset; sint16 yStartPoint = 0; sint16 height = image->height; if (dpi->zoom_level != 0) { if (height % 2) { height--; yStartPoint++; } if (dpi->zoom_level == 2) { if (height % 4) { height -= 2; yStartPoint += 2; } } if (height == 0) { return false; } } y = floor2(y, round); sint16 yEndPoint = height; y -= dpi->y; if (y < 0) { yEndPoint += y; if (yEndPoint <= 0) { return false; } yStartPoint -= y; y = 0; } y += yEndPoint; y--; if (y > 0) { yEndPoint -= y; if (yEndPoint <= 0) { return false; } } sint16 xStartPoint = 0; sint16 xEndPoint = image->width; if (!(image->flags & G1_FLAG_RLE_COMPRESSION)) { RCT2_GLOBAL(0x009ABDAE, sint16) = 0; } x += image->x_offset; x = floor2(x, round); x -= dpi->x; if (x < 0) { xEndPoint += x; if (xEndPoint <= 0) { return false; } if (!(image->flags & G1_FLAG_RLE_COMPRESSION)) { RCT2_GLOBAL(0x009ABDAE, sint16) -= x; } xStartPoint -= x; x = 0; } x += xEndPoint; x--; if (x > 0) { xEndPoint -= x; if (xEndPoint <= 0) { return false; } if (!(image->flags & G1_FLAG_RLE_COMPRESSION)) { RCT2_GLOBAL(0x009ABDAE, sint16) += x; } } if (image->flags & G1_FLAG_RLE_COMPRESSION) { return sub_67933B_679788_679C4A_67A117(image->offset, xStartPoint, yStartPoint, round); } uint8 *offset = image->offset + (yStartPoint * image->width) + xStartPoint; uint32 ebx = RCT2_GLOBAL(0x00EDF81C, uint32); if (!(image->flags & 2)) { return sub_679236_679662_679B0D_679FF1(ebx, image, offset); } // The code below is untested. int total_no_pixels = image->width * image->height; uint8 *source_pointer = image->offset; uint8 *new_source_pointer_start = malloc(total_no_pixels); uint8 *new_source_pointer = (*&new_source_pointer_start);// 0x9E3D28; int ebx1, ecx; while (total_no_pixels > 0) { sint8 no_pixels = *source_pointer; if (no_pixels >= 0) { source_pointer++; total_no_pixels -= no_pixels; memcpy((char *) new_source_pointer, (char *) source_pointer, no_pixels); new_source_pointer += no_pixels; source_pointer += no_pixels; continue; } ecx = no_pixels; no_pixels &= 0x7; ecx >>= 3;//SAR int eax = ((int) no_pixels) << 8; ecx = -ecx;//Odd eax = (eax & 0xFF00) + *(source_pointer + 1); total_no_pixels -= ecx; source_pointer += 2; ebx1 = (uint32) new_source_pointer - eax; eax = (uint32) source_pointer; source_pointer = (uint8 *) ebx1; ebx1 = eax; eax = 0; memcpy((char *) new_source_pointer, (char *) source_pointer, ecx); new_source_pointer += ecx; source_pointer = (uint8 *) ebx1; } bool output = sub_679236_679662_679B0D_679FF1(ebx, image, new_source_pointer_start + (uint32) offset); free(new_source_pointer_start); return output; } static bool sub_679074(rct_drawpixelinfo *dpi, int imageId, sint16 x, sint16 y) { sint16 before_x = RCT2_GLOBAL(0x9ABDAE, sint16); uint8 before_output = RCT2_GLOBAL(0x00141F569, uint8); RCT2_CALLPROC_X(0x00679074, 0, imageId, x, y, 0, (int) dpi, 0); sint16 original_x = RCT2_GLOBAL(0x9ABDAE, sint16); uint8 original_output = RCT2_GLOBAL(0x00141F569, uint8); RCT2_GLOBAL(0x9ABDAE, sint16) = before_x; RCT2_GLOBAL(0x00141F569, uint8) = before_output; bool new_output = new_sub_679074(dpi, imageId, x, y); sint16 new_x = RCT2_GLOBAL(0x9ABDAE, sint16); assert(new_x == original_x); assert(new_output == original_output); return new_output; } /** * * rct2: 0x00679023 */ void sub_679023(rct_drawpixelinfo *dpi, int imageId, int x, int y) { RCT2_GLOBAL(0x00141F569, uint8) = 0; imageId &= 0xBFFFFFFF; if (imageId & 0x20000000) { RCT2_GLOBAL(0x00EDF81C, uint32) = 0x20000000; int index = (imageId >> 19) & 0x7F; if (imageId & 0x80000000) { index &= 0x1F; } int g1Index = RCT2_ADDRESS(0x0097FCBC, uint32)[index]; RCT2_GLOBAL(0x009ABDA4, uint8*) = g1Elements[g1Index].offset; } else { RCT2_GLOBAL(0x00EDF81C, uint32) = 0; } RCT2_GLOBAL(0x00141F569, uint8) = sub_679074(dpi, imageId, x, y); } /** * * rct2: 0x0068862C */ void sub_68862C() { rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); paint_struct *ps = RCT2_GLOBAL(0x00EE7884, paint_struct*), *old_ps, *next_ps, *attached_ps; while ((ps = ps->next_quadrant_ps) != NULL) { old_ps = ps; next_ps = ps; while (next_ps != NULL) { ps = next_ps; sub_679023(dpi, ps->image_id, ps->x, ps->y); store_interaction_info(ps); next_ps = ps->var_20; } attached_ps = ps->attached_ps; while (attached_ps != NULL) { sub_679023( dpi, attached_ps->image_id, (attached_ps->attached_x + ps->x) & 0xFFFF, (attached_ps->attached_y + ps->y) & 0xFFFF ); store_interaction_info(ps); attached_ps = attached_ps->next_attached_ps; } ps = old_ps; } } /** * * rct2: 0x00685ADC * screenX: eax * screenY: ebx * flags: edx * x: ax * y: cx * interactionType: bl * mapElement: edx * viewport: edi */ void get_map_coordinates_from_pos(int screenX, int screenY, int flags, sint16 *x, sint16 *y, int *interactionType, rct_map_element **mapElement, rct_viewport **viewport) { RCT2_GLOBAL(0x9AC154, uint16_t) = flags & 0xFFFF; RCT2_GLOBAL(0x9AC148, uint8_t) = 0; rct_window* window = window_find_from_point(screenX, screenY); if (window != NULL && window->viewport != NULL) { rct_viewport* myviewport = window->viewport; RCT2_GLOBAL(0x9AC138 + 4, int16_t) = screenX; RCT2_GLOBAL(0x9AC138 + 6, int16_t) = screenY; screenX -= (int)myviewport->x; screenY -= (int)myviewport->y; if (screenX >= 0 && screenX < (int)myviewport->width && screenY >= 0 && screenY < (int)myviewport->height) { screenX <<= myviewport->zoom; screenY <<= myviewport->zoom; screenX += (int)myviewport->view_x; screenY += (int)myviewport->view_y; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16_t) = myviewport->zoom; screenX &= (0xFFFF << myviewport->zoom) & 0xFFFF; screenY &= (0xFFFF << myviewport->zoom) & 0xFFFF; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, int16_t) = screenX; RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, int16_t) = screenY; rct_drawpixelinfo* dpi = RCT2_ADDRESS(RCT2_ADDRESS_VIEWPORT_DPI, rct_drawpixelinfo); dpi->y = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, int16_t); dpi->height = 1; dpi->zoom_level = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16_t); dpi->x = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, int16_t); dpi->width = 1; RCT2_GLOBAL(0xEE7880, uint32_t) = 0xF1A4CC; RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*) = dpi; painter_setup(); viewport_paint_setup(); sub_688217(); sub_68862C(); } if (viewport != NULL) *viewport = myviewport; } if (interactionType != NULL) *interactionType = RCT2_GLOBAL(0x9AC148, uint8_t); if (x != NULL) *x = RCT2_GLOBAL(0x9AC14C, int16_t); if (y != NULL) *y = RCT2_GLOBAL(0x9AC14E, int16_t); if (mapElement != NULL) *mapElement = RCT2_GLOBAL(0x9AC150, rct_map_element*); } /** * Left, top, right and bottom represent 2D map coordinates at zoom 0. */ void viewport_invalidate(rct_viewport *viewport, int left, int top, int right, int bottom) { int viewportLeft = viewport->view_x; int viewportTop = viewport->view_y; int viewportRight = viewport->view_x + viewport->view_width; int viewportBottom = viewport->view_y + viewport->view_height; if (right > viewportLeft && bottom > viewportTop) { left = max(left, viewportLeft); top = max(top, viewportTop); right = min(right, viewportRight); bottom = min(bottom, viewportBottom); uint8 zoom = 1 << viewport->zoom; left -= viewportLeft; top -= viewportTop; right -= viewportLeft; bottom -= viewportTop; left /= zoom; top /= zoom; right /= zoom; bottom /= zoom; left += viewport->x; top += viewport->y; right += viewport->x; bottom += viewport->y; gfx_set_dirty_blocks(left, top, right, bottom); } } rct_viewport *viewport_find_from_point(int screenX, int screenY) { rct_window *w; rct_viewport *viewport; w = window_find_from_point(screenX, screenY); if (w == NULL) return NULL; viewport = w->viewport; if (viewport == NULL) return NULL; if (screenX < viewport->x || screenY < viewport->y) return NULL; if (screenX >= viewport->x + viewport->width || screenY >= viewport->y + viewport->height) return NULL; return viewport; } /** * * rct2: 0x00688972 * In: * screen_x: eax * screen_y: ebx * Out: * x: ax * y: bx * map_element: edx ? * viewport: edi */ void screen_get_map_xy(int screenX, int screenY, sint16 *x, sint16 *y, rct_viewport **viewport) { sint16 my_x, my_y; int z, interactionType; rct_viewport *myViewport; get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_TERRAIN, &my_x, &my_y, &interactionType, NULL, &myViewport); if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { *x = 0x8000; return; } RCT2_GLOBAL(0x00F1AD34, sint16) = my_x; RCT2_GLOBAL(0x00F1AD36, sint16) = my_y; RCT2_GLOBAL(0x00F1AD38, sint16) = my_x + 31; RCT2_GLOBAL(0x00F1AD3A, sint16) = my_y + 31; rct_xy16 start_vp_pos = screen_coord_to_viewport_coord(myViewport, screenX, screenY); rct_xy16 map_pos = { my_x + 16, my_y + 16 }; for (int i = 0; i < 5; i++) { z = map_element_height(map_pos.x, map_pos.y); map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); map_pos.x = clamp(RCT2_GLOBAL(0x00F1AD34, sint16), map_pos.x, RCT2_GLOBAL(0x00F1AD38, sint16)); map_pos.y = clamp(RCT2_GLOBAL(0x00F1AD36, sint16), map_pos.y, RCT2_GLOBAL(0x00F1AD3A, sint16)); } *x = map_pos.x; *y = map_pos.y; if (viewport != NULL) *viewport = myViewport; } /** * * rct2: 0x006894D4 */ void screen_get_map_xy_with_z(sint16 screenX, sint16 screenY, sint16 z, sint16 *mapX, sint16 *mapY) { rct_viewport *viewport = viewport_find_from_point(screenX, screenY); if (viewport == NULL) { *mapX = 0x8000; return; } screenX = viewport->view_x + ((screenX - viewport->x) << viewport->zoom); screenY = viewport->view_y + ((screenY - viewport->y) << viewport->zoom); rct_xy16 mapPosition = viewport_coord_to_map_coord(screenX, screenY + z, 0); if (mapPosition.x < 0 || mapPosition.x >= (256 * 32) || mapPosition.y < 0 || mapPosition.y >(256 * 32)) { *mapX = 0x8000; return; } *mapX = mapPosition.x; *mapY = mapPosition.y; } /** * * rct2: 0x00689604 */ void screen_get_map_xy_quadrant(sint16 screenX, sint16 screenY, sint16 *mapX, sint16 *mapY, uint8 *quadrant) { screen_get_map_xy(screenX, screenY, mapX, mapY, NULL); if (*mapX == (sint16)0x8000) return; *quadrant = map_get_tile_quadrant(*mapX, *mapY); *mapX = floor2(*mapX, 32); *mapY = floor2(*mapY, 32); } /** * * rct2: 0x0068964B */ void screen_get_map_xy_quadrant_with_z(sint16 screenX, sint16 screenY, sint16 z, sint16 *mapX, sint16 *mapY, uint8 *quadrant) { screen_get_map_xy_with_z(screenX, screenY, z, mapX, mapY); if (*mapX == (sint16)0x8000) return; *quadrant = map_get_tile_quadrant(*mapX, *mapY); *mapX = floor2(*mapX, 32); *mapY = floor2(*mapY, 32); } /** * * rct2: 0x00689692 */ void screen_get_map_xy_side(sint16 screenX, sint16 screenY, sint16 *mapX, sint16 *mapY, uint8 *side) { screen_get_map_xy(screenX, screenY, mapX, mapY, NULL); if (*mapX == (sint16)0x8000) return; *side = map_get_tile_side(*mapX, *mapY); *mapX = floor2(*mapX, 32); *mapY = floor2(*mapY, 32); } /** * * rct2: 0x006896DC */ void screen_get_map_xy_side_with_z(sint16 screenX, sint16 screenY, sint16 z, sint16 *mapX, sint16 *mapY, uint8 *side) { screen_get_map_xy_with_z(screenX, screenY, z, mapX, mapY); if (*mapX == (sint16)0x8000) return; *side = map_get_tile_side(*mapX, *mapY); *mapX = floor2(*mapX, 32); *mapY = floor2(*mapY, 32); } /** * Get current viewport rotation. * * If an invalid rotation is detected and DEBUG_LEVEL_1 is enabled, an error * will be reported. * * @returns rotation in range 0-3 (inclusive) */ uint8 get_current_rotation() { uint8 rotation = gCurrentRotation; uint8 rotation_masked = rotation & 3; #if DEBUG_LEVEL_1 if (rotation != rotation_masked) { log_error("Found wrong rotation %d! Will return %d instead.", (uint32)rotation, (uint32)rotation_masked); } #endif // DEBUG_LEVEL_1 return rotation_masked; }