1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

Extract Side tunnels

This commit is contained in:
Marijn van der Werf
2016-10-17 11:57:41 +02:00
parent cb4519e9c5
commit 9efb41a13f
10 changed files with 361 additions and 319 deletions

View File

@@ -34,6 +34,7 @@
C606CCC91DB4054000FE4015 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCBC1DB4054000FE4015 /* Utils.cpp */; };
C606CCCE1DB427A000FE4015 /* GeneralSupportHeightCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCCA1DB427A000FE4015 /* GeneralSupportHeightCall.cpp */; };
C606CCCF1DB427A000FE4015 /* SegmentSupportHeightCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */; };
C606CCD21DB4D7C800FE4015 /* SideTunnelCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */; };
C612A8991D64825300B634CA /* vehicle_data.c in Sources */ = {isa = PBXBuildFile; fileRef = C612A8971D64825300B634CA /* vehicle_data.c */; };
C61FB7241CF86356004CE991 /* NetworkUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61FB7221CF86356004CE991 /* NetworkUser.cpp */; };
C64FDA641D6D9A2100F259B9 /* air_powered_vertical_coaster.c in Sources */ = {isa = PBXBuildFile; fileRef = C686F8BB1CDBC3B7009F9BFC /* air_powered_vertical_coaster.c */; };
@@ -526,6 +527,8 @@
C606CCCB1DB427A000FE4015 /* GeneralSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeneralSupportHeightCall.hpp; sourceTree = "<group>"; };
C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentSupportHeightCall.cpp; sourceTree = "<group>"; };
C606CCCD1DB427A000FE4015 /* SegmentSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SegmentSupportHeightCall.hpp; sourceTree = "<group>"; };
C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SideTunnelCall.cpp; sourceTree = "<group>"; };
C606CCD11DB4D7C800FE4015 /* SideTunnelCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SideTunnelCall.hpp; sourceTree = "<group>"; };
C612A8971D64825300B634CA /* vehicle_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vehicle_data.c; sourceTree = "<group>"; };
C612A8981D64825300B634CA /* vehicle_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vehicle_data.h; sourceTree = "<group>"; };
C61FB7221CF86356004CE991 /* NetworkUser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkUser.cpp; sourceTree = "<group>"; usesTabs = 0; };
@@ -1208,6 +1211,8 @@
C606CCB71DB4054000FE4015 /* Printer.hpp */,
C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */,
C606CCCD1DB427A000FE4015 /* SegmentSupportHeightCall.hpp */,
C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */,
C606CCD11DB4D7C800FE4015 /* SideTunnelCall.hpp */,
C606CCB81DB4054000FE4015 /* String.cpp */,
C606CCB91DB4054000FE4015 /* String.hpp */,
C606CCBA1DB4054000FE4015 /* TestTrack.cpp */,
@@ -2399,6 +2404,7 @@
C64FDAC01D6D9E3B00F259B9 /* track_data.c in Sources */,
C64FDABF1D6D9CEA00F259B9 /* map_element.c in Sources */,
C64FDABE1D6D9CD900F259B9 /* ride_data.c in Sources */,
C606CCD21DB4D7C800FE4015 /* SideTunnelCall.cpp in Sources */,
C64FDABC1D6D9C8800F259B9 /* addresses.c in Sources */,
C64FDA641D6D9A2100F259B9 /* air_powered_vertical_coaster.c in Sources */,
C64FDA651D6D9A2100F259B9 /* bobsleigh_coaster.c in Sources */,

View File

