mirror of https://github.com/OpenRCT2/OpenRCT2.git
1212 lines
37 KiB
C++
1212 lines
37 KiB
C++
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
|
/*****************************************************************************
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
*
|
|
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
|
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
|
*****************************************************************************/
|
|
#pragma endregion
|
|
|
|
#include <algorithm>
|
|
#include "../config/Config.h"
|
|
#include "../core/Math.hpp"
|
|
#include "../drawing/Drawing.h"
|
|
#include "../interface/Viewport.h"
|
|
#include "../localisation/Localisation.h"
|
|
#include "../localisation/LocalisationService.h"
|
|
#include "Paint.h"
|
|
#include "sprite/Sprite.h"
|
|
#include "tile_element/TileElement.h"
|
|
|
|
// Global for paint clipping height
|
|
uint8 gClipHeight = 128; // Default to middle value
|
|
|
|
paint_session gPaintSession;
|
|
static bool _paintSessionInUse;
|
|
|
|
static constexpr const uint8 BoundBoxDebugColours[] =
|
|
{
|
|
0, // NONE
|
|
102, // TERRAIN
|
|
114, // SPRITE
|
|
229, // RIDE
|
|
126, // WATER
|
|
138, // SCENERY
|
|
150, // FOOTPATH
|
|
162, // FOOTPATH_ITEM
|
|
174, // PARK
|
|
186, // WALL
|
|
198, // LARGE_SCENERY
|
|
210, // LABEL
|
|
222, // BANNER
|
|
};
|
|
|
|
bool gShowDirtyVisuals;
|
|
bool gPaintBoundingBoxes;
|
|
|
|
static void paint_session_init(paint_session * session, rct_drawpixelinfo * dpi);
|
|
static void paint_attached_ps(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 viewFlags);
|
|
static void paint_ps_image_with_bounding_boxes(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 imageId, sint16 x, sint16 y);
|
|
static void paint_ps_image(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 imageId, sint16 x, sint16 y);
|
|
static uint32 paint_ps_colourify_image(uint32 imageId, uint8 spriteType, uint32 viewFlags);
|
|
|
|
static void paint_session_init(paint_session * session, rct_drawpixelinfo * dpi)
|
|
{
|
|
session->Unk140E9A8 = dpi;
|
|
session->EndOfPaintStructArray = &session->PaintStructs[4000 - 1];
|
|
session->NextFreePaintStruct = session->PaintStructs;
|
|
session->UnkF1AD28 = nullptr;
|
|
session->UnkF1AD2C = nullptr;
|
|
for (auto &quadrant : session->Quadrants)
|
|
{
|
|
quadrant = nullptr;
|
|
}
|
|
session->QuadrantBackIndex = -1;
|
|
session->QuadrantFrontIndex = 0;
|
|
session->PSStringHead = nullptr;
|
|
session->LastPSString = nullptr;
|
|
session->WoodenSupportsPrependTo = nullptr;
|
|
session->CurrentlyDrawnItem = nullptr;
|
|
session->SurfaceElement = nullptr;
|
|
}
|
|
|
|
static void paint_session_add_ps_to_quadrant(paint_session * session, paint_struct * ps, sint32 positionHash)
|
|
{
|
|
uint32 paintQuadrantIndex = Math::Clamp(0, positionHash / 32, MAX_PAINT_QUADRANTS - 1);
|
|
ps->quadrant_index = paintQuadrantIndex;
|
|
ps->next_quadrant_ps = session->Quadrants[paintQuadrantIndex];
|
|
session->Quadrants[paintQuadrantIndex] = ps;
|
|
|
|
session->QuadrantBackIndex = std::min(session->QuadrantBackIndex, paintQuadrantIndex);
|
|
session->QuadrantFrontIndex = std::max(session->QuadrantFrontIndex, paintQuadrantIndex);
|
|
}
|
|
|
|
/**
|
|
* Extracted from 0x0098196c, 0x0098197c, 0x0098198c, 0x0098199c
|
|
*/
|
|
static paint_struct * sub_9819_c(
|
|
paint_session * session, uint32 image_id, LocationXYZ16 offset, LocationXYZ16 boundBoxSize, LocationXYZ16 boundBoxOffset)
|
|
{
|
|
if (session->NextFreePaintStruct >= session->EndOfPaintStructArray) return nullptr;
|
|
auto g1 = gfx_get_g1_element(image_id & 0x7FFFF);
|
|
if (g1 == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
paint_struct * ps = &session->NextFreePaintStruct->basic;
|
|
ps->image_id = image_id;
|
|
|
|
switch (session->CurrentRotation)
|
|
{
|
|
case 0:
|
|
rotate_map_coordinates(&offset.x, &offset.y, 0);
|
|
break;
|
|
case 1:
|
|
rotate_map_coordinates(&offset.x, &offset.y, 3);
|
|
break;
|
|
case 2:
|
|
rotate_map_coordinates(&offset.x, &offset.y, 2);
|
|
break;
|
|
case 3:
|
|
rotate_map_coordinates(&offset.x, &offset.y, 1);
|
|
break;
|
|
}
|
|
offset.x += session->SpritePosition.x;
|
|
offset.y += session->SpritePosition.y;
|
|
|
|
LocationXY16 map = coordinate_3d_to_2d(&offset, session->CurrentRotation);
|
|
|
|
ps->x = map.x;
|
|
ps->y = map.y;
|
|
|
|
sint32 left = map.x + g1->x_offset;
|
|
sint32 bottom = map.y + g1->y_offset;
|
|
|
|
sint32 right = left + g1->width;
|
|
sint32 top = bottom + g1->height;
|
|
|
|
rct_drawpixelinfo * dpi = session->Unk140E9A8;
|
|
|
|
if (right <= dpi->x)return nullptr;
|
|
if (top <= dpi->y)return nullptr;
|
|
if (left >= dpi->x + dpi->width)return nullptr;
|
|
if (bottom >= dpi->y + dpi->height)return nullptr;
|
|
|
|
|
|
// This probably rotates the variables so they're relative to rotation 0.
|
|
switch (session->CurrentRotation)
|
|
{
|
|
case 0:
|
|
boundBoxSize.x--;
|
|
boundBoxSize.y--;
|
|
rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 0);
|
|
rotate_map_coordinates(&boundBoxSize.x, &boundBoxSize.y, 0);
|
|
break;
|
|
case 1:
|
|
boundBoxSize.x--;
|
|
rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 3);
|
|
rotate_map_coordinates(&boundBoxSize.x, &boundBoxSize.y, 3);
|
|
break;
|
|
case 2:
|
|
rotate_map_coordinates(&boundBoxSize.x, &boundBoxSize.y, 2);
|
|
rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 2);
|
|
break;
|
|
case 3:
|
|
boundBoxSize.y--;
|
|
rotate_map_coordinates(&boundBoxSize.x, &boundBoxSize.y, 1);
|
|
rotate_map_coordinates(&boundBoxOffset.x, &boundBoxOffset.y, 1);
|
|
break;
|
|
}
|
|
|
|
ps->bounds.x_end = boundBoxSize.x + boundBoxOffset.x + session->SpritePosition.x;
|
|
ps->bounds.z = boundBoxOffset.z;
|
|
ps->bounds.z_end = boundBoxOffset.z + boundBoxSize.z;
|
|
ps->bounds.y_end = boundBoxSize.y + boundBoxOffset.y + session->SpritePosition.y;
|
|
ps->flags = 0;
|
|
ps->bounds.x = boundBoxOffset.x + session->SpritePosition.x;
|
|
ps->bounds.y = boundBoxOffset.y + session->SpritePosition.y;
|
|
ps->attached_ps = nullptr;
|
|
ps->var_20 = nullptr;
|
|
ps->sprite_type = session->InteractionType;
|
|
ps->var_29 = 0;
|
|
ps->map_x = session->MapPosition.x;
|
|
ps->map_y = session->MapPosition.y;
|
|
ps->tileElement = (rct_tile_element*)session->CurrentlyDrawnItem;
|
|
|
|
return ps;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x0068B6C2
|
|
*/
|
|
void paint_session_generate(paint_session * session)
|
|
{
|
|
rct_drawpixelinfo * dpi = session->Unk140E9A8;
|
|
LocationXY16 mapTile =
|
|
{
|
|
(sint16)(dpi->x & 0xFFE0),
|
|
(sint16)((dpi->y - 16) & 0xFFE0)
|
|
};
|
|
|
|
sint16 half_x = mapTile.x >> 1;
|
|
uint16 num_vertical_quadrants = (dpi->height + 2128) >> 5;
|
|
|
|
session->CurrentRotation = get_current_rotation();
|
|
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) {
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y);
|
|
|
|
sprite_paint_setup(session, mapTile.x - 32, mapTile.y + 32);
|
|
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y + 32);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y + 32);
|
|
|
|
mapTile.x += 32;
|
|
sprite_paint_setup(session, 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) {
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y);
|
|
|
|
sprite_paint_setup(session, mapTile.x - 32, mapTile.y - 32);
|
|
|
|
tile_element_paint_setup(session, mapTile.x - 32, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x - 32, mapTile.y);
|
|
|
|
mapTile.y += 32;
|
|
sprite_paint_setup(session, 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) {
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y);
|
|
|
|
sprite_paint_setup(session, mapTile.x + 32, mapTile.y - 32);
|
|
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y - 32);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y - 32);
|
|
|
|
mapTile.x -= 32;
|
|
|
|
sprite_paint_setup(session, 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)
|
|
{
|
|
tile_element_paint_setup(session, mapTile.x, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y);
|
|
|
|
sprite_paint_setup(session, mapTile.x + 32, mapTile.y + 32);
|
|
|
|
tile_element_paint_setup(session, mapTile.x + 32, mapTile.y);
|
|
sprite_paint_setup(session, mapTile.x + 32, mapTile.y);
|
|
|
|
mapTile.y -= 32;
|
|
|
|
sprite_paint_setup(session, mapTile.x, mapTile.y);
|
|
|
|
mapTile.x += 32;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
template<uint8_t> static bool check_bounding_box(const paint_struct_bound_box& initialBBox,
|
|
const paint_struct_bound_box& currentBBox)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template<> bool check_bounding_box<0>(const paint_struct_bound_box& initialBBox,
|
|
const paint_struct_bound_box& currentBBox)
|
|
{
|
|
if (initialBBox.z_end >= currentBBox.z && initialBBox.y_end >= currentBBox.y && initialBBox.x_end >= currentBBox.x
|
|
&& !(initialBBox.z < currentBBox.z_end && initialBBox.y < currentBBox.y_end && initialBBox.x < currentBBox.x_end))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<> bool check_bounding_box<1>(const paint_struct_bound_box& initialBBox,
|
|
const paint_struct_bound_box& currentBBox)
|
|
{
|
|
if (initialBBox.z_end >= currentBBox.z && initialBBox.y_end >= currentBBox.y && initialBBox.x_end < currentBBox.x
|
|
&& !(initialBBox.z < currentBBox.z_end && initialBBox.y < currentBBox.y_end && initialBBox.x >= currentBBox.x_end))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<> bool check_bounding_box<2>(const paint_struct_bound_box& initialBBox,
|
|
const paint_struct_bound_box& currentBBox)
|
|
{
|
|
if (initialBBox.z_end >= currentBBox.z && initialBBox.y_end < currentBBox.y && initialBBox.x_end < currentBBox.x
|
|
&& !(initialBBox.z < currentBBox.z_end && initialBBox.y >= currentBBox.y_end && initialBBox.x >= currentBBox.x_end))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<> bool check_bounding_box<3>(const paint_struct_bound_box& initialBBox,
|
|
const paint_struct_bound_box& currentBBox)
|
|
{
|
|
if (initialBBox.z_end >= currentBBox.z && initialBBox.y_end < currentBBox.y && initialBBox.x_end >= currentBBox.x
|
|
&& !(initialBBox.z < currentBBox.z_end && initialBBox.y >= currentBBox.y_end && initialBBox.x < currentBBox.x_end))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<uint8 _TRotation>
|
|
static paint_struct * paint_arrange_structs_helper_rotation(paint_struct * ps_next, uint16 quadrantIndex, uint8 flag)
|
|
{
|
|
paint_struct * ps;
|
|
paint_struct * ps_temp;
|
|
do
|
|
{
|
|
ps = ps_next;
|
|
ps_next = ps_next->next_quadrant_ps;
|
|
if (ps_next == nullptr) return ps;
|
|
} while (quadrantIndex > ps_next->quadrant_index);
|
|
|
|
// Cache the last visited node so we don't have to walk the whole list again
|
|
paint_struct * ps_cache = ps;
|
|
|
|
ps_temp = ps;
|
|
do {
|
|
ps = ps->next_quadrant_ps;
|
|
if (ps == nullptr) break;
|
|
|
|
if (ps->quadrant_index > quadrantIndex + 1)
|
|
{
|
|
ps->quadrant_flags = PAINT_QUADRANT_FLAG_BIGGER;
|
|
}
|
|
else if (ps->quadrant_index == quadrantIndex + 1)
|
|
{
|
|
ps->quadrant_flags = PAINT_QUADRANT_FLAG_NEXT | PAINT_QUADRANT_FLAG_IDENTICAL;
|
|
}
|
|
else if (ps->quadrant_index == quadrantIndex)
|
|
{
|
|
ps->quadrant_flags = flag | PAINT_QUADRANT_FLAG_IDENTICAL;
|
|
}
|
|
} while (ps->quadrant_index <= quadrantIndex + 1);
|
|
ps = ps_temp;
|
|
|
|
while (true)
|
|
{
|
|
while (true)
|
|
{
|
|
ps_next = ps->next_quadrant_ps;
|
|
if (ps_next == nullptr) return ps_cache;
|
|
if (ps_next->quadrant_flags & PAINT_QUADRANT_FLAG_BIGGER) return ps_cache;
|
|
if (ps_next->quadrant_flags & PAINT_QUADRANT_FLAG_IDENTICAL) break;
|
|
ps = ps_next;
|
|
}
|
|
|
|
ps_next->quadrant_flags &= ~PAINT_QUADRANT_FLAG_IDENTICAL;
|
|
ps_temp = ps;
|
|
|
|
const paint_struct_bound_box& initialBBox = ps_next->bounds;
|
|
|
|
while (true)
|
|
{
|
|
ps = ps_next;
|
|
ps_next = ps_next->next_quadrant_ps;
|
|
if (ps_next == nullptr) break;
|
|
if (ps_next->quadrant_flags & PAINT_QUADRANT_FLAG_BIGGER) break;
|
|
if (!(ps_next->quadrant_flags & PAINT_QUADRANT_FLAG_NEXT)) continue;
|
|
|
|
const paint_struct_bound_box& currentBBox = ps_next->bounds;
|
|
|
|
const bool compareResult = check_bounding_box<_TRotation>(initialBBox, currentBBox);
|
|
|
|
if (compareResult)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
paint_struct * paint_arrange_structs_helper(paint_struct * ps_next, uint16 quadrantIndex, uint8 flag, uint8 rotation)
|
|
{
|
|
switch (rotation)
|
|
{
|
|
case 0:
|
|
return paint_arrange_structs_helper_rotation<0>(ps_next, quadrantIndex, flag);
|
|
case 1:
|
|
return paint_arrange_structs_helper_rotation<1>(ps_next, quadrantIndex, flag);
|
|
case 2:
|
|
return paint_arrange_structs_helper_rotation<2>(ps_next, quadrantIndex, flag);
|
|
case 3:
|
|
return paint_arrange_structs_helper_rotation<3>(ps_next, quadrantIndex, flag);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00688217
|
|
*/
|
|
paint_struct paint_session_arrange(paint_session * session)
|
|
{
|
|
paint_struct psHead = { 0 };
|
|
paint_struct * ps = &psHead;
|
|
ps->next_quadrant_ps = nullptr;
|
|
uint32 quadrantIndex = session->QuadrantBackIndex;
|
|
const uint8 rotation = get_current_rotation();
|
|
if (quadrantIndex != UINT32_MAX)
|
|
{
|
|
do
|
|
{
|
|
paint_struct * ps_next = session->Quadrants[quadrantIndex];
|
|
if (ps_next != nullptr)
|
|
{
|
|
ps->next_quadrant_ps = ps_next;
|
|
do
|
|
{
|
|
ps = ps_next;
|
|
ps_next = ps_next->next_quadrant_ps;
|
|
|
|
} while (ps_next != nullptr);
|
|
}
|
|
} while (++quadrantIndex <= session->QuadrantFrontIndex);
|
|
|
|
paint_struct * ps_cache = paint_arrange_structs_helper(&psHead, session->QuadrantBackIndex & 0xFFFF, PAINT_QUADRANT_FLAG_NEXT, rotation);
|
|
|
|
quadrantIndex = session->QuadrantBackIndex;
|
|
while (++quadrantIndex < session->QuadrantFrontIndex)
|
|
{
|
|
ps_cache = paint_arrange_structs_helper(ps_cache, quadrantIndex & 0xFFFF, 0, rotation);
|
|
}
|
|
}
|
|
|
|
return psHead;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00688485
|
|
*/
|
|
void paint_draw_structs(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 viewFlags)
|
|
{
|
|
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 == VIEWPORT_INTERACTION_ITEM_SPRITE)
|
|
{
|
|
if (dpi->zoom_level >= 1)
|
|
{
|
|
x = floor2(x, 2);
|
|
y = floor2(y, 2);
|
|
if (dpi->zoom_level >= 2)
|
|
{
|
|
x = floor2(x, 4);
|
|
y = floor2(y, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 imageId = paint_ps_colourify_image(ps->image_id, ps->sprite_type, viewFlags);
|
|
if (gPaintBoundingBoxes && dpi->zoom_level == 0)
|
|
{
|
|
paint_ps_image_with_bounding_boxes(dpi, ps, imageId, x, y);
|
|
}
|
|
else
|
|
{
|
|
paint_ps_image(dpi, ps, imageId, x, y);
|
|
}
|
|
|
|
if (ps->var_20 != nullptr)
|
|
{
|
|
// NOTE: RCT uses var_20 instead of next_quadrant_ps, do we still need it?
|
|
ps = ps->var_20;
|
|
}
|
|
else
|
|
{
|
|
paint_attached_ps(dpi, ps, viewFlags);
|
|
ps = previous_ps->next_quadrant_ps;
|
|
previous_ps = ps;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00688596
|
|
* Part of 0x688485
|
|
*/
|
|
static void paint_attached_ps(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 viewFlags)
|
|
{
|
|
attached_paint_struct * attached_ps = ps->attached_ps;
|
|
for (; attached_ps; attached_ps = attached_ps->next)
|
|
{
|
|
sint16 x = attached_ps->x + ps->x;
|
|
sint16 y = attached_ps->y + ps->y;
|
|
|
|
uint32 imageId = paint_ps_colourify_image(attached_ps->image_id, ps->sprite_type, viewFlags);
|
|
if (attached_ps->flags & PAINT_STRUCT_FLAG_IS_MASKED)
|
|
{
|
|
gfx_draw_sprite_raw_masked(dpi, x, y, imageId, attached_ps->colour_image_id);
|
|
}
|
|
else
|
|
{
|
|
gfx_draw_sprite(dpi, imageId, x, y, ps->tertiary_colour);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void paint_ps_image_with_bounding_boxes(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 imageId, sint16 x, sint16 y)
|
|
{
|
|
const uint8 colour = BoundBoxDebugColours[ps->sprite_type];
|
|
const uint8 rotation = get_current_rotation();
|
|
|
|
const LocationXYZ16 frontTop =
|
|
{
|
|
(sint16)ps->bounds.x_end,
|
|
(sint16)ps->bounds.y_end,
|
|
(sint16)ps->bounds.z_end
|
|
};
|
|
const LocationXY16 screenCoordFrontTop = coordinate_3d_to_2d(&frontTop, rotation);
|
|
|
|
const LocationXYZ16 frontBottom =
|
|
{
|
|
(sint16)ps->bounds.x_end,
|
|
(sint16)ps->bounds.y_end,
|
|
(sint16)ps->bounds.z
|
|
};
|
|
const LocationXY16 screenCoordFrontBottom = coordinate_3d_to_2d(&frontBottom, rotation);
|
|
|
|
const LocationXYZ16 leftTop =
|
|
{
|
|
(sint16)ps->bounds.x,
|
|
(sint16)ps->bounds.y_end,
|
|
(sint16)ps->bounds.z_end
|
|
};
|
|
const LocationXY16 screenCoordLeftTop = coordinate_3d_to_2d(&leftTop, rotation);
|
|
|
|
const LocationXYZ16 leftBottom =
|
|
{
|
|
(sint16)ps->bounds.x,
|
|
(sint16)ps->bounds.y_end,
|
|
(sint16)ps->bounds.z
|
|
};
|
|
const LocationXY16 screenCoordLeftBottom = coordinate_3d_to_2d(&leftBottom, rotation);
|
|
|
|
const LocationXYZ16 rightTop =
|
|
{
|
|
(sint16)ps->bounds.x_end,
|
|
(sint16)ps->bounds.y,
|
|
(sint16)ps->bounds.z_end
|
|
};
|
|
const LocationXY16 screenCoordRightTop = coordinate_3d_to_2d(&rightTop, rotation);
|
|
|
|
const LocationXYZ16 rightBottom =
|
|
{
|
|
(sint16)ps->bounds.x_end,
|
|
(sint16)ps->bounds.y,
|
|
(sint16)ps->bounds.z
|
|
};
|
|
const LocationXY16 screenCoordRightBottom = coordinate_3d_to_2d(&rightBottom, rotation);
|
|
|
|
const LocationXYZ16 backTop =
|
|
{
|
|
(sint16)ps->bounds.x,
|
|
(sint16)ps->bounds.y,
|
|
(sint16)ps->bounds.z_end
|
|
};
|
|
const LocationXY16 screenCoordBackTop = coordinate_3d_to_2d(&backTop, rotation);
|
|
|
|
const LocationXYZ16 backBottom =
|
|
{
|
|
(sint16)ps->bounds.x,
|
|
(sint16)ps->bounds.y,
|
|
(sint16)ps->bounds.z
|
|
};
|
|
const LocationXY16 screenCoordBackBottom = coordinate_3d_to_2d(&backBottom, rotation);
|
|
|
|
// bottom square
|
|
gfx_draw_line(dpi, screenCoordFrontBottom.x, screenCoordFrontBottom.y, screenCoordLeftBottom.x, screenCoordLeftBottom.y, colour);
|
|
gfx_draw_line(dpi, screenCoordBackBottom.x, screenCoordBackBottom.y, screenCoordLeftBottom.x, screenCoordLeftBottom.y, colour);
|
|
gfx_draw_line(dpi, screenCoordBackBottom.x, screenCoordBackBottom.y, screenCoordRightBottom.x, screenCoordRightBottom.y, colour);
|
|
gfx_draw_line(dpi, screenCoordFrontBottom.x, screenCoordFrontBottom.y, screenCoordRightBottom.x, screenCoordRightBottom.y, colour);
|
|
|
|
//vertical back + sides
|
|
gfx_draw_line(dpi, screenCoordBackTop.x, screenCoordBackTop.y, screenCoordBackBottom.x, screenCoordBackBottom.y, colour);
|
|
gfx_draw_line(dpi, screenCoordLeftTop.x, screenCoordLeftTop.y, screenCoordLeftBottom.x, screenCoordLeftBottom.y, colour);
|
|
gfx_draw_line(dpi, screenCoordRightTop.x, screenCoordRightTop.y, screenCoordRightBottom.x, screenCoordRightBottom.y, colour);
|
|
|
|
// top square back
|
|
gfx_draw_line(dpi, screenCoordBackTop.x, screenCoordBackTop.y, screenCoordLeftTop.x, screenCoordLeftTop.y, colour);
|
|
gfx_draw_line(dpi, screenCoordBackTop.x, screenCoordBackTop.y, screenCoordRightTop.x, screenCoordRightTop.y, colour);
|
|
|
|
paint_ps_image(dpi, ps, imageId, x, y);
|
|
|
|
// vertical front
|
|
gfx_draw_line(dpi, screenCoordFrontTop.x, screenCoordFrontTop.y, screenCoordFrontBottom.x, screenCoordFrontBottom.y, colour);
|
|
|
|
// top square
|
|
gfx_draw_line(dpi, screenCoordFrontTop.x, screenCoordFrontTop.y, screenCoordLeftTop.x, screenCoordLeftTop.y, colour);
|
|
gfx_draw_line(dpi, screenCoordFrontTop.x, screenCoordFrontTop.y, screenCoordRightTop.x, screenCoordRightTop.y, colour);
|
|
}
|
|
|
|
static void paint_ps_image(rct_drawpixelinfo * dpi, paint_struct * ps, uint32 imageId, sint16 x, sint16 y)
|
|
{
|
|
if (ps->flags & PAINT_STRUCT_FLAG_IS_MASKED)
|
|
{
|
|
return gfx_draw_sprite_raw_masked(dpi, x, y, imageId, ps->colour_image_id);
|
|
}
|
|
|
|
gfx_draw_sprite(dpi, imageId, x, y, ps->tertiary_colour);
|
|
}
|
|
|
|
static uint32 paint_ps_colourify_image(uint32 imageId, uint8 spriteType, uint32 viewFlags)
|
|
{
|
|
constexpr uint32 primaryColour = COLOUR_BRIGHT_YELLOW;
|
|
constexpr uint32 secondaryColour = COLOUR_GREY;
|
|
constexpr uint32 seeThoughFlags = IMAGE_TYPE_TRANSPARENT | (primaryColour << 19) | (secondaryColour << 24);
|
|
|
|
if (viewFlags & VIEWPORT_FLAG_SEETHROUGH_RIDES)
|
|
{
|
|
if (spriteType == VIEWPORT_INTERACTION_ITEM_RIDE)
|
|
{
|
|
imageId &= 0x7FFFF;
|
|
imageId |= seeThoughFlags;
|
|
}
|
|
}
|
|
if (viewFlags & VIEWPORT_FLAG_UNDERGROUND_INSIDE)
|
|
{
|
|
if (spriteType == VIEWPORT_INTERACTION_ITEM_WALL)
|
|
{
|
|
imageId &= 0x7FFFF;
|
|
imageId |= seeThoughFlags;
|
|
}
|
|
}
|
|
if (viewFlags & VIEWPORT_FLAG_SEETHROUGH_PATHS)
|
|
{
|
|
switch (spriteType)
|
|
{
|
|
case VIEWPORT_INTERACTION_ITEM_FOOTPATH:
|
|
case VIEWPORT_INTERACTION_ITEM_FOOTPATH_ITEM:
|
|
case VIEWPORT_INTERACTION_ITEM_BANNER:
|
|
imageId &= 0x7FFFF;
|
|
imageId |= seeThoughFlags;
|
|
break;
|
|
}
|
|
}
|
|
if (viewFlags & VIEWPORT_FLAG_SEETHROUGH_SCENERY)
|
|
{
|
|
switch (spriteType)
|
|
{
|
|
case VIEWPORT_INTERACTION_ITEM_SCENERY:
|
|
case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY:
|
|
case VIEWPORT_INTERACTION_ITEM_WALL:
|
|
imageId &= 0x7FFFF;
|
|
imageId |= seeThoughFlags;
|
|
break;
|
|
}
|
|
}
|
|
return imageId;
|
|
}
|
|
|
|
static void draw_pixel_info_crop_by_zoom(rct_drawpixelinfo *dpi)
|
|
{
|
|
sint32 zoom = dpi->zoom_level;
|
|
dpi->zoom_level = 0;
|
|
dpi->x >>= zoom;
|
|
dpi->y >>= zoom;
|
|
dpi->width >>= zoom;
|
|
dpi->height >>= zoom;
|
|
}
|
|
|
|
paint_session * paint_session_alloc(rct_drawpixelinfo * dpi)
|
|
{
|
|
// Currently limited to just one session at a time
|
|
assert(!_paintSessionInUse);
|
|
_paintSessionInUse = true;
|
|
paint_session * session = &gPaintSession;
|
|
|
|
paint_session_init(session, dpi);
|
|
return session;
|
|
}
|
|
|
|
void paint_session_free(paint_session * session)
|
|
{
|
|
_paintSessionInUse = false;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
* @return (ebp) paint_struct on success (CF == 0), nullptr on failure (CF == 1)
|
|
*/
|
|
paint_struct * sub_98196C(
|
|
paint_session * session,
|
|
uint32 image_id,
|
|
sint8 x_offset,
|
|
sint8 y_offset,
|
|
sint16 bound_box_length_x,
|
|
sint16 bound_box_length_y,
|
|
sint8 bound_box_length_z,
|
|
sint16 z_offset)
|
|
{
|
|
assert((uint16)bound_box_length_x == (sint16)bound_box_length_x);
|
|
assert((uint16)bound_box_length_y == (sint16)bound_box_length_y);
|
|
|
|
session->UnkF1AD28 = nullptr;
|
|
session->UnkF1AD2C = nullptr;
|
|
|
|
if (session->NextFreePaintStruct >= session->EndOfPaintStructArray)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto g1Element = gfx_get_g1_element(image_id & 0x7FFFF);
|
|
if (g1Element == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
paint_struct *ps = &session->NextFreePaintStruct->basic;
|
|
ps->image_id = image_id;
|
|
|
|
LocationXYZ16 coord_3d =
|
|
{
|
|
x_offset, // ax
|
|
y_offset, // cx
|
|
z_offset
|
|
};
|
|
|
|
LocationXYZ16 boundBox =
|
|
{
|
|
bound_box_length_x, // di
|
|
bound_box_length_y, // si
|
|
bound_box_length_z,
|
|
};
|
|
|
|
switch (session->CurrentRotation)
|
|
{
|
|
case 0:
|
|
rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_WEST);
|
|
|
|
boundBox.x--;
|
|
boundBox.y--;
|
|
rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_WEST);
|
|
break;
|
|
|
|
case 1:
|
|
rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_SOUTH);
|
|
|
|
boundBox.x--;
|
|
rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_SOUTH);
|
|
break;
|
|
|
|
case 2:
|
|
rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_EAST);
|
|
rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_EAST);
|
|
break;
|
|
|
|
case 3:
|
|
rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_NORTH);
|
|
|
|
boundBox.y--;
|
|
rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_NORTH);
|
|
break;
|
|
}
|
|
|
|
coord_3d.x += session->SpritePosition.x;
|
|
coord_3d.y += session->SpritePosition.y;
|
|
|
|
ps->bounds.x_end = coord_3d.x + boundBox.x;
|
|
ps->bounds.y_end = coord_3d.y + boundBox.y;
|
|
|
|
// TODO: check whether this is right. edx is ((bound_box_length_z + z_offset) << 16 | z_offset)
|
|
ps->bounds.z = coord_3d.z;
|
|
ps->bounds.z_end = (boundBox.z + coord_3d.z);
|
|
|
|
LocationXY16 map = coordinate_3d_to_2d(&coord_3d, session->CurrentRotation);
|
|
|
|
ps->x = map.x;
|
|
ps->y = map.y;
|
|
|
|
sint16 left = map.x + g1Element->x_offset;
|
|
sint16 bottom = map.y + g1Element->y_offset;
|
|
|
|
sint16 right = left + g1Element->width;
|
|
sint16 top = bottom + g1Element->height;
|
|
|
|
rct_drawpixelinfo *dpi = session->Unk140E9A8;
|
|
|
|
if (right <= dpi->x) return nullptr;
|
|
if (top <= dpi->y) return nullptr;
|
|
if (left >= (dpi->x + dpi->width)) return nullptr;
|
|
if (bottom >= (dpi->y + dpi->height)) return nullptr;
|
|
|
|
ps->flags = 0;
|
|
ps->bounds.x = coord_3d.x;
|
|
ps->bounds.y = coord_3d.y;
|
|
ps->attached_ps = nullptr;
|
|
ps->var_20 = nullptr;
|
|
ps->sprite_type = session->InteractionType;
|
|
ps->var_29 = 0;
|
|
ps->map_x = session->MapPosition.x;
|
|
ps->map_y = session->MapPosition.y;
|
|
ps->tileElement = (rct_tile_element*)session->CurrentlyDrawnItem;
|
|
|
|
session->UnkF1AD28 = ps;
|
|
|
|
sint32 positionHash = 0;
|
|
switch (session->CurrentRotation)
|
|
{
|
|
case 0:
|
|
positionHash = coord_3d.y + coord_3d.x;
|
|
break;
|
|
case 1:
|
|
positionHash = coord_3d.y - coord_3d.x + 0x2000;
|
|
break;
|
|
case 2:
|
|
positionHash = -(coord_3d.y + coord_3d.x) + 0x4000;
|
|
break;
|
|
case 3:
|
|
positionHash = coord_3d.x - coord_3d.y + 0x2000;
|
|
break;
|
|
}
|
|
paint_session_add_ps_to_quadrant(session, ps, positionHash);
|
|
|
|
session->NextFreePaintStruct++;
|
|
|
|
return ps;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
* @return (ebp) paint_struct on success (CF == 0), nullptr on failure (CF == 1)
|
|
*/
|
|
paint_struct * sub_98197C(
|
|
paint_session * session,
|
|
uint32 image_id,
|
|
sint8 x_offset,
|
|
sint8 y_offset,
|
|
sint16 bound_box_length_x,
|
|
sint16 bound_box_length_y,
|
|
sint8 bound_box_length_z,
|
|
sint16 z_offset,
|
|
sint16 bound_box_offset_x,
|
|
sint16 bound_box_offset_y,
|
|
sint16 bound_box_offset_z)
|
|
{
|
|
session->UnkF1AD28 = nullptr;
|
|
session->UnkF1AD2C = nullptr;
|
|
|
|
LocationXYZ16 offset = { x_offset, y_offset, z_offset };
|
|
LocationXYZ16 boundBoxSize = { bound_box_length_x, bound_box_length_y, bound_box_length_z };
|
|
LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z };
|
|
paint_struct * ps = sub_9819_c(session, image_id, offset, boundBoxSize, boundBoxOffset);
|
|
|
|
if (ps == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
session->UnkF1AD28 = ps;
|
|
|
|
LocationXY16 attach =
|
|
{
|
|
(sint16)ps->bounds.x,
|
|
(sint16)ps->bounds.y
|
|
};
|
|
|
|
rotate_map_coordinates(&attach.x, &attach.y, session->CurrentRotation);
|
|
switch (session->CurrentRotation)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
case 3:
|
|
attach.x += 0x2000;
|
|
break;
|
|
case 2:
|
|
attach.x += 0x4000;
|
|
break;
|
|
}
|
|
|
|
sint32 positionHash = attach.x + attach.y;
|
|
paint_session_add_ps_to_quadrant(session, ps, positionHash);
|
|
|
|
session->NextFreePaintStruct++;
|
|
return ps;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* 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)
|
|
* @return (ebp) paint_struct on success (CF == 0), nullptr on failure (CF == 1)
|
|
*/
|
|
paint_struct * sub_98198C(
|
|
paint_session * session,
|
|
uint32 image_id,
|
|
sint8 x_offset,
|
|
sint8 y_offset,
|
|
sint16 bound_box_length_x,
|
|
sint16 bound_box_length_y,
|
|
sint8 bound_box_length_z,
|
|
sint16 z_offset,
|
|
sint16 bound_box_offset_x,
|
|
sint16 bound_box_offset_y,
|
|
sint16 bound_box_offset_z)
|
|
{
|
|
assert((uint16)bound_box_length_x == bound_box_length_x);
|
|
assert((uint16)bound_box_length_y == bound_box_length_y);
|
|
|
|
session->UnkF1AD28 = nullptr;
|
|
session->UnkF1AD2C = nullptr;
|
|
|
|
LocationXYZ16 offset = { x_offset, y_offset, z_offset };
|
|
LocationXYZ16 boundBoxSize = { bound_box_length_x, bound_box_length_y, bound_box_length_z };
|
|
LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z };
|
|
paint_struct * ps = sub_9819_c(session, image_id, offset, boundBoxSize, boundBoxOffset);
|
|
|
|
if (ps == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
session->UnkF1AD28 = ps;
|
|
session->NextFreePaintStruct++;
|
|
return 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)
|
|
* @return (ebp) paint_struct on success (CF == 0), nullptr on failure (CF == 1)
|
|
*/
|
|
paint_struct * sub_98199C(
|
|
paint_session * session,
|
|
uint32 image_id,
|
|
sint8 x_offset,
|
|
sint8 y_offset,
|
|
sint16 bound_box_length_x,
|
|
sint16 bound_box_length_y,
|
|
sint8 bound_box_length_z,
|
|
sint16 z_offset,
|
|
sint16 bound_box_offset_x,
|
|
sint16 bound_box_offset_y,
|
|
sint16 bound_box_offset_z)
|
|
{
|
|
assert((uint16)bound_box_length_x == (sint16)bound_box_length_x);
|
|
assert((uint16)bound_box_length_y == (sint16)bound_box_length_y);
|
|
|
|
if (session->UnkF1AD28 == nullptr)
|
|
{
|
|
return sub_98197C(
|
|
session, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
|
|
bound_box_offset_x, bound_box_offset_y, bound_box_offset_z);
|
|
}
|
|
|
|
LocationXYZ16 offset = { x_offset, y_offset, z_offset };
|
|
LocationXYZ16 boundBox = { bound_box_length_x, bound_box_length_y, bound_box_length_z };
|
|
LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z };
|
|
paint_struct * ps = sub_9819_c(session, image_id, offset, boundBox, boundBoxOffset);
|
|
|
|
if (ps == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
paint_struct *old_ps = session->UnkF1AD28;
|
|
old_ps->var_20 = ps;
|
|
|
|
session->UnkF1AD28 = ps;
|
|
session->NextFreePaintStruct++;
|
|
return ps;
|
|
}
|
|
|
|
/**
|
|
* rct2: 0x006881D0
|
|
*
|
|
* @param image_id (ebx)
|
|
* @param x (ax)
|
|
* @param y (cx)
|
|
* @return (!CF) success
|
|
*/
|
|
bool paint_attach_to_previous_attach(paint_session * session, uint32 image_id, uint16 x, uint16 y)
|
|
{
|
|
if (session->UnkF1AD2C == nullptr)
|
|
{
|
|
return paint_attach_to_previous_ps(session, image_id, x, y);
|
|
}
|
|
|
|
if (session->NextFreePaintStruct >= session->EndOfPaintStructArray)
|
|
{
|
|
return false;
|
|
}
|
|
attached_paint_struct * ps = &session->NextFreePaintStruct->attached;
|
|
ps->image_id = image_id;
|
|
ps->x = x;
|
|
ps->y = y;
|
|
ps->flags = 0;
|
|
|
|
attached_paint_struct * ebx = session->UnkF1AD2C;
|
|
|
|
ps->next = nullptr;
|
|
ebx->next = ps;
|
|
|
|
session->UnkF1AD2C = ps;
|
|
|
|
session->NextFreePaintStruct++;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* rct2: 0x0068818E
|
|
*
|
|
* @param image_id (ebx)
|
|
* @param x (ax)
|
|
* @param y (cx)
|
|
* @return (!CF) success
|
|
*/
|
|
bool paint_attach_to_previous_ps(paint_session * session, uint32 image_id, uint16 x, uint16 y)
|
|
{
|
|
if (session->NextFreePaintStruct >= session->EndOfPaintStructArray)
|
|
{
|
|
return false;
|
|
}
|
|
attached_paint_struct * ps = &session->NextFreePaintStruct->attached;
|
|
|
|
ps->image_id = image_id;
|
|
ps->x = x;
|
|
ps->y = y;
|
|
ps->flags = 0;
|
|
|
|
paint_struct * masterPs = session->UnkF1AD28;
|
|
if (masterPs == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
session->NextFreePaintStruct++;
|
|
|
|
attached_paint_struct * oldFirstAttached = masterPs->attached_ps;
|
|
masterPs->attached_ps = ps;
|
|
|
|
ps->next = oldFirstAttached;
|
|
|
|
session->UnkF1AD2C = ps;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* rct2: 0x00685EBC, 0x00686046, 0x00685FC8, 0x00685F4A, 0x00685ECC
|
|
* @param amount (eax)
|
|
* @param string_id (bx)
|
|
* @param y (cx)
|
|
* @param z (dx)
|
|
* @param offset_x (si)
|
|
* @param y_offsets (di)
|
|
* @param rotation (ebp)
|
|
*/
|
|
void paint_floating_money_effect(paint_session * session, money32 amount, rct_string_id string_id, sint16 y, sint16 z, sint8 y_offsets[], sint16 offset_x, uint32 rotation)
|
|
{
|
|
if (session->NextFreePaintStruct >= session->EndOfPaintStructArray)
|
|
{
|
|
return;
|
|
}
|
|
|
|
paint_string_struct * ps = &session->NextFreePaintStruct->string;
|
|
ps->string_id = string_id;
|
|
ps->next = nullptr;
|
|
ps->args[0] = amount;
|
|
ps->args[1] = y;
|
|
ps->args[2] = 0;
|
|
ps->args[3] = 0;
|
|
ps->y_offsets = (uint8 *)y_offsets;
|
|
|
|
const LocationXYZ16 position =
|
|
{
|
|
session->SpritePosition.x,
|
|
session->SpritePosition.y,
|
|
z
|
|
};
|
|
const LocationXY16 coord = coordinate_3d_to_2d(&position, rotation);
|
|
|
|
ps->x = coord.x + offset_x;
|
|
ps->y = coord.y;
|
|
|
|
session->NextFreePaintStruct++;
|
|
|
|
if (session->LastPSString == nullptr)
|
|
{
|
|
session->PSStringHead = ps;
|
|
}
|
|
else
|
|
{
|
|
session->LastPSString->next = ps;
|
|
}
|
|
session->LastPSString = ps;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006860C3
|
|
*/
|
|
void paint_draw_money_structs(rct_drawpixelinfo * dpi, paint_string_struct * ps)
|
|
{
|
|
rct_drawpixelinfo dpi2 = *dpi;
|
|
draw_pixel_info_crop_by_zoom(&dpi2);
|
|
|
|
do
|
|
{
|
|
utf8 buffer[256];
|
|
format_string(buffer, 256, ps->string_id, &ps->args);
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
|
|
|
|
// Use sprite font unless the currency contains characters unsupported by the sprite font
|
|
bool forceSpriteFont = false;
|
|
const currency_descriptor& currencyDesc = CurrencyDescriptors[gConfigGeneral.currency_format];
|
|
if (LocalisationService_UseTrueTypeFont() && font_supports_string_sprite(currencyDesc.symbol_unicode))
|
|
{
|
|
forceSpriteFont = true;
|
|
}
|
|
|
|
gfx_draw_string_with_y_offsets(&dpi2, buffer, COLOUR_BLACK, ps->x, ps->y, (sint8 *)ps->y_offsets, forceSpriteFont);
|
|
} while ((ps = ps->next) != nullptr);
|
|
} |