getArenaMinChunkZ()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
package de.pewpewproject.lasertag.worldgen.chunkgen.template;
2
3
import de.pewpewproject.lasertag.LasertagMod;
4
import de.pewpewproject.lasertag.common.types.Vec3;
5
import de.pewpewproject.lasertag.common.util.NbtUtil;
6
import de.pewpewproject.lasertag.resource.ResourceManagers;
7
import de.pewpewproject.lasertag.resource.StructureResourceManager;
8
import de.pewpewproject.lasertag.worldgen.chunkgen.type.ArenaType;
9
import de.pewpewproject.lasertag.worldgen.chunkgen.type.ProceduralArenaType;
10
import net.minecraft.nbt.NbtCompound;
11
import net.minecraft.nbt.NbtIo;
12
import net.minecraft.resource.Resource;
13
import net.minecraft.structure.StructureTemplate;
14
import net.minecraft.util.Identifier;
15
import net.minecraft.util.math.BlockPos;
16
import net.minecraft.util.math.ChunkSectionPos;
17
import net.minecraft.util.math.Vec3i;
18
19
import java.io.IOException;
20
import java.util.ArrayList;
21
import java.util.Arrays;
22
import java.util.List;
23
import java.util.Random;
24
25
/**
26
 * @author Étienne Muser
27
 */
