Skip to content

Commit 0104746

Browse files
committed
feat: Improve phasing mechanics
1 parent e274f29 commit 0104746

1 file changed

Lines changed: 68 additions & 54 deletions

File tree

‎core/src/main/java/com/nisovin/magicspells/spells/instant/PhaseSpell.java‎

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import java.util.List;
44
import java.util.ArrayList;
5+
import java.util.function.Predicate;
56

67
import org.bukkit.Material;
78
import org.bukkit.Location;
89
import org.bukkit.block.Block;
10+
import org.bukkit.util.BoundingBox;
11+
import org.bukkit.FluidCollisionMode;
912
import org.bukkit.util.BlockIterator;
1013

1114
import com.nisovin.magicspells.util.*;
@@ -15,34 +18,45 @@
1518

1619
public class PhaseSpell extends InstantSpell {
1720

18-
private final List<Material> phasableBlocks;
19-
private final List<Material> nonPhasableBlocks;
20-
2121
private final ConfigData<Integer> maxDistance;
22+
private final ConfigData<Integer> requireGroundDistance;
23+
24+
private final ConfigData<Boolean> airOnlyExit;
25+
private final ConfigData<Boolean> snapToGround;
26+
private final ConfigData<Boolean> ignoreTransparentBlocks;
2227
private final ConfigData<Boolean> powerAffectsMaxDistance;
23-
private String strCantPhase;
28+
29+
private final String strCantPhase;
30+
31+
private final List<Material> enterBlocks = new ArrayList<>();
32+
private final List<Material> nonExitBlocks = new ArrayList<>();
33+
private final List<Material> phasableBlocks = new ArrayList<>();
34+
private final List<Material> nonPhasableBlocks = new ArrayList<>();
2435

2536
public PhaseSpell(MagicConfig config, String spellName) {
2637
super(config, spellName);
2738

2839
maxDistance = getConfigDataInt("max-distance", 15);
29-
strCantPhase = getConfigString("str-cant-phase", "Unable to find place to phase to.");
40+
requireGroundDistance = getConfigDataInt("require-ground-distance", 0);
41+
42+
airOnlyExit = getConfigDataBoolean("air-only-exit", true);
43+
snapToGround = getConfigDataBoolean("snap-to-ground", false);
44+
ignoreTransparentBlocks = getConfigDataBoolean("ignore-transparent-blocks", false);
3045
powerAffectsMaxDistance = getConfigDataBoolean("power-affects-max-distance", true);
3146

32-
phasableBlocks = new ArrayList<>();
33-
nonPhasableBlocks = new ArrayList<>();
47+
strCantPhase = getConfigString("str-cant-phase", "Unable to find place to phase to.");
3448

49+
processMaterials(enterBlocks, "enter-blocks");
50+
processMaterials(nonExitBlocks, "non-exit-blocks");
3551
processMaterials(phasableBlocks, "phasable-blocks");
3652
processMaterials(nonPhasableBlocks, "non-phasable-blocks");
3753
}
3854

3955
private void processMaterials(List<Material> materials, String path) {
40-
List<String> matList = getConfigStringList(path, null);
41-
if (matList == null || matList.isEmpty()) return;
42-
for (String mat : matList) {
56+
for (String mat : getConfigStringList(path, List.of())) {
4357
Material material = Util.getMaterial(mat);
4458
if (material == null) {
45-
MagicSpells.error("PhaseSpell has an invalid material specified on '" + path + "': " + mat);
59+
MagicSpells.error("PhaseSpell '" + internalName + "' has an invalid material specified on '" + path + "': " + mat);
4660
continue;
4761
}
4862
materials.add(material);
@@ -51,51 +65,68 @@ private void processMaterials(List<Material> materials, String path) {
5165

5266
@Override
5367
public CastResult cast(SpellData data) {
54-
int r = getRange(data);
68+
int range = getRange(data);
69+
int rangeSquared = range * range;
70+
Location casterLoc = data.caster().getLocation();
5571

5672
int distance = maxDistance.get(data);
5773
if (powerAffectsMaxDistance.get(data)) distance = Math.round(distance * data.power());
74+
int distanceSquared = distance * distance;
5875

5976
BlockIterator iter;
6077
try {
61-
iter = new BlockIterator(data.caster(), distance << 1);
78+
iter = new BlockIterator(data.caster(), distance);
6279
} catch (IllegalStateException e) {
63-
sendMessage(strCantPhase, data.caster(), data);
80+
sendMessage(strCantPhase, data);
6481
return new CastResult(PostCastAction.ALREADY_HANDLED, data);
6582
}
6683

67-
int i = 0;
68-
Block start = null;
69-
Location location = null, casterLoc = data.caster().getLocation();
84+
Predicate<Location> transparent = ignoreTransparentBlocks.get(data) ? isTransparent(data) : l -> l.getBlock().isEmpty();
7085

71-
while (i++ < r << 1 && iter.hasNext()) {
86+
while (iter.hasNext()) {
7287
Block b = iter.next();
73-
if (b.getType().isAir()) continue;
74-
if (casterLoc.distanceSquared(b.getLocation()) >= r * r) continue;
75-
start = b;
76-
break;
88+
Location loc = b.getLocation();
89+
90+
if (enterBlocks.contains(b.getType())) break;
91+
if (transparent.test(loc)) continue;
92+
93+
if (casterLoc.distanceSquared(loc) < rangeSquared && canPassThrough(b)) break;
94+
95+
sendMessage(strCantPhase, data);
96+
return new CastResult(PostCastAction.ALREADY_HANDLED, data);
7797
}
7898

79-
if (start != null) {
80-
if (canPassThrough(start)) {
81-
while (i++ < distance << 1 && iter.hasNext()) {
82-
Block block = iter.next();
83-
if (block.getType().isAir() && block.getRelative(0, 1, 0).getType().isAir() && casterLoc.distanceSquared(block.getLocation()) < distance * distance) {
84-
location = block.getLocation();
85-
break;
86-
}
87-
if (!canPassThrough(block)) break;
88-
}
99+
Location location = null;
100+
boolean airOnlyExit = this.airOnlyExit.get(data);
101+
boolean snapToGround = this.snapToGround.get(data);
102+
int requireGroundDistance = this.requireGroundDistance.get(data);
103+
104+
while (iter.hasNext()) {
105+
Block block = iter.next();
106+
Location loc = block.getLocation().add(0.5, 0, 0.5);
107+
108+
if (casterLoc.distanceSquared(loc) >= distanceSquared) break;
109+
110+
Location adjusted = BlockUtils.adjustToSafeLocation(data.caster(), loc, requireGroundDistance, snapToGround);
111+
if (adjusted == null) continue;
112+
113+
BoundingBox box = data.caster().getBoundingBox();
114+
box.shift(adjusted.clone().subtract(casterLoc));
115+
if (!Util.hasCollisionsIn(data.caster().getWorld(), box, false, FluidCollisionMode.NEVER,
116+
b -> (airOnlyExit && !b.isEmpty()) || nonExitBlocks.contains(b.getType())
117+
)) {
118+
location = adjusted;
119+
break;
89120
}
121+
122+
if (!canPassThrough(adjusted.getBlock())) break;
90123
}
91124

92125
if (location == null) {
93-
sendMessage(strCantPhase, data.caster(), data);
126+
sendMessage(strCantPhase, data);
94127
return new CastResult(PostCastAction.ALREADY_HANDLED, data);
95128
}
96129

97-
location.setX(location.getX() + 0.5);
98-
location.setZ(location.getZ() + 0.5);
99130
location.setPitch(casterLoc.getPitch());
100131
location.setYaw(casterLoc.getYaw());
101132
data = data.location(location);
@@ -107,25 +138,8 @@ public CastResult cast(SpellData data) {
107138
}
108139

109140
private boolean canPassThrough(Block block) {
110-
// Check only blacklist.
111-
if (phasableBlocks.isEmpty()) return !nonPhasableBlocks.contains(block.getType());
112-
return phasableBlocks.contains(block.getType()) && !nonPhasableBlocks.contains(block.getType());
113-
}
114-
115-
public List<Material> getPhasableBlocks() {
116-
return phasableBlocks;
117-
}
118-
119-
public List<Material> getNonPhasableBlocks() {
120-
return nonPhasableBlocks;
121-
}
122-
123-
public String getStrCantPhase() {
124-
return strCantPhase;
125-
}
126-
127-
public void setStrCantPhase(String strCantPhase) {
128-
this.strCantPhase = strCantPhase;
141+
Material type = block.getType();
142+
return !nonPhasableBlocks.contains(type) && (phasableBlocks.isEmpty() || phasableBlocks.contains(type));
129143
}
130144

131145
}

0 commit comments

Comments
 (0)