@@ -38,6 +38,8 @@ namespace Printer {
static std::string PrintSegmentSupportHeightCall(SegmentSupportCall call);
static std::string PrintSideTunnelEdge(TunnelCall edge);
std::string PrintFunctionCalls(std::vector<function_call> calls, uint16 baseHeight) {
std::string out;
@@ -140,6 +142,73 @@ namespace Printer {
return out;
}
std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]) {
std::string s;
for (int direction = 0; direction < 4; ++direction) {
s += " + ";
}
s += "\n";
for (int direction = 0; direction < 4; ++direction) {
std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][2]);
std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][3]);
s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str());
}
s += "\n";
for (int direction = 0; direction < 4; ++direction) {
printf(" + + ");
}
s += "\n";
for (int direction = 0; direction < 4; ++direction) {
std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][0]);
std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][1]);
s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str());
}
s += "\n";
for (int direction = 0; direction < 4; ++direction) {
s += " + ";
}
s += "\n";
for (int direction = 0; direction < 4; ++direction) {
s += String::Format(" direction %d ", direction);
}
s += "\n";
return s;
}
static std::string PrintSideTunnelEdge(TunnelCall edge) {
std::string s;
switch (edge.call) {
case TUNNELCALL_SKIPPED:
s = " ";
break;
case TUNNELCALL_NONE:
s = " - ";
break;
case TUNNELCALL_CALL:
std::string offset;
if (edge.offset <= 0) {
offset = String::Format("%d", edge.offset);
} else {
offset = String::Format("+%d", edge.offset);
}
s = String::Format("%3s/%X ", offset.c_str(), edge.type);
break;
}
return s;
}
static std::string GetImageIdString(uint32 imageId)
{
std::string result;

View File

@@ -20,6 +20,7 @@
#include <vector>
#include "intercept.h"
#include "SideTunnelCall.hpp"
#include "SegmentSupportHeightCall.hpp"
namespace Printer {
@@ -27,4 +28,6 @@ namespace Printer {
std::string PrintFunctionCalls(std::vector <function_call> calls, uint16 baseHeight);
std::string PrintSegmentSupportHeightCalls(std::vector<SegmentSupportCall> calls);
std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]);
}

View File

@@ -0,0 +1,106 @@
#pragma region Copyright (c) 2014-2016 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 "SideTunnelCall.hpp"
sint16 SideTunnelCall::GetTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]) {
for (sint16 offset = -56; offset <= 56; offset += 8) {
if (calls[0].height != (baseHeight - 8 + offset) / 16) continue;
if (calls[1].height != (baseHeight + 0 + offset) / 16) continue;
if (calls[2].height != (baseHeight + 8 + offset) / 16) continue;
return offset;
}
log_error("Unknown tunnel height. (%d, %d, %d)", calls[0].height, calls[1].height, calls[2].height);
return 0;
}
TunnelCall SideTunnelCall::ExtractTunnelCalls(tunnel_entry *calls, uint8 count, uint16 baseHeight, bool *error) {
TunnelCall tunnelCall = {0};
if (count == 0) {
tunnelCall.call = TUNNELCALL_NONE;
return tunnelCall;
}
if (count == 3) {
tunnelCall.call = TUNNELCALL_CALL;
tunnelCall.offset = GetTunnelOffset(baseHeight, calls);
tunnelCall.type = calls[0].type;
return tunnelCall;
}
*error = true;
return tunnelCall;
}
bool SideTunnelCall::TunnelCallsLineUp(TunnelCall tunnelCalls[4][4])
{
for (int side = 0; side < 4; ++side) {
TunnelCall * referenceCall = nullptr;
for (int direction = 0; direction < 4; ++direction) {
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) {
continue;
}
if (referenceCall == nullptr) {
referenceCall = &tunnelCalls[direction][side];
continue;
}
if (referenceCall->call != tunnelCalls[direction][side].call) return false;
if (referenceCall->call == TUNNELCALL_CALL) {
if (referenceCall->type != tunnelCalls[direction][side].type) return false;
if (referenceCall->offset != tunnelCalls[direction][side].offset) return false;
}
}
}
return true;
}
void SideTunnelCall::GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4])
{
for (int side = 0; side < 4; ++side) {
for (int direction = 0; direction < 4; ++direction) {
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) {
continue;
}
(*out)[side].call = tunnelCalls[direction][side].call;
(*out)[side].type = tunnelCalls[direction][side].type;
(*out)[side].offset = tunnelCalls[direction][side].offset;
}
}
}
bool SideTunnelCall::TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4])
{
for (int side = 0; side < 4; side++) {
if (expected[side].call != actual[side].call) return false;
if (expected[side].call == TUNNELCALL_CALL) {
if (expected[side].type != actual[side].type) return false;
if (expected[side].offset != actual[side].offset) return false;
}
}
return true;
}