28
public class ProceduralArenaTemplate extends ArenaTemplate {
29
30
    private static final int BLOCKS_PER_CHUNK = 16;
31
32
    // Start pos of the arena (lower left corner in block coordinates, shell not included)
33
    private BlockPos arenaStartPos = null;
34
35
    // The bounds of the arena in chunks (shell included)
36
    private Integer arenaMinChunkX = null;
37
    private Integer arenaMinChunkZ = null;
38
    private Integer arenaMaxChunkX = null;
39
    private Integer arenaMaxChunkZ = null;
40
41
    private Integer lobbyMinChunkX = null;
42
    private Integer lobbyMinChunkZ = null;
43
    private Integer lobbyMaxChunkX = null;
44
    private Integer lobbyMaxChunkZ = null;
45
46
    private Integer completeChunkXSize = null;
47
    private Integer completeChunkZSize = null;
48
49
    protected Vec3i completeOffset;
50
51
    private List<StructureTemplate> randomTemplates;
52
53
    private StructureTemplate lobbyTemplate = null;
54
55
    private StructureTemplate shellTemplate = null;
56
57
    private final ProceduralArenaType proceduralArenaType;
58
59
    public ProceduralArenaTemplate(ProceduralArenaType arenaType, long seed) {
60
        super(true);
61
62
        var lobbyStartPos = BlockPos.ORIGIN.subtract(arenaType.lobbyOffset);
63
        initLobbyTemplate(arenaType);
64
        initLobbySize(lobbyStartPos);
65
        initArenaSize(arenaType);
66
        initShellTemplate(arenaType);
67
        initRandomTemplates(arenaType, seed);
68
69
        this.proceduralArenaType = arenaType;
70
    }
71
72
    public StructureTemplate getShellTemplate() {
73
        return this.shellTemplate;
74
    }
75
76
    public StructureTemplate getLobbyTemplate() {
77
        return this.lobbyTemplate;
78
    }
79
80
    public StructureTemplate getRandomTemplate(int segmentId) {
81
        return this.randomTemplates.get(segmentId);
82
    }
83
84
    @Override
85
    public Vec3i getArenaSize() {
86
        return new Vec3i(completeChunkXSize * BLOCKS_PER_CHUNK,
87
                Math.max(lobbyTemplate.getSize().getY(), shellTemplate.getSize().getY()),
88
                completeChunkZSize * BLOCKS_PER_CHUNK);
89
    }
90
91
    @Override
92
    public Vec3i getPlacementOffset() {
93
        return this.completeOffset;
94
    }
95
96
    public ProceduralArenaType getProceduralArenaType() {
97
        return this.proceduralArenaType;
98
    }
99
100
    public boolean isLobbyChunk(int x, int z) {
101
        return isInside(x, z,
102
                lobbyMinChunkX,
103
                lobbyMinChunkZ,
104
                lobbyMaxChunkX,
105
                lobbyMaxChunkZ);
106
    }
107
108
    public boolean isArenaChunk(int x, int z) {
109
        return isInside(x, z,
110
                arenaMinChunkX,
111
                arenaMinChunkZ,
112
                arenaMaxChunkX,
113
                arenaMaxChunkZ);
114
    }
115
116
    public boolean isArenaChunkWithoutShell(int x, int z) {
117
        // +/- 1 offset because of shell
118
        return isInside(x, z,
119
                arenaMinChunkX + 1,
120
                arenaMinChunkZ + 1,
121
                arenaMaxChunkX - 1,
122
                arenaMaxChunkZ - 1);
123
    }
124
125
    public int getArenaMinChunkX() {
126
        return this.arenaMinChunkX;
127
    }
128
129
    public int getArenaMinChunkZ() {
130
        return this.arenaMinChunkZ;
131
    }
132
133
    private void initLobbySize(BlockPos lobbyStartPos) {
134
135
        var lobbySize = this.lobbyTemplate.getSize();
136
        var lobbyEndX = lobbyStartPos.getX() + lobbySize.getX();
137
        var lobbyEndZ = lobbyStartPos.getZ() + lobbySize.getZ();
138
        lobbyMinChunkX = ChunkSectionPos.getSectionCoord(lobbyStartPos.getX());
139
        lobbyMinChunkZ = ChunkSectionPos.getSectionCoord(lobbyStartPos.getZ());
140
        lobbyMaxChunkX = ChunkSectionPos.getSectionCoord(lobbyEndX);
141
        lobbyMaxChunkZ = ChunkSectionPos.getSectionCoord(lobbyEndZ);
142
    }
143
144
    private void initArenaSize(ProceduralArenaType proceduralArenaType) {
145
146
        var segments = proceduralArenaType.segments;
147
148
        int startChunkX;
149
        int startChunkZ;
150
        int arenaChunkSizeX = proceduralArenaType.segments.length;
151
        int arenaChunkSizeZ = proceduralArenaType.segments[0].length;
152
        if (lobbyMaxChunkX > lobbyMaxChunkZ) {
153
            startChunkZ = lobbyMaxChunkZ + 1;
154
            // Integer division intended
155
            startChunkX = -(segments.length / 2) - 1;
156
157
            completeChunkXSize = Math.max(arenaChunkSizeX + 2, lobbyMaxChunkX - lobbyMinChunkX);
158
            completeChunkZSize = (lobbyMaxChunkZ - lobbyMinChunkZ) + arenaChunkSizeZ + 2;
159
        } else {
160
            startChunkX = lobbyMaxChunkX + 1;
161
            // Integer division intended
162
            startChunkZ = -(segments[0].length / 2) - 1;
163
164
            completeChunkXSize = (lobbyMaxChunkX - lobbyMinChunkX) + arenaChunkSizeX + 2;
165
            completeChunkZSize = Math.max(arenaChunkSizeZ + 2, lobbyMaxChunkZ - lobbyMinChunkZ);
166
        }
167
168
        this.arenaStartPos = new BlockPos((startChunkX + 1) * BLOCKS_PER_CHUNK, 0, (startChunkZ + 1) * BLOCKS_PER_CHUNK);
169
        this.arenaMinChunkX = startChunkX;
170
        this.arenaMinChunkZ = startChunkZ;
171
        // Plus one for the shell
172
        this.arenaMaxChunkX = this.arenaMinChunkX + arenaChunkSizeX + 1;
173
        this.arenaMaxChunkZ = this.arenaMinChunkZ + arenaChunkSizeZ + 1;
174
175
        var completeMinChunkX = Math.min(lobbyMinChunkX, arenaMinChunkX);
176
        var completeMinChunkZ = Math.min(lobbyMinChunkZ, arenaMinChunkZ);
177
        this.completeOffset = new Vec3i(-completeMinChunkX * BLOCKS_PER_CHUNK, 0, -completeMinChunkZ * BLOCKS_PER_CHUNK);
178
    }
179
180
    private void initRandomTemplates(ProceduralArenaType proceduralArenaType, long seed) {
181
182
        var random = new Random(seed);
183
184
        var resourceManager = ResourceManagers.STRUCTURE_RESOURCE_MANAGER;
185
        var templateResources = new ArrayList<List<Vec3<Integer, Identifier, Resource>>>();
186
187
        Arrays.stream(proceduralArenaType.segments).flatMapToInt(Arrays::stream).distinct().sorted().forEach(segmentId -> {
188
189
            var resources = resourceManager.getFolder(new Identifier(LasertagMod.ID, ArenaType.PROCEDURAL.nbtFileId.getPath() + "/" + proceduralArenaType.mapFolderName + "/" + proceduralArenaType.folderNameMapping[segmentId]));
190
191
            var mappedResources = resources.stream()
192
                    .map(r -> new Vec3<>(segmentId, r.getKey(), r.getValue()))
193
                    .toList();
194
195
            templateResources.add(mappedResources);
196
        });
197
198
199
        randomTemplates = templateResources.stream()
200
                .map(indexTemplates -> indexTemplates.get(random.nextInt(indexTemplates.size())))
201
                .map(r -> {
202
                    var localOffset = this.getLocalChunkOffset(proceduralArenaType, r.x());
203
                    return initTemplate(r.y(), r.z(), BlockPos.ORIGIN.subtract(arenaStartPos.add(localOffset)));
204
                })
205
                .toList();
206
    }
207
208 View Code Duplication
    private void initLobbyTemplate(ProceduralArenaType proceduralArenaType) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
209
210
        var lobbyId = new Identifier(LasertagMod.ID, ArenaType.PROCEDURAL.nbtFileId.getPath() + "/" + proceduralArenaType.mapFolderName + "/lobby.litematic");
211
        var lobbyResource = ResourceManagers.STRUCTURE_RESOURCE_MANAGER.get(lobbyId);
212
213
        if (lobbyResource == null) {
214
            throw new RuntimeException("Could not find lobby resource '" + lobbyId.getPath() + "' for arena '" + proceduralArenaType.translatableName + "'");
215
        }
216
217
        this.lobbyTemplate = initTemplate(lobbyId, lobbyResource, proceduralArenaType.lobbyOffset);
218
    }
