1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD 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, version 2.
4  * OpenTTD 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file script_tunnel.cpp Implementation of ScriptTunnel. */
9 
10 #include "../../stdafx.h"
11 #include "script_tunnel.hpp"
12 #include "script_rail.hpp"
13 #include "../script_instance.hpp"
14 #include "../../tunnel_map.h"
15 
16 #include "../../safeguards.h"
17 
IsTunnelTile(TileIndex tile)18 /* static */ bool ScriptTunnel::IsTunnelTile(TileIndex tile)
19 {
20 	if (!::IsValidTile(tile)) return false;
21 	return ::IsTunnelTile(tile);
22 }
23 
GetOtherTunnelEnd(TileIndex tile)24 /* static */ TileIndex ScriptTunnel::GetOtherTunnelEnd(TileIndex tile)
25 {
26 	if (!::IsValidTile(tile)) return INVALID_TILE;
27 
28 	/* If it's a tunnel already, take the easy way out! */
29 	if (IsTunnelTile(tile)) return ::GetOtherTunnelEnd(tile);
30 
31 	int start_z;
32 	Slope start_tileh = ::GetTileSlope(tile, &start_z);
33 	DiagDirection direction = ::GetInclinedSlopeDirection(start_tileh);
34 	if (direction == INVALID_DIAGDIR) return INVALID_TILE;
35 
36 	TileIndexDiff delta = ::TileOffsByDiagDir(direction);
37 	int end_z;
38 	do {
39 		tile += delta;
40 		if (!::IsValidTile(tile)) return INVALID_TILE;
41 
42 		::GetTileSlope(tile, &end_z);
43 	} while (start_z != end_z);
44 
45 	return tile;
46 }
47 
48 /**
49  * Helper function to connect a just built tunnel to nearby roads.
50  * @param instance The script instance we have to built the road for.
51  */
_DoCommandReturnBuildTunnel2(class ScriptInstance * instance)52 static void _DoCommandReturnBuildTunnel2(class ScriptInstance *instance)
53 {
54 	if (!ScriptTunnel::_BuildTunnelRoad2()) {
55 		ScriptInstance::DoCommandReturn(instance);
56 		return;
57 	}
58 
59 	/* This can never happen, as in test-mode this callback is never executed,
60 	 *  and in execute-mode, the other callback is called. */
61 	NOT_REACHED();
62 }
63 
64 /**
65  * Helper function to connect a just built tunnel to nearby roads.
66  * @param instance The script instance we have to built the road for.
67  */
_DoCommandReturnBuildTunnel1(class ScriptInstance * instance)68 static void _DoCommandReturnBuildTunnel1(class ScriptInstance *instance)
69 {
70 	if (!ScriptTunnel::_BuildTunnelRoad1()) {
71 		ScriptInstance::DoCommandReturn(instance);
72 		return;
73 	}
74 
75 	/* This can never happen, as in test-mode this callback is never executed,
76 	 *  and in execute-mode, the other callback is called. */
77 	NOT_REACHED();
78 }
79 
BuildTunnel(ScriptVehicle::VehicleType vehicle_type,TileIndex start)80 /* static */ bool ScriptTunnel::BuildTunnel(ScriptVehicle::VehicleType vehicle_type, TileIndex start)
81 {
82 	EnforcePrecondition(false, ::IsValidTile(start));
83 	EnforcePrecondition(false, vehicle_type == ScriptVehicle::VT_RAIL || vehicle_type == ScriptVehicle::VT_ROAD);
84 	EnforcePrecondition(false, vehicle_type != ScriptVehicle::VT_RAIL || ScriptRail::IsRailTypeAvailable(ScriptRail::GetCurrentRailType()));
85 	EnforcePrecondition(false, vehicle_type != ScriptVehicle::VT_ROAD || ScriptRoad::IsRoadTypeAvailable(ScriptRoad::GetCurrentRoadType()));
86 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY || vehicle_type == ScriptVehicle::VT_ROAD);
87 
88 	uint type = 0;
89 	if (vehicle_type == ScriptVehicle::VT_ROAD) {
90 		type |= (TRANSPORT_ROAD << 8);
91 		type |= ScriptRoad::GetCurrentRoadType();
92 	} else {
93 		type |= (TRANSPORT_RAIL << 8);
94 		type |= ScriptRail::GetCurrentRailType();
95 	}
96 
97 	/* For rail we do nothing special */
98 	if (vehicle_type == ScriptVehicle::VT_RAIL) {
99 		return ScriptObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL);
100 	}
101 
102 	ScriptObject::SetCallbackVariable(0, start);
103 	return ScriptObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, nullptr, &::_DoCommandReturnBuildTunnel1);
104 }
105 
_BuildTunnelRoad1()106 /* static */ bool ScriptTunnel::_BuildTunnelRoad1()
107 {
108 	/* Build the piece of road on the 'start' side of the tunnel */
109 	TileIndex end = ScriptObject::GetCallbackVariable(0);
110 	TileIndex start = ScriptTunnel::GetOtherTunnelEnd(end);
111 
112 	DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
113 	DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
114 
115 	return ScriptObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, nullptr, &::_DoCommandReturnBuildTunnel2);
116 }
117 
_BuildTunnelRoad2()118 /* static */ bool ScriptTunnel::_BuildTunnelRoad2()
119 {
120 	/* Build the piece of road on the 'end' side of the tunnel */
121 	TileIndex end = ScriptObject::GetCallbackVariable(0);
122 	TileIndex start = ScriptTunnel::GetOtherTunnelEnd(end);
123 
124 	DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
125 	DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
126 
127 	return ScriptObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD);
128 }
129 
RemoveTunnel(TileIndex tile)130 /* static */ bool ScriptTunnel::RemoveTunnel(TileIndex tile)
131 {
132 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
133 	EnforcePrecondition(false, IsTunnelTile(tile));
134 
135 	return ScriptObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
136 }
137