diff --git a/engines/ultima/ultima8/world/sort_item.h b/engines/ultima/ultima8/world/sort_item.h index 76e95e55ffa..7eda5e1a888 100644 --- a/engines/ultima/ultima8/world/sort_item.h +++ b/engines/ultima/ultima8/world/sort_item.h @@ -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; diff --git a/test/engines/ultima/ultima8/world/sort_item.h b/test/engines/ultima/ultima8/world/sort_item.h index 4c4d02e8ed5..fd205a24e4c 100644 --- a/test/engines/ultima/ultima8/world/sort_item.h +++ b/test/engines/ultima/ultima8/world/sort_item.h @@ -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)); + } };