Terraria v1.4.4.9
Terraria source code documentation
All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Events Macros
TrackGenerator.cs
Go to the documentation of this file.
1using System;
4using Terraria.ID;
6
8
9public class TrackGenerator
10{
12 {
16 }
17
18 private enum TrackSlope : sbyte
19 {
20 Up = -1,
22 Down
23 }
24
25 private enum TrackMode : byte
26 {
27 Normal,
28 Tunnel
29 }
30
31 [DebuggerDisplay("X = {X}, Y = {Y}, Slope = {Slope}")]
32 private struct TrackHistory
33 {
34 public short X;
35
36 public short Y;
37
39
41
42 public TrackHistory(int x, int y, TrackSlope slope)
43 {
44 X = (short)x;
45 Y = (short)y;
46 Slope = slope;
47 Mode = TrackMode.Normal;
48 }
49 }
50
51 private static readonly ushort[] InvalidWalls = new ushort[20]
52 {
53 7, 94, 95, 8, 98, 99, 9, 96, 97, 3,
54 83, 68, 62, 78, 87, 86, 42, 74, 27, 149
55 };
56
57 private static readonly ushort[] InvalidTiles = new ushort[24]
58 {
59 383, 384, 15, 304, 30, 321, 245, 246, 240, 241,
60 242, 16, 34, 158, 377, 94, 10, 19, 86, 219,
61 484, 190, 664, 665
62 };
63
64 private readonly TrackHistory[] _history = new TrackHistory[4096];
65
66 private readonly TrackHistory[] _rewriteHistory = new TrackHistory[25];
67
68 private int _xDirection;
69
70 private int _length;
71
72 private int playerHeight = 6;
73
74 public bool Place(Point origin, int minLength, int maxLength)
75 {
76 if (!FindSuitableOrigin(ref origin))
77 {
78 return false;
79 }
80 CreateTrackStart(origin);
81 if (!FindPath(minLength, maxLength))
82 {
83 return false;
84 }
85 PlacePath();
86 return true;
87 }
88
89 private void PlacePath()
90 {
91 bool[] array = new bool[_length];
92 for (int i = 0; i < _length; i++)
93 {
94 if (WorldGen.genRand.Next(7) == 0)
95 {
96 playerHeight = WorldGen.genRand.Next(5, 9);
97 }
98 for (int j = 0; j < playerHeight; j++)
99 {
100 if (Main.tile[_history[i].X, _history[i].Y - j - 1].wall == 244)
101 {
102 Main.tile[_history[i].X, _history[i].Y - j - 1].wall = 0;
103 }
104 if (Main.tile[_history[i].X, _history[i].Y - j].wall == 244)
105 {
106 Main.tile[_history[i].X, _history[i].Y - j].wall = 0;
107 }
108 if (Main.tile[_history[i].X, _history[i].Y - j + 1].wall == 244)
109 {
110 Main.tile[_history[i].X, _history[i].Y - j + 1].wall = 0;
111 }
112 if (Main.tile[_history[i].X, _history[i].Y - j].type == 135)
113 {
114 array[i] = true;
115 }
116 WorldGen.KillTile(_history[i].X, _history[i].Y - j, fail: false, effectOnly: false, noItem: true);
117 }
118 }
119 for (int k = 0; k < _length; k++)
120 {
121 if (WorldGen.genRand.Next(7) == 0)
122 {
123 playerHeight = WorldGen.genRand.Next(5, 9);
124 }
125 TrackHistory trackHistory = _history[k];
126 Tile.SmoothSlope(trackHistory.X, trackHistory.Y + 1);
127 Tile.SmoothSlope(trackHistory.X, trackHistory.Y - playerHeight);
128 bool wire = Main.tile[trackHistory.X, trackHistory.Y].wire();
129 if (array[k] && k < _length && k > 0 && _history[k - 1].Y == trackHistory.Y && _history[k + 1].Y == trackHistory.Y)
130 {
131 Main.tile[trackHistory.X, trackHistory.Y].ClearEverything();
132 WorldGen.PlaceTile(trackHistory.X, trackHistory.Y, 314, mute: false, forced: true, -1, 1);
133 }
134 else
135 {
136 Main.tile[trackHistory.X, trackHistory.Y].ResetToType(314);
137 }
138 Main.tile[trackHistory.X, trackHistory.Y].wire(wire);
139 if (k == 0)
140 {
141 continue;
142 }
143 for (int l = 0; l < 8; l++)
144 {
145 WorldUtils.TileFrame(_history[k - 1].X, _history[k - 1].Y - l, frameNeighbors: true);
146 }
147 if (k == _length - 1)
148 {
149 for (int m = 0; m < playerHeight; m++)
150 {
151 WorldUtils.TileFrame(trackHistory.X, trackHistory.Y - m, frameNeighbors: true);
152 }
153 }
154 }
155 }
156
157 private void CreateTrackStart(Point origin)
158 {
159 _xDirection = ((origin.X <= Main.maxTilesX / 2) ? 1 : (-1));
160 _length = 1;
161 for (int i = 0; i < _history.Length; i++)
162 {
163 _history[i] = new TrackHistory(origin.X + i * _xDirection, origin.Y + i, TrackSlope.Down);
164 }
165 }
166
167 private bool FindPath(int minLength, int maxLength)
168 {
169 int length = _length;
170 while (_length < _history.Length - 100)
171 {
172 TrackSlope slope = ((_history[_length - 1].Slope != TrackSlope.Up) ? TrackSlope.Down : TrackSlope.Straight);
173 AppendToHistory(slope);
175 if (trackPlacementState == TrackPlacementState.Invalid)
176 {
177 break;
178 }
179 length = _length;
180 TrackPlacementState trackPlacementState2 = trackPlacementState;
181 while (trackPlacementState2 != 0)
182 {
183 trackPlacementState2 = CreateTunnel();
184 if (trackPlacementState2 == TrackPlacementState.Invalid)
185 {
186 break;
187 }
188 length = _length;
189 }
190 if (_length >= maxLength)
191 {
192 break;
193 }
194 }
195 _length = Math.Min(maxLength, length);
196 if (_length < minLength)
197 {
198 return false;
199 }
200 SmoothTrack();
202 }
203
205 {
206 TrackSlope trackSlope = TrackSlope.Straight;
207 int num = 10;
208 TrackPlacementState trackPlacementState = TrackPlacementState.Invalid;
209 int x = _history[_length - 1].X;
210 int y = _history[_length - 1].Y;
211 for (TrackSlope trackSlope2 = TrackSlope.Up; trackSlope2 <= TrackSlope.Down; trackSlope2++)
212 {
213 TrackPlacementState trackPlacementState2 = TrackPlacementState.Invalid;
214 for (int i = 1; i < num; i++)
215 {
216 trackPlacementState2 = CalculateStateForLocation(x + i * _xDirection, y + i * (int)trackSlope2);
217 switch (trackPlacementState2)
218 {
219 default:
220 trackSlope = trackSlope2;
221 num = i;
222 trackPlacementState = trackPlacementState2;
223 break;
224 case TrackPlacementState.Obstructed:
225 continue;
226 case TrackPlacementState.Invalid:
227 break;
228 }
229 break;
230 }
231 if (trackPlacementState != 0 && trackPlacementState2 == TrackPlacementState.Obstructed && (trackPlacementState != TrackPlacementState.Obstructed || trackSlope != 0))
232 {
233 trackSlope = trackSlope2;
234 num = 10;
235 trackPlacementState = trackPlacementState2;
236 }
237 }
238 if (_length == 0 || !CanSlopesTouch(_history[_length - 1].Slope, trackSlope))
239 {
241 }
242 _history[_length - 1].Mode = TrackMode.Tunnel;
243 for (int j = 1; j < num; j++)
244 {
245 AppendToHistory(trackSlope, TrackMode.Tunnel);
246 }
247 return trackPlacementState;
248 }
249
250 private void AppendToHistory(TrackSlope slope, TrackMode mode = TrackMode.Normal)
251 {
252 _history[_length] = new TrackHistory(_history[_length - 1].X + _xDirection, (int)_history[_length - 1].Y + (int)slope, slope);
253 _history[_length].Mode = mode;
254 _length++;
255 }
256
258 {
259 int num = _length - 1;
260 int num2 = Math.Min(_length, _rewriteHistory.Length);
261 for (int i = 0; i < num2; i++)
262 {
263 _rewriteHistory[i] = _history[num - i];
264 }
265 while (num >= _length - num2)
266 {
267 if (_history[num].Slope == TrackSlope.Down)
268 {
269 TrackPlacementState historySegmentPlacementState = GetHistorySegmentPlacementState(num, _length - num);
270 if (historySegmentPlacementState == TrackPlacementState.Available)
271 {
272 return historySegmentPlacementState;
273 }
274 RewriteSlopeDirection(num, TrackSlope.Straight);
275 }
276 num--;
277 }
278 if (GetHistorySegmentPlacementState(num + 1, _length - (num + 1)) == TrackPlacementState.Available)
279 {
280 return TrackPlacementState.Available;
281 }
282 for (num = _length - 1; num >= _length - num2 + 1; num--)
283 {
284 if (_history[num].Slope == TrackSlope.Straight)
285 {
286 TrackPlacementState historySegmentPlacementState2 = GetHistorySegmentPlacementState(_length - num2, num2);
287 if (historySegmentPlacementState2 == TrackPlacementState.Available)
288 {
289 return historySegmentPlacementState2;
290 }
292 }
293 }
294 for (int j = 0; j < num2; j++)
295 {
296 _history[_length - 1 - j] = _rewriteHistory[j];
297 }
299 return GetHistorySegmentPlacementState(num + 1, _length - (num + 1));
300 }
301
302 private void RewriteSlopeDirection(int index, TrackSlope slope)
303 {
304 int num = slope - _history[index].Slope;
305 _history[index].Slope = slope;
306 for (int i = index; i < _length; i++)
307 {
308 _history[i].Y += (short)num;
309 }
310 }
311
313 {
314 TrackPlacementState result = TrackPlacementState.Available;
315 for (int i = startIndex; i < startIndex + length; i++)
316 {
318 switch (trackPlacementState)
319 {
320 case TrackPlacementState.Invalid:
321 return trackPlacementState;
322 case TrackPlacementState.Obstructed:
323 if (_history[i].Mode != TrackMode.Tunnel)
324 {
325 result = trackPlacementState;
326 }
327 break;
328 }
329 }
330 return result;
331 }
332
333 private void SmoothTrack()
334 {
335 int num = _length - 1;
336 bool flag = false;
337 for (int num2 = _length - 1; num2 >= 0; num2--)
338 {
339 if (flag)
340 {
341 num = Math.Min(num2 + 15, num);
342 if (_history[num2].Y >= _history[num].Y)
343 {
344 for (int i = num2 + 1; _history[i].Y > _history[num2].Y; i++)
345 {
346 _history[i].Y = _history[num2].Y;
347 _history[i].Slope = TrackSlope.Straight;
348 }
349 if (_history[num2].Y == _history[num].Y)
350 {
351 flag = false;
352 }
353 }
354 }
355 else if (_history[num2].Y > _history[num].Y)
356 {
357 flag = true;
358 }
359 else
360 {
361 num = num2;
362 }
363 }
364 }
365
366 private static bool CanSlopesTouch(TrackSlope leftSlope, TrackSlope rightSlope)
367 {
368 if (leftSlope != rightSlope && leftSlope != 0)
369 {
370 return rightSlope == TrackSlope.Straight;
371 }
372 return true;
373 }
374
375 private static bool FindSuitableOrigin(ref Point origin)
376 {
377 TrackPlacementState trackPlacementState;
378 while ((trackPlacementState = CalculateStateForLocation(origin.X, origin.Y)) != TrackPlacementState.Obstructed)
379 {
380 origin.Y++;
381 if (trackPlacementState == TrackPlacementState.Invalid)
382 {
383 return false;
384 }
385 }
386 origin.Y--;
387 return CalculateStateForLocation(origin.X, origin.Y) == TrackPlacementState.Available;
388 }
389
391 {
392 for (int i = 0; i < 6; i++)
393 {
394 if (IsLocationInvalid(x, y - i))
395 {
396 return TrackPlacementState.Invalid;
397 }
398 }
399 for (int j = 0; j < 6; j++)
400 {
401 if (IsMinecartTrack(x, y + j))
402 {
403 return TrackPlacementState.Invalid;
404 }
405 }
406 for (int k = 0; k < 6; k++)
407 {
408 if (WorldGen.SolidTile(x, y - k))
409 {
410 return TrackPlacementState.Obstructed;
411 }
412 }
413 if (WorldGen.IsTileNearby(x, y, 314, 30))
414 {
415 return TrackPlacementState.Invalid;
416 }
417 return TrackPlacementState.Available;
418 }
419
420 private static bool IsMinecartTrack(int x, int y)
421 {
422 if (Main.tile[x, y].active())
423 {
424 return Main.tile[x, y].type == 314;
425 }
426 return false;
427 }
428
429 private static bool IsLocationInvalid(int x, int y)
430 {
431 if (y > Main.UnderworldLayer || x < 5 || y < (int)Main.worldSurface || x > Main.maxTilesX - 5)
432 {
433 return true;
434 }
435 if (Math.Abs((double)x - GenVars.shimmerPosition.X) < (double)(WorldGen.shimmerSafetyDistance / 2) && Math.Abs((double)y - GenVars.shimmerPosition.Y) < (double)(WorldGen.shimmerSafetyDistance / 2))
436 {
437 return true;
438 }
439 if (WorldGen.oceanDepths(x, y))
440 {
441 return true;
442 }
443 ushort wall = Main.tile[x, y].wall;
444 for (int i = 0; i < InvalidWalls.Length; i++)
445 {
446 if (wall == InvalidWalls[i] && (!WorldGen.notTheBees || wall != 108))
447 {
448 return true;
449 }
450 }
451 ushort type = Main.tile[x, y].type;
452 for (int j = 0; j < InvalidTiles.Length; j++)
453 {
454 if (type == InvalidTiles[j])
455 {
456 return true;
457 }
458 }
459 for (int k = -1; k <= 1; k++)
460 {
461 if (Main.tile[x + k, y].active() && (Main.tile[x + k, y].type == 314 || !TileID.Sets.GeneralPlacementTiles[Main.tile[x + k, y].type]) && (!WorldGen.notTheBees || Main.tile[x + k, y].type != 225))
462 {
463 return true;
464 }
465 }
466 return false;
467 }
468
469 [Conditional("DEBUG")]
470 private void DrawPause()
471 {
472 }
473}
static byte Min(byte val1, byte val2)
Definition Math.cs:912
static double Abs(double value)
bool Place(Point origin, int minLength, int maxLength)
static TrackPlacementState CalculateStateForLocation(int x, int y)
void RewriteSlopeDirection(int index, TrackSlope slope)
void AppendToHistory(TrackSlope slope, TrackMode mode=TrackMode.Normal)
static bool CanSlopesTouch(TrackSlope leftSlope, TrackSlope rightSlope)
bool FindPath(int minLength, int maxLength)
static bool FindSuitableOrigin(ref Point origin)
TrackPlacementState GetHistorySegmentPlacementState(int startIndex, int length)
static bool[] GeneralPlacementTiles
Definition TileID.cs:221
static double worldSurface
Definition Main.cs:1272
static int maxTilesX
Definition Main.cs:1114
static Tile[,] tile
Definition Main.cs:1675
static int UnderworldLayer
Definition Main.cs:2825
static void SmoothSlope(int x, int y, bool applyToNeighbors=true, bool sync=false)
Definition Tile.cs:759
static Vector2D shimmerPosition
Definition GenVars.cs:310
static void TileFrame(int x, int y, bool frameNeighbors=false)
Definition WorldUtils.cs:61
static bool SolidTile(Tile testTile)
static void KillTile(int i, int j, bool fail=false, bool effectOnly=false, bool noItem=false)
static readonly int shimmerSafetyDistance
Definition WorldGen.cs:930
static bool PlaceTile(int i, int j, int Type, bool mute=false, bool forced=false, int plr=-1, int style=0)
static UnifiedRandom genRand
Definition WorldGen.cs:1215
static bool oceanDepths(int x, int y)
Definition WorldGen.cs:7142
static bool notTheBees
Definition WorldGen.cs:1164
static bool IsTileNearby(int x, int y, int type, int distance)
Definition WorldGen.cs:5117