View File

@@ -0,0 +1,44 @@
#pragma region Copyright (c) 2014-2016 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
#pragma once
#include "../../src/common.h"
extern "C" {
#include "../../src/paint/map_element/map_element.h"
}
enum {
TUNNELCALL_SKIPPED,
TUNNELCALL_NONE,
TUNNELCALL_CALL,
};
struct TunnelCall {
uint8 call;
sint16 offset;
uint8 type;
};
namespace SideTunnelCall {
sint16 GetTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]);
TunnelCall ExtractTunnelCalls(tunnel_entry * list, uint8 count, uint16 baseHeight, bool * error);
bool TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4]);
void GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4]);
bool TunnelCallsLineUp(TunnelCall tunnelCalls[4][4]);
};

View File

@@ -22,6 +22,7 @@
#include "GeneralSupportHeightCall.hpp"
#include "Printer.hpp"
#include "SegmentSupportHeightCall.hpp"
#include "SideTunnelCall.hpp"
#include "String.hpp"
#include "TestTrack.hpp"
#include "Utils.hpp"
@@ -78,6 +79,8 @@ static uint8 TestTrackElementSegmentSupportHeight(uint8 rideType, uint8 trackTyp
static uint8 TestTrackElementGeneralSupportHeight(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error);
static uint8 TestTrackElementSideTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error);
uint8 TestTrack::TestPaintTrackElement(uint8 rideType, uint8 trackType) {
if (!Utils::rideSupportsTrackType(rideType, trackType)) {
return TEST_FAILED;
@@ -99,7 +102,8 @@ uint8 TestTrack::TestPaintTrackElement(uint8 rideType, uint8 trackType) {
static TestFunction functions[] = {
TestTrackElementPaintCalls,
TestTrackElementSegmentSupportHeight,
TestTrackElementGeneralSupportHeight
TestTrackElementGeneralSupportHeight,
TestTrackElementSideTunnels,
};
for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) {
@@ -380,3 +384,117 @@ static uint8 TestTrackElementGeneralSupportHeight(uint8 rideType, uint8 trackTyp
return TEST_SUCCESS;
}
static uint8 TestTrackElementSideTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) {
uint8 rideIndex = 0;
uint16 height = 3 * 16;
rct_map_element mapElement = {0};
mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE;
mapElement.properties.track.type = trackType;
mapElement.base_height = height / 16;
g_currently_drawn_item = &mapElement;
rct_map_element surfaceElement = {0};
surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE;
surfaceElement.base_height = 2;
gSurfaceElement = &surfaceElement;
gDidPassSurface = true;
Intercept2::ResetEnvironment();
Intercept2::ResetTunnels();
TunnelCall tileTunnelCalls[4][4];
for (int direction = 0; direction < 4; direction++) {
Intercept2::ResetTunnels();
for (sint8 offset = -8; offset <= 8; offset += 8) {
CallOriginal(rideType, trackType, direction, trackSequence, height + offset, &mapElement);
}
uint8 rightIndex = (4 - direction) % 4;
uint8 leftIndex = (rightIndex + 1) % 4;
for (int i = 0; i < 4; ++i) {
tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
}
bool err = false;
tileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(gRightTunnels, gRightTunnelCount, height,
&err);
tileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(gLeftTunnels, gLeftTunnelCount, height,
&err);
if (err) {
*error += "Multiple tunnels on one side aren't supported.\n";
return TEST_FAILED;
}
}
TunnelCall newTileTunnelCalls[4][4];
for (int direction = 0; direction < 4; direction++) {
Intercept2::ResetTunnels();
testpaint_clear_ignore();
for (sint8 offset = -8; offset <= 8; offset += 8) {
// TODO: move tunnel pushing to interface so we don't have to check the output 3 times
CallNew(rideType, trackType, direction, trackSequence, height + offset, &mapElement);
}
uint8 rightIndex = (4 - direction) % 4;
uint8 leftIndex = (rightIndex + 1) % 4;
for (int i = 0; i < 4; ++i) {
newTileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
}
bool err = false;
newTileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(gRightTunnels, gRightTunnelCount, height,
&err);
newTileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(gLeftTunnels, gLeftTunnelCount, height,
&err);
if (err) {
*error += "Multiple tunnels on one side aren't supported.\n";
return TEST_FAILED;
}
}
if (!SideTunnelCall::TunnelCallsLineUp(tileTunnelCalls)) {
*error += String::Format(
"Original tunnel calls don\'t line up. Skipping tunnel validation [trackSequence:%d].\n",
trackSequence
);
*error += Printer::PrintSideTunnelCalls(tileTunnelCalls);
if (!SideTunnelCall::TunnelCallsLineUp(newTileTunnelCalls)) {
*error += String::Format("Decompiled tunnel calls don\'t line up. [trackSequence:%d].\n", trackSequence);
*error += Printer::PrintSideTunnelCalls(newTileTunnelCalls);
return TEST_FAILED;
}
return TEST_SUCCESS;
}
TunnelCall referencePattern[4];
SideTunnelCall::GetTunnelCallReferencePattern(tileTunnelCalls, &referencePattern);
TunnelCall actualPattern[4];
SideTunnelCall::GetTunnelCallReferencePattern(newTileTunnelCalls, &actualPattern);
if (!SideTunnelCall::TunnelPatternsMatch(referencePattern, actualPattern)) {
*error += String::Format("Tunnel calls don't match expected pattern. [trackSequence:%d]\n", trackSequence);
*error += " Expected:\n";
*error += Printer::PrintSideTunnelCalls(tileTunnelCalls);
*error += " Actual:\n";
*error += Printer::PrintSideTunnelCalls(newTileTunnelCalls);
return TEST_FAILED;
}
return TEST_SUCCESS;
}

View File

@@ -20,6 +20,7 @@
#include "intercept.h"
#include "SegmentSupportHeightCall.hpp"
#include "SideTunnelCall.hpp"
#include "String.hpp"
#include "Utils.hpp"
@@ -400,7 +401,7 @@ private:
bool blockSegmentsBeforeSupports = false;
std::vector<function_call> calls[4], chainLiftCalls[4], cableLiftCalls[4];
Intercept2::TunnelCall tileTunnelCalls[4][4];
TunnelCall tileTunnelCalls[4][4];
sint16 verticalTunnelHeights[4];
std::vector<SegmentSupportCall> segmentSupportCalls[4];
support_height generalSupports[4] = { 0 };
@@ -737,7 +738,7 @@ private:
int trackSequence,
int height,
rct_map_element * mapElement,
Intercept2::TunnelCall tileTunnelCalls[4][4],
TunnelCall tileTunnelCalls[4][4],
sint16 verticalTunnelHeights[4])
{
intercept_reset_tunnels();
@@ -751,13 +752,13 @@ private:
uint8 leftIndex = (rightIndex + 1) % 4;
for (int i = 0; i < 4; ++i) {
tileTunnelCalls[direction][i].call = Intercept2::TUNNELCALL_SKIPPED;
tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
}
if (gRightTunnelCount == 0) {
tileTunnelCalls[direction][rightIndex].call = Intercept2::TUNNELCALL_NONE;
tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE;
} else if (gRightTunnelCount == 3) {
tileTunnelCalls[direction][rightIndex].call = Intercept2::TUNNELCALL_CALL;
tileTunnelCalls[direction][rightIndex].offset = Intercept2::getTunnelOffset(height, gRightTunnels);
tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL;
tileTunnelCalls[direction][rightIndex].offset = SideTunnelCall::GetTunnelOffset(height, gRightTunnels);
tileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
@@ -765,10 +766,10 @@ private:
}
if (gLeftTunnelCount == 0) {
tileTunnelCalls[direction][leftIndex].call = Intercept2::TUNNELCALL_NONE;
tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE;
} else if (gLeftTunnelCount == 3) {
tileTunnelCalls[direction][leftIndex].call = Intercept2::TUNNELCALL_CALL;
tileTunnelCalls[direction][leftIndex].offset = Intercept2::getTunnelOffset(height, gLeftTunnels);
tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL;
tileTunnelCalls[direction][leftIndex].offset = SideTunnelCall::GetTunnelOffset(height, gLeftTunnels);
tileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
@@ -788,7 +789,7 @@ private:
return true;
}
void GenerateTunnelCall(int tabs, Intercept2::TunnelCall tileTunnelCalls[4][4], sint16 verticalTunnelHeights[4])
void GenerateTunnelCall(int tabs, TunnelCall tileTunnelCalls[4][4], sint16 verticalTunnelHeights[4])
{
constexpr uint8 TunnelLeft = 0;
constexpr uint8 TunnelRight = 1;
@@ -809,7 +810,7 @@ private:
for (int side = 0; side < 4; side++)
{
auto tunnel = tileTunnelCalls[direction][side];
if (tunnel.call == Intercept2::TUNNELCALL_CALL)
if (tunnel.call == TUNNELCALL_CALL)
{
tunnelOffset[direction] = tunnel.offset;
tunnelType[direction] = tunnel.type;
@@ -857,7 +858,7 @@ private:
WriteLine(tabs, "case %d:", i);
for (int side = 0; side < 4; side++)
{
if (tileTunnelCalls[i][side].call == Intercept2::TUNNELCALL_CALL)
if (tileTunnelCalls[i][side].call == TUNNELCALL_CALL)
{
GenerateTunnelCall(tabs + 1, tileTunnelCalls[i][side].offset, tileTunnelCalls[i][side].type, dsToWay[i][side]);
}

View File

@@ -107,19 +107,7 @@ namespace Intercept2
static const uint32 DEFAULT_SCHEME_MISC = COLOUR_DARK_PURPLE << 19 | COLOUR_LIGHT_PURPLE << 24 | 0xA0000000;
static const uint32 DEFAULT_SCHEME_3 = COLOUR_BRIGHT_PURPLE << 19 | COLOUR_DARK_BLUE << 24 | 0xA0000000;
enum {
TUNNELCALL_SKIPPED,
TUNNELCALL_NONE,
TUNNELCALL_CALL,
};
struct TunnelCall {
uint8 call;
sint16 offset;
uint8 type;
};
sint16 getTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]);
void ResetEnvironment();
void ResetTunnels();

View File

@@ -86,295 +86,6 @@ namespace Intercept2
gSupport.slope = 0xFF;
}
static bool tunnelCallsLineUp(TunnelCall tunnelCalls[4][4])
{
for (int side = 0; side < 4; ++side) {
TunnelCall * referenceCall = nullptr;
for (int direction = 0; direction < 4; ++direction) {
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) {
continue;
}
if (referenceCall == nullptr) {
referenceCall = &tunnelCalls[direction][side];
continue;
}
if (referenceCall->call != tunnelCalls[direction][side].call) return false;
if (referenceCall->call == TUNNELCALL_CALL) {
if (referenceCall->type != tunnelCalls[direction][side].type) return false;
if (referenceCall->offset != tunnelCalls[direction][side].offset) return false;
}
}
}
return true;
}
static void getTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4])
{
for (int side = 0; side < 4; ++side) {
for (int direction = 0; direction < 4; ++direction) {
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) {
continue;
}
(*out)[side].call = tunnelCalls[direction][side].call;
(*out)[side].type = tunnelCalls[direction][side].type;
(*out)[side].offset = tunnelCalls[direction][side].offset;
}
}
}
static utf8string getTunnelEdgeString(TunnelCall edge)
{
utf8string out = new utf8[32];
switch (edge.call) {
case TUNNELCALL_SKIPPED:
snprintf(out, 32, "%s", " ");
break;
case TUNNELCALL_NONE:
snprintf(out, 32, "%s", " - ");
break;
case TUNNELCALL_CALL:
if (edge.offset == 0) {
snprintf(out, 32, " 0/%X ", edge.type);
} else {
utf8string offset = new utf8[16];
if (edge.offset < 0) {
snprintf(offset, 16, "%d", edge.offset);
} else {
snprintf(offset, 16, "+%d", edge.offset);
}
snprintf(out, 32, "%3s/%X ", offset, edge.type);
delete[] offset;
}
break;
}
return out;
}
static void printTunnelCalls(TunnelCall tunnelCalls[4][4])
{
for (int direction = 0; direction < 4; ++direction) {
printf(" + ");
}
printf("\n");
for (int direction = 0; direction < 4; ++direction) {
utf8string tlEdge = getTunnelEdgeString(tunnelCalls[direction][2]);
utf8string trEdge = getTunnelEdgeString(tunnelCalls[direction][3]);
printf(" %s %s ", tlEdge, trEdge);
delete [] tlEdge;
delete [] trEdge;
}
printf("\n");
for (int direction = 0; direction < 4; ++direction) {
printf(" + + ");
}
printf("\n");
for (int direction = 0; direction < 4; ++direction) {
utf8string brEdge = getTunnelEdgeString(tunnelCalls[direction][0]);
utf8string blEdge = getTunnelEdgeString(tunnelCalls[direction][1]);
printf(" %s %s ", blEdge, brEdge);
delete [] blEdge;
delete [] brEdge;
}
printf("\n");
for (int direction = 0; direction < 4; ++direction) {
printf(" + ");
}
printf("\n");
for (int direction = 0; direction < 4; ++direction) {
printf(" direction %d ", direction);
}
printf("\n");
}
static bool tunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4])
{
for (int side = 0; side < 4; side++) {
if (expected[side].call != actual[side].call) return false;
if (expected[side].call == TUNNELCALL_CALL) {
if (expected[side].type != actual[side].type) return false;
if (expected[side].offset != actual[side].offset) return false;
}
}
return true;
}
sint16 getTunnelOffset(uint32 baseHeight, tunnel_entry calls[3])
{
for (sint16 offset = -56; offset <= 56; offset += 8) {
if (calls[0].height != (baseHeight - 8 + offset) / 16) continue;
if (calls[1].height != (baseHeight + 0 + offset) / 16) continue;
if (calls[2].height != (baseHeight + 8 + offset) / 16) continue;
return offset;
}
log_error("Unknown tunnel height. (%d, %d, %d)", calls[0].height, calls[1].height, calls[2].height);
return 0;
}
static bool testTunnels(uint8 rideType, uint8 trackType)
{
uint8 rideIndex = 0;
rct_map_element mapElement = {0};
mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE;
mapElement.properties.track.type = trackType;
mapElement.base_height = 3;
g_currently_drawn_item = &mapElement;
ResetEnvironment();
int height = 48;
TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType];
int sequenceCount = Utils::getTrackSequenceCount(rideType, trackType);
for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) {
TunnelCall tileTunnelCalls[4][4];
for (int direction = 0; direction < 4; direction++) {
ResetTunnels();
uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[rideType][trackType];
for (int offset = -8; offset <= 8; offset += 8) {
// 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 + offset,
(int) &mapElement,
rideIndex * sizeof(rct_ride),
trackSequence
);
}
uint8 rightIndex = (4 - direction) % 4;
uint8 leftIndex = (rightIndex + 1) % 4;
for (int i = 0; i < 4; ++i) {
tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
}
if (gRightTunnelCount == 0) {
tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE;
} else if (gRightTunnelCount == 3) {
tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL;
tileTunnelCalls[direction][rightIndex].offset = getTunnelOffset(height, gRightTunnels);
tileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
return false;
}
if (gLeftTunnelCount == 0) {
tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE;
} else if (gLeftTunnelCount == 3) {
tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL;
tileTunnelCalls[direction][leftIndex].offset = getTunnelOffset(height, gLeftTunnels);
tileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
return false;
}
}
TunnelCall newTileTunnelCalls[4][4];
for (int direction = 0; direction < 4; direction++) {
gLeftTunnelCount = 0;
gRightTunnelCount = 0;
testpaint_clear_ignore();
TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction);
for (int offset = -8; offset <= 8; offset += 8) {
// TODO: move tunnel pushing to interface so we don't have to check the output 3 times
newPaintFunction(rideIndex, trackSequence, direction, height + offset, &mapElement);
}
uint8 rightIndex = (4 - direction) % 4;
uint8 leftIndex = (rightIndex + 1) % 4;
for (int i = 0; i < 4; ++i) {
newTileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
}
if (gRightTunnelCount == 0) {
newTileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE;
} else if (gRightTunnelCount == 3) {
newTileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL;
newTileTunnelCalls[direction][rightIndex].offset = getTunnelOffset(height, gRightTunnels);
newTileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
return false;
}
if (gLeftTunnelCount == 0) {
newTileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE;
} else if (gLeftTunnelCount == 3) {
newTileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL;
newTileTunnelCalls[direction][leftIndex].offset = getTunnelOffset(height, gLeftTunnels);
newTileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type;
} else {
printf("Multiple tunnels on one side aren't supported.\n");
return false;
}
}
if (!tunnelCallsLineUp(tileTunnelCalls)) {
printf("Original tunnel calls don\'t line up. Skipping tunnel validation [trackSequence:%d].\n",
trackSequence);
printTunnelCalls(tileTunnelCalls);
if (!tunnelCallsLineUp(newTileTunnelCalls)) {
printf("Decompiled tunnel calls don\'t line up. [trackSequence:%d].\n", trackSequence);
printTunnelCalls(newTileTunnelCalls);
return false;
}
continue;
}
TunnelCall referencePattern[4] = { 0 };
getTunnelCallReferencePattern(tileTunnelCalls, &referencePattern);
TunnelCall actualPattern[4] = { 0 };
getTunnelCallReferencePattern(newTileTunnelCalls, &actualPattern);
if (!tunnelPatternsMatch(referencePattern, actualPattern)) {
printf("Tunnel calls don't match expected pattern. [trackSequence:%d]\n", trackSequence);
printf("expected:\n");
printTunnelCalls(tileTunnelCalls);
printf("actual:\n");
printTunnelCalls(newTileTunnelCalls);
return false;
}
}
return true;
}
static bool verticalTunnelHeightIsConsistent(uint8 heights[4])
{
for (int i = 1; i < 4; ++i) {
@@ -539,11 +250,6 @@ namespace Intercept2
extern "C"
{
bool testTunnels(uint8 rideType, uint8 trackType)
{
return Intercept2::testTunnels(rideType, trackType);
}
bool testVerticalTunnels(uint8 rideType, uint8 trackType)
{
return Intercept2::testVerticalTunnels(rideType, trackType);

View File

@@ -103,6 +103,7 @@
<ClCompile Include="PaintIntercept.cpp" />
<ClCompile Include="Printer.cpp" />
<ClCompile Include="SegmentSupportHeightCall.cpp" />
<ClCompile Include="SideTunnelCall.cpp" />
<ClCompile Include="String.cpp" />
<ClCompile Include="TestTrack.cpp" />
<ClCompile Include="Utils.cpp" />