-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCustRuleTileBase
More file actions
207 lines (188 loc) · 12.2 KB
/
CustRuleTileBase
File metadata and controls
207 lines (188 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using Unity.VisualScripting;
using System.Runtime.CompilerServices;
/*
These are some basic classes for custom ruletiles that specifically get and use specific tile coordinates to evaluate.
I have removed some parts unique to my project to make more clear the basics of how it works. This is more of a demo, as opposed to code you'd copy paste into a project.
It will likely break if you try to copy paste.
CustRuleTileStatic has references to things that are important for evaluating the ruletile that only exist during runtime. Example: References to Tilemaps
CustRuleTileBase<TNeighbor> contains a VERY important override to the CheckRuleTile method that gives us the coordinate being checked. This class ALSO gets ALL rule logic. Every single rule that you can include in a custom ruletile goes here
do NOT define actual logic for a type of rule outside of this class, and you'll see why next
I just committed with the important 3rd class: CustRuleTile : CustRuleTileBase<CustRuleTile.Neighbor>
CustRuleTile has it's own neighbor class which just contains constants for different neighbor rules. CustRuleTile : CustRuleTileBase<CustRuleTile.Neighbor> so that the rule tile methods know to use this neighbor rule
AND it has public override bool RuleMatchCoordinate(int neighbor, Vector3Int currentTileCoord, Vector3Int targetTileCoord) {....
which is the function that funnels different rules to different logic via switch case
*/
#region Prepare search for ruletiles with Static shared class\
namespace LoupRuleTiles {
[CreateAssetMenu(fileName = "NewRuleTile", menuName = "Level Builder/Custom Rule Tile/Basic")]
public class CustRuleTile : CustRuleTileBase<CustRuleTile.Neighbor> {
public class Neighbor : RuleTile.TilingRule.Neighbor {
//public const int This = 1;
//public const int NotThis = 2;
public const int IsGround = 3;
public const int NotGround = 4;
}
/// <summary> Input coordinates to check and neighbor rule ID, and output whether or not that coordinate works. </summary>
public override bool RuleMatchCoordinate(int neighbor, Vector3Int currentTileCoord, Vector3Int targetTileCoord) {
// If the maps are null because quitting, just return true.
//TileDirectory tileDirectory = TileDirectoryCentral.GetInstance().tileDirectory;
//List<BuildingObjectBase> objBasesFound = MapSearch.PierceGetTile(targetTileCoord);
BuildingObjectBase thisObjBase = CustRuleTileStatic.tileDirectory.directory[this];
TileBase neighborTile;
switch (neighbor) {
case Neighbor.This:
neighborTile = thisObjBase.tileLayer.tilemap.GetTile(targetTileCoord);
return (neighborTile != null) && CustRuleTileStatic.tileDirectory.directory[neighborTile] == thisObjBase;
case Neighbor.NotThis:
neighborTile = thisObjBase.tileLayer.tilemap.GetTile(targetTileCoord);
return (neighborTile == null) || CustRuleTileStatic.tileDirectory.directory[neighborTile] != thisObjBase;
case Neighbor.IsGround: return CheckGround(currentTileCoord, targetTileCoord, MapSearch.PierceGetTile(targetTileCoord, CustRuleTileStatic.ruleMapsGround));
case Neighbor.NotGround: return !CheckGround(currentTileCoord, targetTileCoord, MapSearch.PierceGetTile(targetTileCoord, CustRuleTileStatic.ruleMapsGround));
default: return false;
}
}
}
/// <summary> This class just holds static info for all custom ruletile logic. </summary>
public class CustRuleTileStatic {
public static bool blockRefresh = false;
public static TileDirectory tileDirectory { get; private set; }
public static LevelSettings levelSettings { get; private set; }
/// <summary> Set up static variables for ruletiles to use to search quickly. So we restrict search to only the
/// maps that need to be checked. </summary>
public static void InitializeStaticSearchVariables() {
blockRefresh = false;
}
}
#endregion
/// <summary> This is the base class for custom rule tiles to derive. It has the machinery to allow
/// the game to search for nearby tiles across multiple tilemaps that satisfy requirements
/// specified in the BuildingObjectBase. </summary>
public abstract class CustRuleTileBase<TNeighbor> : RuleTile<TNeighbor> {
public Sprite GetDefaultSprite() { return m_DefaultSprite; }
#region Rule logic
/// <summary> Input coordinates to check and neighbor rule ID, and output whether or not that coordinate works. </summary>
public abstract bool RuleMatchCoordinate(int neighbor, Vector3Int currentTileCoord, Vector3Int targetTileCoord);
/// <summary> Return true iff there is a neighboring tile presenting a a cardinal pointed at the original given
/// that function (objbase => cardinal) (eg obj=>obj.ruleGround). This accounts for rotation. </summary>
protected bool CheckMatedRuleCardinals(Vector3Int currentTileCoord, Vector3Int neighborTileCoord,
List<BuildingObjectBase> neighborBases, Func<BuildingObjectBase, Cardinal> ruleCardinalProperty) {
// Part 1: Get cardinal representation, and if there is not one, then it's a fail.
Vector2Int mainToNeighborVec = (Vector2Int)(neighborTileCoord - currentTileCoord);
bool isCardinal = GridMath.cardinalVectors.Contains(mainToNeighborVec);
if (!isCardinal) return false;
Cardinal neighborToMainCard = GridMath.VectorToCardinal(-mainToNeighborVec);
foreach (BuildingObjectBase neighborBase in neighborBases) {
Rotation neighborRotation = CustRuleTileStatic.tileDirectory.GetRotation(neighborBase.tileLayer.tilemap, (Vector2Int)neighborTileCoord);
Cardinal neighborDirections = GridMath.RotateCardinal(
ruleCardinalProperty(neighborBase), neighborRotation);
if (neighborDirections.HasFlag(neighborToMainCard)) return true;
}
return false;
}
/// <summary> Return true iff there is a neighboring that counts as ground OR it is out of bounds. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool CheckGround(Vector3Int currentTileCoord, Vector3Int neighborTileCoord, List<BuildingObjectBase> neighborBases) {
if (CoordIsOutsideLevelBounds(neighborTileCoord)) return true;
return CheckMatedRuleCardinals(currentTileCoord, neighborTileCoord, neighborBases,
objBase => objBase.ruleGroundDir);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool CoordIsOutsideLevelBounds(Vector3Int tileCoord) {
BoundsInt bounds = CustRuleTileStatic.levelSettings.levelBounds;
return (tileCoord.x < bounds.xMin && tileCoord.x > bounds.xMax
&& tileCoord.y < bounds.yMin && tileCoord.y > bounds.yMax);
}
#endregion
//--------------------------
//-------------Don't Fuck with anything below here. I copy-pasted from unity's assembly, with a few key edits to grab the coordinate
//-------------------------
#region Overriding Unity's Rulematches function to feed me position
public override bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, ref Matrix4x4 transform) {
if (CustRuleTileStatic.blockRefresh)
return true;// Loup edit.
if (RuleMatchesCust(rule, position, tilemap, 0)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, 0f), Vector3.one);
return true;
}
if (rule.m_RuleTransform == TilingRuleOutput.Transform.Rotated) {
for (int i = m_RotationAngle; i < 360; i += m_RotationAngle) {
if (RuleMatchesCust(rule, position, tilemap, i)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, (float)(-i)), Vector3.one);
return true;
}
}
} else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorXY) {
if (RuleMatchesCust(rule, position, tilemap, mirrorX: true, mirrorY: true)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, -1f, 1f));
return true;
}
if (RuleMatchesCust(rule, position, tilemap, mirrorX: true, mirrorY: false)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
return true;
}
if (RuleMatchesCust(rule, position, tilemap, mirrorX: false, mirrorY: true)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
return true;
}
} else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorX) {
if (RuleMatchesCust(rule, position, tilemap, mirrorX: true, mirrorY: false)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
return true;
}
} else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorY) {
if (RuleMatchesCust(rule, position, tilemap, mirrorX: false, mirrorY: true)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
return true;
}
} else if (rule.m_RuleTransform == TilingRuleOutput.Transform.RotatedMirror) {
for (int j = 0; j < 360; j += m_RotationAngle) {
if (j != 0 && RuleMatchesCust(rule, position, tilemap, j)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, (float)(-j)), Vector3.one);
return true;
}
if (RuleMatchesCust(rule, position, tilemap, j, mirrorX: true)) {
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, (float)(-j)), new Vector3(-1f, 1f, 1f));
return true;
}
}
}
return false;
}
// Lower level RuleMatches. I am basically overriding to get more of the info that I actually need.
public bool RuleMatchesCust(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle, bool mirrorX = false) {
int num = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
for (int i = 0; i < num; i++) {
int neighbor = rule.m_Neighbors[i];
Vector3Int position2 = rule.m_NeighborPositions[i];
if (mirrorX) {
position2 = GetMirroredPosition(position2, mirrorX: true, mirrorY: false);
}
Vector3Int rotatedPosition = GetRotatedPosition(position2, angle);
//TileBase tile = tilemap.GetTile(GetOffsetPosition(position, rotatedPosition));
if (!RuleMatchCoordinate(neighbor, position, GetOffsetPosition(position, rotatedPosition))) {
//if (!RuleMatchCoordinate(neighbor, tile)) {
return false;
}
}
return true;
}
public bool RuleMatchesCust(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY) {
int num = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
for (int i = 0; i < num; i++) {
int neighbor = rule.m_Neighbors[i];
Vector3Int mirroredPosition = GetMirroredPosition(rule.m_NeighborPositions[i], mirrorX, mirrorY);
//TileBase tile = tilemap.GetTile(GetOffsetPosition(position, mirroredPosition));
if (!RuleMatchCoordinate(neighbor, position, GetOffsetPosition(position, mirroredPosition))) {//tile)) {
return false;
}
}
return true;
}
#endregion
}
}