219
220 View Code Duplication
    private void initShellTemplate(ProceduralArenaType proceduralArenaType) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
221
222
        var shellId = new Identifier(LasertagMod.ID, ArenaType.PROCEDURAL.nbtFileId.getPath() + "/" + proceduralArenaType.mapFolderName + "/shell.litematic");
223
        var shellResource = ResourceManagers.STRUCTURE_RESOURCE_MANAGER.get(shellId);
224
225
        if (shellResource == null) {
226
            throw new RuntimeException("Could not find shell resource '" + shellId.getPath() + "' for arena '" + proceduralArenaType.translatableName + "'");
227
        }
228
229
        this.shellTemplate = initTemplate(shellId, shellResource, BlockPos.ORIGIN.subtract(arenaStartPos.add(-1, -1, -1)));
230
    }
231
232
    private StructureTemplate initTemplate(Identifier resourceId, Resource resource, Vec3i offset) {
233
234
        // Read nbt file
235
        NbtCompound nbt;
236
        try {
237
            nbt = NbtIo.readCompressed(resource.getInputStream());
238
        } catch (IOException e) {
239
            throw new RuntimeException("Unable to load nbt file for template '" + resourceId.getPath() + "'");
240
        }
241
242
        // If is litematic file
243
        if (resourceId.getPath().endsWith(StructureResourceManager.LITEMATIC_FILE_ENDING)) {
244
245
            // Convert litematic nbt compound to nbt nbt compound
246
            nbt = NbtUtil.convertLitematicToNbt(nbt, "main", offset);
247
248
            // Sanity check
249
            if (nbt == null) {
250
                throw new RuntimeException("Litematica file '" + resourceId + "' could not be converted to nbt.");
251
            }
252
        }
253
254
        var segmentTemplate = new StructureTemplate();
255
        segmentTemplate.readNbt(nbt);
256
        return segmentTemplate;
257
    }
258
259
    /**
260
     * Calculates the offset of the first chunk of the given segmentId inside the arena.
261
     *
262
     * @param proceduralArenaType
263
     * @param segmentId
264
     * @return The offset in blocks
265
     */
266
    private BlockPos getLocalChunkOffset(ProceduralArenaType proceduralArenaType, int segmentId) {
267
        int localChunkStartX = 0;
268
        int localChunkStartZ;
269
        for (var row : proceduralArenaType.segments) {
270
            localChunkStartZ = 0;
271
272
            for (var chunk : row) {
273
                if (chunk == segmentId) {
274
                    return new BlockPos(localChunkStartX * BLOCKS_PER_CHUNK, 0, localChunkStartZ * BLOCKS_PER_CHUNK);
275
                }
276
277
                ++localChunkStartZ;
278
            }
279
280
            ++localChunkStartX;
281
        }
282
283
        return null;
284
    }
285
286
    private boolean isInside(int x, int z, int minX, int minZ, int maxX, int maxZ) {
287
        var xInside = x >= minX && x <= maxX;
288
        var zInside = z >= minZ && z <= maxZ;
289
290
        return xInside && zInside;
291
    }
292
}
293