Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class MaterializeSpell extends TargetedSpell implements TargetedLocationS

//Normal Features
private Set<Material> materials;
private Material material;
private Material defaultMaterial;
private int resetDelay;
private boolean falling;
private boolean applyPhysics;
Expand All @@ -45,6 +45,7 @@ public class MaterializeSpell extends TargetedSpell implements TargetedLocationS
private boolean usePattern;
private List<String> patterns;
private Material[][] rowPatterns;
private boolean hasPatterns;
private boolean restartPatternEachRow;
private boolean randomizePattern;
private boolean stretchPattern;
Expand All @@ -58,15 +59,15 @@ public class MaterializeSpell extends TargetedSpell implements TargetedLocationS
private int rowSize;
private int columnSize;

//Cuboid Checks;
private boolean hasMiddle;

public MaterializeSpell(MagicConfig config, String spellName) {
super(config, spellName);

String blockType = getConfigString("block-type", "stone");
material = Util.getMaterial(blockType);
if (material == null || !material.isBlock()) MagicSpells.error("MaterializeSpell '" + internalName + "' has an invalid block-type defined!");
defaultMaterial = Util.getMaterial(blockType);
if (defaultMaterial == null || !defaultMaterial.isBlock()) {
MagicSpells.error("MaterializeSpell '" + internalName + "' has an invalid block-type defined!");
defaultMaterial = Material.STONE;
}

resetDelay = getConfigInt("reset-delay", 0);
falling = getConfigBoolean("falling", false);
Expand All @@ -93,37 +94,23 @@ public MaterializeSpell(MagicConfig config, String spellName) {
public void initialize() {
super.initialize();

//First, lets split the "area" that was given.
String[] areaParts = area.split("x", 2);

//Let's define the size of the row and column to form a shape array;
rowSize = Integer.parseInt(areaParts[0]);
columnSize = Integer.parseInt(areaParts[1]);

/*For this to work smoothly, we need to see if the shape array has a middle;
It becomes very complicated when working with shape arrays without a block as a geometrical middle
So unfortunately. Shape arrays without a block as its geometrical center cannot be accepted.
3x2, 9x8. Basically, if the product of the length and width is even. Don't use it. */
hasMiddle = ((rowSize * columnSize) % 2) == 1;

if (!hasMiddle && patterns != null) {
MagicSpells.error("MaterializeSpell " + internalName + " is using a shape array without a geometrical center! A single block will spawn instead.");
}
parseArea();

//After the reset-delay passes, we need to remove all the blocks that were materialized.
//We store them within "materials" and "rowPatterns" as well
boolean ready;

materials = new HashSet<>();
materials.add(defaultMaterial);
hasPatterns = usePattern && patterns != null && !patterns.isEmpty();

if (patterns != null) ready = parseBlocks(patterns);
if (hasPatterns) ready = parseBlocks(patterns);
else ready = false;

//If the parser failed, we'll have to force a string inside;
if (!ready) {
rowPatterns = new Material[1][1];
rowPatterns[0][0] = material;
materials.add(material);
rowPatterns[0][0] = defaultMaterial;
}
}

Expand Down Expand Up @@ -151,128 +138,114 @@ public CastResult cast(SpellData data) {

data = event.getSpellData();
block = event.getTargetLocation().getBlock();
against = resolveSupportBlock(against, block);

return castAtResolvedBlock(caster, block, against, data);
}

@Override
public CastResult castAtLocation(SpellData data) {
Block targetBlock = resolveTargetBlock(data);
if (targetBlock == null) return noTarget(strFailed, data);

Block against = targetBlock.getRelative(BlockFace.DOWN);

if (!data.hasCaster()) {
return castAtResolvedBlock(null, targetBlock, against, data);
}

if (!hasMiddle) {
boolean done = materialize(caster, block, against, data);
if (!done) return noTarget(strFailed, data);
return new CastResult(PostCastAction.HANDLE_NORMALLY, data);
if (!(data.caster() instanceof Player caster)) return new CastResult(PostCastAction.ALREADY_HANDLED, data);
return castAtResolvedBlock(caster, targetBlock, against, data);
}

private void parseArea() {
String[] areaParts = area.toLowerCase(Locale.ROOT).split("x", 2);
if (areaParts.length != 2) {
MagicSpells.error("MaterializeSpell '" + internalName + "' has an invalid area '" + area + "' defined. Falling back to 1x1.");
rowSize = 1;
columnSize = 1;
return;
}

//Unfortunately, shape array placement is world relative, will fix later.
//This is the top-left edge of the shape array
Location patternStart = against.getLocation();
try {
rowSize = Math.max(1, Integer.parseInt(areaParts[0]));
columnSize = Math.max(1, Integer.parseInt(areaParts[1]));
} catch (NumberFormatException e) {
MagicSpells.error("MaterializeSpell '" + internalName + "' has an invalid area '" + area + "' defined. Falling back to 1x1.");
rowSize = 1;
columnSize = 1;
}
}

patternStart.setX(against.getX() - Math.ceil(rowSize / 2F));
patternStart.setZ(against.getZ() - Math.ceil(columnSize / 2F));
private CastResult castAtResolvedBlock(Player player, Block block, Block against, SpellData data) {
data = data.location(block.getLocation());
int configuredHeight = height.get(data);

//spawnBlock is the current position in the loop where it will spawn the block
Location spawnBlock = patternStart;
boolean done = isSingleBlockPlacement(configuredHeight)
? materialize(player, block, against, defaultMaterial, data)
: materializeArea(player, block, data);

Block air;
Block ground;
return done ? new CastResult(PostCastAction.HANDLE_NORMALLY, data) : noTarget(strFailed, data);
}

/*The row position dictates which block within a row pattern will be used
when placing the new block.*/
int rowPosition = 0;
private boolean isSingleBlockPlacement(int configuredHeight) {
return rowSize == 1 && columnSize == 1 && Math.abs(configuredHeight) == 1 && !hasPatterns;
}

int height = this.height.get(data);
private Block resolveTargetBlock(SpellData data) {
Block block = data.location().getBlock();
if (block.isReplaceable()) return block;

//If height is 0, the code ceases to function. Let's not have that.
if (height == 0) height = 1;
Block upper = block.getRelative(BlockFace.UP);
return upper.isReplaceable() ? upper : null;
}

private Block resolveSupportBlock(Block previousSupport, Block targetBlock) {
if (targetBlock.getRelative(BlockFace.DOWN).equals(previousSupport)) return previousSupport;
return targetBlock.getRelative(BlockFace.DOWN);
}

private boolean materializeArea(Player player, Block centerBlock, SpellData data) {
Location start = centerBlock.getLocation().clone().add(-(rowSize / 2), 0, -(columnSize / 2));

//Let's start at the bottom floor then work our way up; or down if height is less than 0.
for (int y = 0; y < height; y++) {
/*The pattern position is the pattern being read for a specific row
This should always reset when it goes over into a new height.*/
int configuredHeight = height.get(data);
int layerDirection = configuredHeight < 0 ? -1 : 1;
int layerCount = Math.max(Math.abs(configuredHeight), 1);

int rowPosition = 0;
for (int layer = 0; layer < layerCount; layer++) {
int patternPosition = 0;
int yOffset = layer * layerDirection;

//The block placement loop will start finish a row of coloumns then move down a row.
for (int z = 0; z < columnSize; z++) {
//Everytime a shape row is finished, we need to start at the topleft and move down 1 row.
spawnBlock = patternStart.clone().add(0, y, z);

//Let's parse the list of patterns for that row.
if (patterns != null && patternPosition >= patterns.size()) patternPosition = 0;
if (hasPatterns && patternPosition >= rowPatterns.length) patternPosition = 0;

Comment on lines 220 to 222
int rowLength = getRowLength(patternPosition);

//If they want the pattern to restart on each row, reset rowpositon to 0.
if (restartPatternEachRow) rowPosition = 0;

//Let's spawn a block on each column before we move down a row.
for (int x = 0; x < rowSize; x++) {
ground = spawnBlock.getBlock();
air = ground.getRelative(BlockFace.UP);

//Now if we are looking for a block outside the rowlist range.
//We need to go back to the start and repeat that row pattern
if (rowPosition >= rowLength) rowPosition = 0;

//Doesn't really become a pattern if you randomize it but ok!
if (!stretchPattern || y < 1)
material = blockGenerator(randomizePattern, patternPosition, rowPosition);
else material = ground.getType();

//Add one to the row position so that it will move to the next block.
Block placeBlock = start.clone().add(x, yOffset, z).getBlock();
Material blockMaterial = resolveBlockMaterial(placeBlock, patternPosition, rowPosition, layerDirection, layer);
rowPosition++;

//As soon as a block can't be spawned, it will return an error.
boolean done = materialize(caster, air, ground, data.location(block.getLocation()));
if (!done) return noTarget(strFailed, data);

//Done with placing that one block? Move on to the next one.
spawnBlock.setX((ground.getX() + 1));
Block supportBlock = placeBlock.getRelative(0, -layerDirection, 0);
SpellData blockData = data.location(placeBlock.getLocation());
boolean done = materialize(player, placeBlock, supportBlock, blockMaterial, blockData);
if (!done) return false;
}
//If multiple patterns were requested, lets move to the next line.

patternPosition++;
}
}

return new CastResult(PostCastAction.HANDLE_NORMALLY, data);
return true;
}

@Override
public CastResult castAtLocation(SpellData data) {
if (!data.hasCaster()) {
Block block = data.location().getBlock();
if (block.getType().isAir()) {
boolean done = materialize(null, block, block, data);
return done ? new CastResult(PostCastAction.HANDLE_NORMALLY, data) : noTarget(strFailed, data);
}

Block block2 = block.getRelative(BlockFace.UP);
if (block2.getType().isAir()) {
data = data.location(block2.getLocation());

boolean done = materialize(null, block2, block, data);
return done ? new CastResult(PostCastAction.HANDLE_NORMALLY, data) : noTarget(strFailed, data);
}

return noTarget(strFailed, data);
}

if (!(data.caster() instanceof Player caster)) return new CastResult(PostCastAction.ALREADY_HANDLED, data);

Location target = data.location();

Block block = target.getBlock();
Block against = target.add(target.getDirection()).getBlock();
if (block.equals(against)) against = block.getRelative(BlockFace.DOWN);

if (block.getType().isAir()) {
boolean done = materialize(caster, block, against, data);
return done ? new CastResult(PostCastAction.HANDLE_NORMALLY, data) : noTarget(strFailed, data);
}

Block block2 = block.getRelative(BlockFace.UP);
if (block2.getType().isAir()) {
data = data.location(block2.getLocation());

boolean done = materialize(caster, block2, block, data);
return done ? new CastResult(PostCastAction.HANDLE_NORMALLY, data) : noTarget(strFailed, data);
}

return noTarget(strFailed, data);
private Material resolveBlockMaterial(Block placeBlock, int patternPosition, int rowPosition, int layerDirection, int layer) {
if (stretchPattern && layer > 0) return placeBlock.getRelative(0, -layerDirection, 0).getType();
return blockGenerator(randomizePattern, patternPosition, rowPosition);
}

private int getRowLength(int patternPosition) {
Expand Down Expand Up @@ -310,31 +283,35 @@ private boolean parseBlocks(List<String> patternList) {
}

private Material blockGenerator(boolean randomize, int patternPosition, int rowPosition) {
Material mat;
if (!hasPatterns) return defaultMaterial;

int randomIndex = random.nextInt(getRowLength(patternPosition));
return randomize ? rowPatterns[patternPosition][randomIndex] : rowPatterns[patternPosition][rowPosition];
}

if (!randomize) mat = rowPatterns[patternPosition][rowPosition];
else mat = rowPatterns[patternPosition][randomIndex];
private boolean materialize(Player player, Block block, Block against, Material blockMaterial, SpellData data) {
if (!block.isReplaceable()) return false;

return mat;
}
var blockData = blockMaterial.createBlockData();
if (!falling && !block.canPlace(blockData)) return false;

private boolean materialize(Player player, Block block, Block against, SpellData data) {
BlockState blockState = block.getState();

if (checkPlugins && player != null) {
block.setType(material, false);
block.setBlockData(blockData, false);
MagicSpellsBlockPlaceEvent event = new MagicSpellsBlockPlaceEvent(block, blockState, against, player.getEquipment().getItemInMainHand(), player, true);
EventUtil.call(event);
blockState.update(true);
if (event.isCancelled()) return false;

if (falling) blockState.update(true);
else if (applyPhysics) block.setBlockData(blockData, true);
Comment on lines 300 to +307
}
else if (!falling) block.setBlockData(blockData, applyPhysics);

if (falling) {
Location location = block.getLocation().add(0.5, fallHeight.get(data), 0.5);
block.getWorld().spawn(location, FallingBlock.class, fb -> fb.setBlockData(material.createBlockData()));
block.getWorld().spawn(location, FallingBlock.class, fb -> fb.setBlockData(blockData));
}
else block.setType(material, applyPhysics);

playSpellEffects(EffectPosition.TARGET, block.getLocation(), data);
if (player != null) {
Expand All @@ -343,7 +320,7 @@ private boolean materialize(Player player, Block block, Block against, SpellData
}

if (playBreakEffect) block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, block.getBlockData());
if (removeBlocks) blocks.add(block);
if (removeBlocks && !falling) blocks.add(block);

if (resetDelay > 0 && !falling) {
MagicSpells.scheduleDelayedTask(() -> {
Expand All @@ -355,9 +332,10 @@ private boolean materialize(Player player, Block block, Block against, SpellData
EventUtil.call(event);
if (event.isCancelled()) return;
}
var placedBlockData = block.getBlockData();
block.setType(Material.AIR);
playSpellEffects(EffectPosition.BLOCK_DESTRUCTION, block.getLocation(), data);
if (playBreakEffect) block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, block.getBlockData());
if (playBreakEffect) block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, placedBlockData);
}
}, resetDelay);
}
Expand Down
Loading