ULTIMA8: Additional item sort rules and test.

This also fixes #14035
This commit is contained in:
Matthew Jimenez 2023-01-22 11:41:29 -06:00
parent 7c18fd44c3
commit 3637430a41
2 changed files with 89 additions and 11 deletions

View file

@ -22,6 +22,7 @@
#ifndef ULTIMA8_WORLD_SORTITEM_H
#define ULTIMA8_WORLD_SORTITEM_H
#include "common/str.h"
#include "ultima/ultima8/misc/common_types.h"
namespace Ultima {
@ -341,8 +342,8 @@ inline bool SortItem::below(const SortItem &si2) const {
// Specialist z flat handling
if (si1._flat && si2._flat) {
// Differing z is easy for flats
if (si1._zTop != si2._zTop)
return si1._zTop < si2._zTop;
if (si1._z != si2._z)
return si1._z < si2._z;
// Equal z
@ -381,22 +382,32 @@ inline bool SortItem::below(const SortItem &si2) const {
}
// Clearly in z
if (si1._zTop <= si2._z)
if (si1._z < si2._z && si1._zTop <= si2._z)
return true;
if (si1._z >= si2._zTop)
if (si1._z > si2._z && si1._z >= si2._zTop)
return false;
// Overlapping z-bottom check
// If an object's base (z-bottom) is higher another's, it should be rendered after.
// This check must be on the z-bottom and not the z-top because two objects with the
// same z-position may have different heights (think of a mouse sorting vs the Avatar).
if (si1._z != si2._z)
return si1._z < si2._z;
// Equal z
// Flat always gets drawn before
if (si1._flat != si2._flat)
return si1._flat > si2._flat;
// Trans always gets drawn after
if (si1._trans != si2._trans)
return si1._trans < si2._trans;
}
// Are overlapping in all 3 dimentions if we come here
// Overlapping z-bottom check
// If an object's base (z-bottom) is higher another's, it should be rendered after.
// This check must be on the z-bottom and not the z-top because two objects with the
// same z-position may have different heights (think of a mouse sorting vs the Avatar).
if (si1._z != si2._z)
return si1._z < si2._z;
// Land always gets drawn first
if (si1._land != si2._land)
return si1._land > si2._land;

View file

@ -155,6 +155,37 @@ class U8SortItemTestSuite : public CxxTest::TestSuite {
TS_ASSERT(!si2.below(si1));
}
/**
* Overlapping non-flat items draw transparent after
* Test case for rendering issue at MainActor::teleport 41 17627 16339 48
* Wall with window should render after non-window wall
*/
void test_nonflat_tranparent_sort() {
Ultima::Ultima8::SortItem si1(nullptr);
Ultima::Ultima8::SortItem si2(nullptr);
si1._x = 32;
si1._y = 96;
si1._z = 0;
si1._xLeft = 0;
si1._yFar = 0;
si1._zTop = 40;
si1._solid = true;
si2._x = 32;
si2._y = 160;
si2._z = 0;
si2._xLeft = 0;
si2._yFar = 32;
si2._zTop = 40;
si2._trans = true;
si2._solid = true;
si2._land = true;
TS_ASSERT(si1.below(si2));
TS_ASSERT(!si2.below(si1));
}
/* Overlapping non-flat occludes flat */
void test_basic_occludes() {
Ultima::Ultima8::SortItem si1(nullptr);
@ -173,4 +204,40 @@ class U8SortItemTestSuite : public CxxTest::TestSuite {
TS_ASSERT(si1.occludes(si2));
TS_ASSERT(!si2.occludes(si1));
}
/**
* Overlapping non-flat does occlude flat due to frame offset
* Test case for rendering issue at MainActor::teleport 49 19167 17582 48
*/
void test_frame_offset_occludes() {
Ultima::Ultima8::SortItem si1(nullptr);
Ultima::Ultima8::SortItem si2(nullptr);
si1._xLeft = si2._xLeft = 0;
si1._yFar = si2._yFar = 0;
si1._z = si2._z = 0;
si1._y = si2._y = 128;
si1._x = si2._x = 128;
si1._zTop = 16;
si2._zTop = 0;
si1.calculateBoxBounds(0, 0);
si2.calculateBoxBounds(0, 0);
// ShapeFrame (240:1)
si1._sx = si1._sxBot - 32;
si1._sy = si1._syBot - 48;
si1._sx2 = si1._sx + 65;
si1._sy2 = si1._sy + 48;
// ShapeFrame (301:1)
si2._sx = si2._sxBot - 31;
si2._sy = si2._syBot - 31;
si2._sx2 = si2._sx + 62;
si2._sy2 = si2._sy + 32;
// FIXME: This case fails here currently
//TS_ASSERT(!si1.occludes(si2));
TS_ASSERT(!si2.occludes(si1));
}
};