add rotation support

This commit is contained in:
QuImUfu 2023-07-03 11:15:40 +02:00
parent 5094c033a3
commit 897c1e5975
7 changed files with 210 additions and 106 deletions

View File

@ -20,18 +20,6 @@ repositories {
mavenCentral() mavenCentral()
} }
loom {
splitEnvironmentSourceSets()
mods {
"modid" {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
}
dependencies { dependencies {
// To change the versions see the gradle.properties file // To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"

View File

@ -9,7 +9,7 @@ loader_version=0.14.21
fabric_version=0.84.0+1.20.1 fabric_version=0.84.0+1.20.1
#Mod properties #Mod properties
mod_version = 0.9.2 mod_version = 0.9.4
maven_group = quimufu.structure_item maven_group = quimufu.structure_item
archives_base_name = structure_item archives_base_name = structure_item

View File

@ -83,6 +83,7 @@ public class MyItem extends Item {
if (i == 4) { if (i == 4) {
texts.add(Text.translatable("item.structure_item.item.tooltip.blacklist.more", texts.add(Text.translatable("item.structure_item.item.tooltip.blacklist.more",
Text.literal(String.valueOf(bl.size() - i)))); Text.literal(String.valueOf(bl.size() - i))));
break;
} }
} }
} }
@ -96,6 +97,15 @@ public class MyItem extends Item {
} else { } else {
texts.add(Text.translatable("item.structure_item.item.tooltip.doNotPlaceEntities")); texts.add(Text.translatable("item.structure_item.item.tooltip.doNotPlaceEntities"));
} }
if (tag.contains("rotate", NbtElement.STRING_TYPE)) {
texts.add(Text.translatable("item.structure_item.item.tooltip.dynamic.rotation"));
texts.add(Text.translatable("item.structure_item.item.tooltip.dynamic.rotation.value",
Text.translatable("item.structure_item.item.tooltip.dynamic.dir." + tag.getString("rotate"))));
} else {
texts.add(Text.translatable("item.structure_item.item.tooltip.fixed.rotation"));
}
} }
} }
@ -165,15 +175,40 @@ public class MyItem extends Item {
} }
StructureTemplate x = xOpt.get(); StructureTemplate x = xOpt.get();
BlockRotation rotation = BlockRotation.NONE;
if (tag.contains("rotate", NbtElement.STRING_TYPE)) {
String defaultOrientation = tag.getString("rotate");
Direction defaultDir = Direction.byName(defaultOrientation);
Direction currentDir = c.getSide();
if (defaultDir == null) {
Text message =
Text.translatable("items.structure.spawner.invalid.rotate",
Text.literal(defaultOrientation));
sendPlayerChat(player, message);
} else {
rotation = getRotationBetween(defaultDir, currentDir);
}
}
BlockPos loc = c.getBlockPos().offset(c.getSide()); BlockPos loc = c.getBlockPos().offset(c.getSide());
if (tag.contains("offset", 10)) { if (tag.contains("offset", 10)) {
BlockPos offset = NbtHelper.toBlockPos(tag.getCompound("offset")); BlockPos offset = NbtHelper.toBlockPos(tag.getCompound("offset"));
loc = loc.add(offset); loc = loc.add(offset);
Vec3i size = x.getSize();
switch (rotation) {
case CLOCKWISE_90 -> loc = loc.add(new Vec3i(size.getX()-1, 0, 0));
case CLOCKWISE_180 -> loc = loc.add(new Vec3i(size.getX()-1, 0, size.getZ()-1));
case COUNTERCLOCKWISE_90 -> loc = loc.add(new Vec3i(0, 0, size.getZ()-1));
}
} else if (tag.contains("offsetV2", 10)) { } else if (tag.contains("offsetV2", 10)) {
Direction direction = c.getSide().getOpposite(); Direction direction = c.getSide().getOpposite();
try { try {
StructureOffsetSettings offset = StructureOffsetSettings.ofTag(tag.getCompound("offsetV2")); StructureOffsetSettings offset = StructureOffsetSettings.ofTag(tag.getCompound("offsetV2"));
offset.setRotation(rotation);
Vec3i size = x.getSize(); Vec3i size = x.getSize();
loc = loc.add(offset.getEffective(direction, size)); loc = loc.add(offset.getEffective(direction, size));
} catch (Exception e) { } catch (Exception e) {
@ -185,11 +220,12 @@ public class MyItem extends Item {
} else { } else {
Direction direction = c.getSide().getOpposite(); Direction direction = c.getSide().getOpposite();
StructureOffsetSettings offset = StructureOffsetSettings.dynamic(); StructureOffsetSettings offset = StructureOffsetSettings.dynamic();
offset.setRotation(rotation);
Vec3i size = x.getSize(); Vec3i size = x.getSize();
loc = loc.add(offset.getEffective(direction, size)); loc = loc.add(offset.getEffective(direction, size));
} }
MyPlacementSettings ps = (new MyPlacementSettings()); MyPlacementSettings ps = new MyPlacementSettings();
if (tag.contains("replaceEntities", 99)) { if (tag.contains("replaceEntities", 99)) {
ps.setReplaceEntities(tag.getBoolean("replaceEntities")); ps.setReplaceEntities(tag.getBoolean("replaceEntities"));
} }
@ -213,15 +249,17 @@ public class MyItem extends Item {
} }
ps.forbidOverwrite(blacklist); ps.forbidOverwrite(blacklist);
} }
ps.setWorld(c.getWorld()) ps.setWorld(c.getWorld())
.setSize(x.getSize()) .setSize(x.getSize())
.setMirror(BlockMirror.NONE) .setMirror(BlockMirror.NONE)
.setRotation(BlockRotation.NONE); .setRotation(rotation);
boolean success = false; boolean success = false;
try { try {
if (x.place((ServerWorld) c.getWorld(), loc, loc, ps, c.getWorld().getRandom(), 2)) if (x.place((ServerWorld) c.getWorld(), loc, BlockPos.ORIGIN, ps, c.getWorld().getRandom(), 2))
success = true; success = true;
} catch (NullPointerException ignored) { } catch (PlacementNotAllowedException ignored) {
} }
if (success) { if (success) {
c.getStack().decrement(1); c.getStack().decrement(1);
@ -235,6 +273,22 @@ public class MyItem extends Item {
return ActionResult.FAIL; return ActionResult.FAIL;
} }
private BlockRotation getRotationBetween(Direction from, Direction to) {
if (to == Direction.DOWN || to == Direction.UP) {
return BlockRotation.NONE;
}
if (from.getOpposite() == to) {
return BlockRotation.CLOCKWISE_180;
}
if (from.rotateYClockwise() == to) {
return BlockRotation.CLOCKWISE_90;
}
if (from.rotateYCounterclockwise() == to) {
return BlockRotation.COUNTERCLOCKWISE_90;
}
return BlockRotation.NONE;
}
private static void sendPlayer(ServerPlayerEntity player, Text message) { private static void sendPlayer(ServerPlayerEntity player, Text message) {
if (player == null) if (player == null)
return; return;

View File

@ -4,12 +4,18 @@ import com.google.common.collect.Lists;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.structure.StructurePlacementData; import net.minecraft.structure.StructurePlacementData;
import net.minecraft.structure.StructureTemplate; import net.minecraft.structure.StructureTemplate;
import net.minecraft.structure.processor.BlockIgnoreStructureProcessor;
import net.minecraft.structure.processor.StructureProcessor;
import net.minecraft.structure.processor.StructureProcessorType;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3i; import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldView;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -20,6 +26,11 @@ public class MyPlacementSettings extends StructurePlacementData {
private Vec3i size; private Vec3i size;
private boolean replaceEntities = true; private boolean replaceEntities = true;
public MyPlacementSettings() {
addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS);
addProcessor(new CheckingStructureProcess());
}
public void forbidOverwrite(List<Block> blocks) { public void forbidOverwrite(List<Block> blocks) {
if (blocks.size() == 0) { if (blocks.size() == 0) {
blacklist = null; blacklist = null;
@ -38,69 +49,6 @@ public class MyPlacementSettings extends StructurePlacementData {
return this; return this;
} }
@Override
public StructureTemplate.PalettedBlockInfoList getRandomBlockInfos(List<StructureTemplate.PalettedBlockInfoList> blocks, BlockPos pos) {
if (world == null || pos == null || size == null) {
return super.getRandomBlockInfos(blocks, pos);
}
List<StructureTemplate.PalettedBlockInfoList> eligibleStructures;
eligibleStructures = getEligibleStructures(blocks, pos);
if (eligibleStructures.size() == 0)
return null;
StructureTemplate.PalettedBlockInfoList randomBlockInfos = super.getRandomBlockInfos(eligibleStructures, pos);
List<StructureTemplate.StructureBlockInfo> locs = randomBlockInfos.getAll();
if (!locs.isEmpty()) {
List<Entity> entitiesWithinAABB = world.getNonSpectatingEntities(Entity.class, new Box(pos,pos.add(size)));
for (StructureTemplate.StructureBlockInfo blockInfo : locs) {
BlockPos posToClean = blockInfo.pos().add(pos);
for (Entity e : entitiesWithinAABB) {
if (!(e instanceof PlayerEntity) && e.getBoundingBox().intersects(new Box(posToClean))) {
e.remove(Entity.RemovalReason.DISCARDED);
}
}
}
}
return randomBlockInfos;
}
private List<StructureTemplate.PalettedBlockInfoList> getEligibleStructures(List<StructureTemplate.PalettedBlockInfoList> blocks, BlockPos pos) {
List<StructureTemplate.PalettedBlockInfoList> eligibleStructures = new ArrayList<>();
if (blacklist == null && shouldReplaceEntities()) {
eligibleStructures = blocks;
} else {
for (StructureTemplate.PalettedBlockInfoList struct : blocks) {
if (isValid(struct, pos)) {
eligibleStructures.add(struct);
}
}
}
return eligibleStructures;
}
private boolean isValid(StructureTemplate.PalettedBlockInfoList struct, BlockPos pos) {
List<? extends Entity> entitiesWithinAABB;
if (shouldReplaceEntities()) {
entitiesWithinAABB = world.getNonSpectatingEntities(PlayerEntity.class, new Box(pos, pos.add(size)));
} else {
entitiesWithinAABB = world.getNonSpectatingEntities(Entity.class, new Box(pos, pos.add(size)));
}
for (StructureTemplate.StructureBlockInfo bi : struct.getAll()) {
BlockPos posToCheck = bi.pos().add(pos);
if (World.isValid(posToCheck)) {
for (Entity e : entitiesWithinAABB) {
if (e.getBoundingBox().intersects(new Box(posToCheck))) {
return false;
}
}
Block blockToCheck = world.getBlockState(posToCheck).getBlock();
if(blacklist.contains(blockToCheck))
return false;
}
}
return true;
}
public boolean shouldReplaceEntities() { public boolean shouldReplaceEntities() {
return replaceEntities; return replaceEntities;
} }
@ -108,4 +56,38 @@ public class MyPlacementSettings extends StructurePlacementData {
public void setReplaceEntities(boolean replaceEntities) { public void setReplaceEntities(boolean replaceEntities) {
this.replaceEntities = replaceEntities; this.replaceEntities = replaceEntities;
} }
public class CheckingStructureProcess extends StructureProcessor {
List<? extends Entity> entitiesWithinAABB;
@Nullable
@Override
public StructureTemplate.StructureBlockInfo process(WorldView world, BlockPos pos, BlockPos pivot, StructureTemplate.StructureBlockInfo originalBlockInfo, StructureTemplate.StructureBlockInfo currentBlockInfo, StructurePlacementData data) {
if (entitiesWithinAABB == null) {
if (shouldReplaceEntities()) {
entitiesWithinAABB = ((ServerWorld) world).getNonSpectatingEntities(PlayerEntity.class, new Box(pos.subtract(size), pos.add(size)));
} else {
entitiesWithinAABB = ((ServerWorld) world).getNonSpectatingEntities(Entity.class, new Box(pos.subtract(size), pos.add(size)));
}
}
BlockPos posToCheck;
if (currentBlockInfo != null && World.isValid(posToCheck = currentBlockInfo.pos())) {
for (Entity e : entitiesWithinAABB) {
if (e.getBoundingBox().intersects(new Box(posToCheck))) {
throw new PlacementNotAllowedException();
}
}
Block blockToCheck = world.getBlockState(posToCheck).getBlock();
if (blacklist.contains(blockToCheck))
throw new PlacementNotAllowedException();
}
return currentBlockInfo;
}
@Override
protected StructureProcessorType<?> getType() {
return null;
}
}
} }

View File

@ -0,0 +1,4 @@
package quimufu.structure_item;
public class PlacementNotAllowedException extends RuntimeException{
}

View File

@ -1,6 +1,7 @@
package quimufu.structure_item; package quimufu.structure_item;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -23,6 +24,7 @@ public class StructureOffsetSettings {
return args[(int) (Math.round(args[0]) + 1L)]; return args[(int) (Math.round(args[0]) + 1L)];
} }
}; };
private BlockRotation rotation;
public static StructureOffsetSettings ofTag(NbtCompound offsetTag) { public static StructureOffsetSettings ofTag(NbtCompound offsetTag) {
StructureOffsetSettings settings = new StructureOffsetSettings(); StructureOffsetSettings settings = new StructureOffsetSettings();
@ -83,30 +85,96 @@ public class StructureOffsetSettings {
private Vec3i getDirectionalOffset(Direction direction, Vec3i size) { private Vec3i getDirectionalOffset(Direction direction, Vec3i size) {
BlockPos loc = new BlockPos(0, 0, 0); BlockPos loc = new BlockPos(0, 0, 0);
switch (direction) { switch (rotation) {
case WEST -> { case NONE:
loc = loc.offset(Direction.NORTH, size.getZ() / 2); switch (direction) {
loc = loc.offset(Direction.WEST, size.getX() - 1); case WEST:
} loc = loc.offset(Direction.NORTH, size.getZ() / 2);
case EAST -> //positive x return loc.offset(Direction.WEST, size.getX() - 1);
loc = loc.offset(Direction.NORTH, size.getZ() / 2); case EAST://positive x
case NORTH -> { return loc.offset(Direction.NORTH, size.getZ() / 2);
loc = loc.offset(Direction.NORTH, size.getZ() - 1); case NORTH:
loc = loc.offset(Direction.WEST, size.getX() / 2); loc = loc.offset(Direction.NORTH, size.getZ() - 1);
} return loc.offset(Direction.WEST, size.getX() / 2);
case SOUTH -> //positive z case SOUTH://positive z
loc = loc.offset(Direction.WEST, size.getX() / 2); return loc.offset(Direction.WEST, size.getX() / 2);
case UP -> { //positive y case UP://positive y
loc = loc.offset(Direction.NORTH, size.getZ() / 2); loc = loc.offset(Direction.NORTH, size.getZ() / 2);
loc = loc.offset(Direction.WEST, size.getX() / 2); loc = loc.offset(Direction.WEST, size.getX() / 2);
loc = loc.offset(Direction.UP); return loc.offset(Direction.UP);
} case DOWN:
case DOWN -> { loc = loc.offset(Direction.NORTH, size.getZ() / 2);
loc = loc.offset(Direction.NORTH, size.getZ() / 2); loc = loc.offset(Direction.WEST, size.getX() / 2);
loc = loc.offset(Direction.WEST, size.getX() / 2); return loc.offset(Direction.DOWN, size.getY());
loc = loc.offset(Direction.DOWN, size.getY()); }
} case CLOCKWISE_90:
switch (direction) {
case WEST:
return loc.offset(Direction.NORTH, size.getX() / 2);
case EAST://positive x
loc = loc.offset(Direction.NORTH, size.getX() / 2);
return loc.offset(Direction.EAST, size.getZ() - 1);
case NORTH:
loc = loc.offset(Direction.NORTH, size.getX() - 1);
return loc.offset(Direction.EAST, size.getZ() / 2);
case SOUTH://positive z
return loc.offset(Direction.EAST, size.getZ() / 2);
case UP://positive y
loc = loc.offset(Direction.NORTH, size.getX() / 2);
loc = loc.offset(Direction.EAST, size.getZ() / 2);
return loc.offset(Direction.UP);
case DOWN:
loc = loc.offset(Direction.NORTH, size.getX() / 2);
loc = loc.offset(Direction.EAST, size.getZ() / 2);
return loc.offset(Direction.DOWN, size.getY());
}
case CLOCKWISE_180:
switch (direction) {
case WEST:
return loc.offset(Direction.SOUTH, size.getX() / 2);
case EAST://positive x
loc = loc.offset(Direction.SOUTH, size.getZ() / 2);
return loc.offset(Direction.EAST, size.getX() - 1);
case NORTH:
return loc.offset(Direction.EAST, size.getX() / 2);
case SOUTH://positive z
loc = loc.offset(Direction.EAST, size.getX() / 2);
return loc.offset(Direction.SOUTH, size.getZ() - 1);
case UP://positive y
loc = loc.offset(Direction.SOUTH, size.getZ() / 2);
loc = loc.offset(Direction.EAST, size.getX() / 2);
return loc.offset(Direction.UP);
case DOWN:
loc = loc.offset(Direction.SOUTH, size.getZ() / 2);
loc = loc.offset(Direction.EAST, size.getX() / 2);
return loc.offset(Direction.DOWN, size.getY());
}
case COUNTERCLOCKWISE_90:
switch (direction) {
case WEST:
loc = loc.offset(Direction.SOUTH, size.getX() / 2);
return loc.offset(Direction.WEST, size.getZ() - 1);
case EAST://positive x
return loc.offset(Direction.SOUTH, size.getX() / 2);
case NORTH:
return loc.offset(Direction.WEST, size.getZ() / 2);
case SOUTH://positive z
loc = loc.offset(Direction.SOUTH, size.getX() - 1);
return loc.offset(Direction.WEST, size.getZ() / 2);
case UP://positive y
loc = loc.offset(Direction.SOUTH, size.getX() / 2);
loc = loc.offset(Direction.WEST, size.getZ() / 2);
return loc.offset(Direction.UP);
case DOWN:
loc = loc.offset(Direction.SOUTH, size.getX() / 2);
loc = loc.offset(Direction.WEST, size.getZ() / 2);
return loc.offset(Direction.DOWN, size.getY());
}
} }
return loc; return loc;
} }
public void setRotation(BlockRotation rotation) {
this.rotation = rotation;
}
} }

View File

@ -5,6 +5,7 @@
"items.structure.spawner.structure.nonexistent": "Structure: %s does not exist", "items.structure.spawner.structure.nonexistent": "Structure: %s does not exist",
"items.structure.spawner.invalid.block": "Block %s invalid", "items.structure.spawner.invalid.block": "Block %s invalid",
"items.structure.spawner.invalid.offsetV2": "offsetV2 tag %s invalid: %s", "items.structure.spawner.invalid.offsetV2": "offsetV2 tag %s invalid: %s",
"items.structure.spawner.invalid.rotate": "rotate tag invalid direction: %s",
"items.structure.spawner.no.tag": "Item has no NBT tag", "items.structure.spawner.no.tag": "Item has no NBT tag",
"items.structure.spawner.no.structure": "Item has no String \"structure\": tag", "items.structure.spawner.no.structure": "Item has no String \"structure\": tag",
"item.structure_item.item": "Structure Spawner", "item.structure_item.item": "Structure Spawner",
@ -12,7 +13,7 @@
"item.structure_item.item.tooltip.structure": "Places down: ", "item.structure_item.item.tooltip.structure": "Places down: ",
"item.structure_item.item.tooltip.allowed.on": "Can be placed on: ", "item.structure_item.item.tooltip.allowed.on": "Can be placed on: ",
"item.structure_item.item.tooltip.fixed.offset": "Offset:", "item.structure_item.item.tooltip.fixed.offset": "Offset:",
"item.structure_item.item.tooltip.v2.offset": "Advanced Offset:", "item.structure_item.item.tooltip.v2.offset": "Advanced offset:",
"item.structure_item.item.tooltip.xyz":" x: %s y: %s z: %s", "item.structure_item.item.tooltip.xyz":" x: %s y: %s z: %s",
"item.structure_item.item.tooltip.xFuncOff":" x: %s", "item.structure_item.item.tooltip.xFuncOff":" x: %s",
"item.structure_item.item.tooltip.yFuncOff":" y: %s", "item.structure_item.item.tooltip.yFuncOff":" y: %s",
@ -21,7 +22,14 @@
"item.structure_item.item.tooltip.blacklist": "Blacklist:", "item.structure_item.item.tooltip.blacklist": "Blacklist:",
"item.structure_item.item.tooltip.blacklist.more": " And %s more...", "item.structure_item.item.tooltip.blacklist.more": " And %s more...",
"item.structure_item.item.tooltip.replaceEntities": "Deletes Entities in the way", "item.structure_item.item.tooltip.replaceEntities": "Deletes Entities in the way",
"item.structure_item.item.tooltip.doNotReplaceEntities": "Doesn't allow placement with Entities in the way", "item.structure_item.item.tooltip.doNotReplaceEntities": "Doesn't allow placement with entities in the way",
"item.structure_item.item.tooltip.placeEntities": "Places contained Entities", "item.structure_item.item.tooltip.placeEntities": "Places contained Entities",
"item.structure_item.item.tooltip.doNotPlaceEntities": "Doesn't place contained Entities" "item.structure_item.item.tooltip.doNotPlaceEntities": "Doesn't place contained entities",
"item.structure_item.item.tooltip.dynamic.rotation": "Rotated dynamically",
"item.structure_item.item.tooltip.dynamic.rotation.value": "Player is standing %s of structure ",
"item.structure_item.item.tooltip.dynamic.dir.north": "north",
"item.structure_item.item.tooltip.dynamic.dir.south": "south",
"item.structure_item.item.tooltip.dynamic.dir.east": "east",
"item.structure_item.item.tooltip.dynamic.dir.west": "west",
"item.structure_item.item.tooltip.fixed.rotation": "Fixed rotation"
} }