Skip to content

Commit 92776e8

Browse files
ChronokenTheComputerGeek2
authored andcommitted
Added new NovaSpell.
Spell Options: type: blocktype(string) relative-offset: relativeOffset(x,y,z) spell: spell casted when a new wave forms(string) spell-on-end: requires remove-previous-blocks to be set to false, casts it when all block waves are gone(string) spell-on-wave-remove: requires remove-previous-blocks to be set to true, casts on the previous removed wave(string) radius: radius of the nova(int) start-radius: start radius of the nova(int) height-per-tick: height per new wave(int) expand-interval: interval between forming a new wave(int) expanding-radius-change: how big should each wave be, in blocks(int) visible-range: visible range of the nova(double) point-blank: point blank(boolean) circle-shape: whether the nova should be a circle or a square(boolean) remove-previous-blocks: whether it should remove the previous wave of blocks(boolean)
1 parent e53980a commit 92776e8

1 file changed

Lines changed: 365 additions & 0 deletions

File tree

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
package com.nisovin.magicspells.spells.targeted;
2+
3+
import java.util.Set;
4+
import java.util.List;
5+
import java.util.HashSet;
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
9+
import org.bukkit.Location;
10+
import org.bukkit.Material;
11+
import org.bukkit.util.Vector;
12+
import org.bukkit.block.Block;
13+
import org.bukkit.entity.Entity;
14+
import org.bukkit.entity.Player;
15+
import org.bukkit.block.BlockFace;
16+
import org.bukkit.entity.LivingEntity;
17+
18+
import com.nisovin.magicspells.Subspell;
19+
import com.nisovin.magicspells.util.Util;
20+
import com.nisovin.magicspells.MagicSpells;
21+
import com.nisovin.magicspells.util.MagicConfig;
22+
import com.nisovin.magicspells.spells.TargetedSpell;
23+
import com.nisovin.magicspells.materials.MagicMaterial;
24+
import com.nisovin.magicspells.spells.TargetedEntitySpell;
25+
import com.nisovin.magicspells.spells.TargetedLocationSpell;
26+
27+
public class NovaSpell extends TargetedSpell implements TargetedLocationSpell, TargetedEntitySpell {
28+
29+
MagicMaterial material;
30+
Vector relativeOffset;
31+
Subspell spellOnEnd;
32+
Subspell locationSpell;
33+
Subspell spellOnWaveRemove;
34+
String spellOnEndName;
35+
String locationSpellName;
36+
String spellOnWaveRemoveName;
37+
38+
int radius;
39+
int startRadius;
40+
int heightPerTick;
41+
int novaTickInterval;
42+
int expandingRadiusChange;
43+
44+
double visibleRange;
45+
46+
boolean pointBlank;
47+
boolean circleShape;
48+
boolean removePreviousBlocks;
49+
50+
public NovaSpell(MagicConfig config, String spellName) {
51+
super(config, spellName);
52+
53+
material = MagicSpells.getItemNameResolver().resolveBlock(getConfigString("type", "water"));
54+
relativeOffset = getConfigVector("relative-offset", "0,0,0");
55+
spellOnEndName = getConfigString("spell-on-end", "");
56+
locationSpellName = getConfigString("spell", "");
57+
spellOnWaveRemoveName = getConfigString("spell-on-wave-remove", "");
58+
59+
radius = getConfigInt("radius", 3);
60+
startRadius = getConfigInt("start-radius", 0);
61+
heightPerTick = getConfigInt("height-per-tick", 0);
62+
novaTickInterval = getConfigInt("expand-interval", 5);
63+
expandingRadiusChange = getConfigInt("expanding-radius-change", 1);
64+
if (expandingRadiusChange < 1) expandingRadiusChange = 1;
65+
66+
visibleRange = Math.max(getConfigDouble("visible-range", 20), 20);
67+
pointBlank = getConfigBoolean("point-blank", true);
68+
circleShape = getConfigBoolean("circle-shape", false);
69+
removePreviousBlocks = getConfigBoolean("remove-previous-blocks", true);
70+
71+
}
72+
73+
@Override
74+
public void initialize() {
75+
super.initialize();
76+
77+
locationSpell = new Subspell(locationSpellName);
78+
if (!locationSpell.process() || !locationSpell.isTargetedLocationSpell()) {
79+
if (!locationSpellName.isEmpty()) MagicSpells.error("NovaSpell " + internalName + " has an invalid spell defined!");
80+
locationSpell = null;
81+
}
82+
83+
spellOnWaveRemove = new Subspell(spellOnWaveRemoveName);
84+
if (!spellOnWaveRemove.process() || !spellOnWaveRemove.isTargetedLocationSpell()) {
85+
if (!spellOnWaveRemoveName.isEmpty()) MagicSpells.error("NovaSpell " + internalName + " has an invalid spell-on-wave-remove defined!");
86+
spellOnWaveRemove = null;
87+
}
88+
89+
spellOnEnd = new Subspell(spellOnEndName);
90+
if (!spellOnEnd.process() || !spellOnEnd.isTargetedLocationSpell()) {
91+
if (!spellOnEndName.isEmpty()) MagicSpells.error("NovaSpell " + internalName + " has an invalid spell-on-end defined!");
92+
spellOnEnd = null;
93+
}
94+
95+
if (material == null) {
96+
MagicSpells.error("NovaSpell " + internalName + " has an invalid block type defined!");
97+
}
98+
}
99+
100+
@Override
101+
public PostCastAction castSpell(Player player, SpellCastState spellCastState, float power, String[] strings) {
102+
if (spellCastState == SpellCastState.NORMAL) {
103+
104+
Location loc;
105+
if (pointBlank) loc = player.getLocation();
106+
else loc = getTargetedBlock(player, power).getLocation();
107+
108+
createNova(player, loc, power);
109+
}
110+
111+
return PostCastAction.HANDLE_NORMALLY;
112+
}
113+
114+
@Override
115+
public boolean castAtEntity(Player player, LivingEntity livingEntity, float v) {
116+
createNova(player, livingEntity.getLocation(), v);
117+
return false;
118+
}
119+
120+
@Override
121+
public boolean castAtEntity(LivingEntity livingEntity, float v) {
122+
return false;
123+
}
124+
125+
@Override
126+
public boolean castAtLocation(Player player, Location location, float v) {
127+
createNova(player, location, v);
128+
return false;
129+
}
130+
131+
@Override
132+
public boolean castAtLocation(Location location, float v) {
133+
return false;
134+
}
135+
136+
void createNova(Player pl, Location loc, float power) {
137+
if (material == null) return;
138+
// Relative offset
139+
Location startLoc = loc.clone();
140+
Vector direction = pl.getLocation().getDirection().normalize();
141+
Vector horizOffset = new Vector(-direction.getZ(), 0.0, direction.getX()).normalize();
142+
startLoc.add(horizOffset.multiply(relativeOffset.getZ())).getBlock().getLocation();
143+
startLoc.add(direction.setY(0).normalize().multiply(relativeOffset.getX()));
144+
startLoc.add(0, relativeOffset.getY(), 0);
145+
146+
// Get nearby players
147+
Collection<Entity> nearbyEntities = startLoc.getWorld().getNearbyEntities(startLoc, visibleRange, visibleRange, visibleRange);
148+
List<Player> nearby = new ArrayList<>();
149+
for (Entity e : nearbyEntities) {
150+
if (!(e instanceof Player)) continue;
151+
nearby.add((Player) e);
152+
}
153+
154+
// Start tracker
155+
if (!circleShape) new NovaTrackerSquare(nearby, startLoc.getBlock(), material, pl, radius, novaTickInterval, expandingRadiusChange, power);
156+
else new NovaTrackerCircle(nearby, startLoc.getBlock(), material, pl, radius, novaTickInterval, expandingRadiusChange, power);
157+
}
158+
159+
private class NovaTrackerSquare implements Runnable {
160+
161+
MagicMaterial matNova;
162+
List<Player> nearby;
163+
Set<Block> blocks;
164+
Player caster;
165+
Block center;
166+
float power;
167+
int radiusNova;
168+
int radiusChange;
169+
int taskId;
170+
int count;
171+
int temp;
172+
173+
public NovaTrackerSquare(List<Player> nearby, Block center, MagicMaterial mat, Player caster, int radius, int tickInterval, int activeRadiusChange, float power) {
174+
this.nearby = nearby;
175+
this.center = center;
176+
this.matNova = mat;
177+
this.caster = caster;
178+
this.power = power;
179+
this.radiusNova = radius;
180+
this.blocks = new HashSet<>();
181+
this.radiusChange = activeRadiusChange;
182+
this.taskId = MagicSpells.scheduleRepeatingTask(this, 0, tickInterval);
183+
184+
this.count = 0;
185+
this.temp = 0;
186+
}
187+
188+
@Override
189+
public void run() {
190+
temp = count;
191+
temp += startRadius;
192+
temp *= radiusChange;
193+
count++;
194+
195+
if (removePreviousBlocks) {
196+
for (Block b : blocks) {
197+
for (Player p : nearby) Util.restoreFakeBlockChange(p, b);
198+
if (spellOnWaveRemove != null) spellOnWaveRemove.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
199+
}
200+
blocks.clear();
201+
}
202+
203+
if (temp > radiusNova + 1) {
204+
stop();
205+
return;
206+
} else if (temp > radiusNova) {
207+
return;
208+
}
209+
210+
int bx = center.getX();
211+
int y = center.getY();
212+
int bz = center.getZ();
213+
y += count * heightPerTick;
214+
215+
for (int x = bx - temp; x <= bx + temp; x++) {
216+
for (int z = bz - temp; z <= bz + temp; z++) {
217+
if (Math.abs(x - bx) != temp && Math.abs(z - bz) != temp) continue;
218+
219+
Block b = center.getWorld().getBlockAt(x, y, z);
220+
if (b.getType() == Material.AIR || b.getType() == Material.LONG_GRASS) {
221+
Block under = b.getRelative(BlockFace.DOWN);
222+
if (under.getType() == Material.AIR || under.getType() == Material.LONG_GRASS) b = under;
223+
} else if (b.getRelative(BlockFace.UP).getType() == Material.AIR || b.getRelative(BlockFace.UP).getType() == Material.LONG_GRASS) {
224+
b = b.getRelative(BlockFace.UP);
225+
}
226+
227+
if (b.getType() != Material.AIR && b.getType() != Material.LONG_GRASS) continue;
228+
229+
if (blocks.contains(b)) continue;
230+
for (Player p : nearby) Util.sendFakeBlockChange(p, b, matNova);
231+
blocks.add(b);
232+
if (locationSpell != null) locationSpell.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
233+
}
234+
}
235+
236+
}
237+
238+
public void stop() {
239+
for (Block b : blocks) {
240+
for (Player p : nearby) Util.restoreFakeBlockChange(p, b);
241+
if (spellOnEnd != null) spellOnEnd.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
242+
}
243+
blocks.clear();
244+
MagicSpells.cancelTask(taskId);
245+
}
246+
247+
}
248+
249+
private class NovaTrackerCircle implements Runnable {
250+
251+
MagicMaterial matNova;
252+
List<Player> nearby;
253+
Set<Block> blocks;
254+
Player caster;
255+
Block center;
256+
float power;
257+
int radiusNova;
258+
int radiusChange;
259+
int taskId;
260+
int count;
261+
int temp;
262+
263+
public NovaTrackerCircle(List<Player> nearby, Block center, MagicMaterial mat, Player caster, int radius, int tickInterval, int activeRadiusChange, float power) {
264+
this.nearby = nearby;
265+
this.center = center;
266+
this.matNova = mat;
267+
this.caster = caster;
268+
this.power = power;
269+
this.radiusNova = radius;
270+
this.blocks = new HashSet<>();
271+
this.radiusChange = activeRadiusChange;
272+
this.taskId = MagicSpells.scheduleRepeatingTask(this, 0, tickInterval);
273+
274+
this.count = 0;
275+
this.temp = 0;
276+
}
277+
278+
@Override
279+
public void run() {
280+
temp = count;
281+
temp += startRadius;
282+
temp *= radiusChange;
283+
count++;
284+
285+
// Remove old blocks
286+
if (removePreviousBlocks) {
287+
for (Block b : blocks) {
288+
for (Player p : nearby) Util.restoreFakeBlockChange(p, b);
289+
if (spellOnWaveRemove != null) spellOnWaveRemove.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
290+
}
291+
blocks.clear();
292+
}
293+
294+
if (temp > radiusNova + 1) {
295+
stop();
296+
return;
297+
} else if (temp > radiusNova) {
298+
return;
299+
}
300+
301+
// Generate the bottom block
302+
Location centerLocation = center.getLocation().clone();
303+
centerLocation.add(0.5, count * heightPerTick, 0.5);
304+
Block b;
305+
306+
if (startRadius == 0 && temp == 0) {
307+
b = centerLocation.getWorld().getBlockAt(centerLocation);
308+
309+
if (b.getType() == Material.AIR || b.getType() == Material.LONG_GRASS) {
310+
Block under = b.getRelative(BlockFace.DOWN);
311+
if (under.getType() == Material.AIR || under.getType() == Material.LONG_GRASS) b = under;
312+
} else if (b.getRelative(BlockFace.UP).getType() == Material.AIR || b.getRelative(BlockFace.UP).getType() == Material.LONG_GRASS) {
313+
b = b.getRelative(BlockFace.UP);
314+
}
315+
316+
if (b.getType() != Material.AIR && b.getType() != Material.LONG_GRASS) return;
317+
318+
if (blocks.contains(b)) return;
319+
for (Player p : nearby) Util.sendFakeBlockChange(p, b, matNova);
320+
blocks.add(b);
321+
if (locationSpell != null) locationSpell.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
322+
}
323+
324+
// Generate the circle
325+
Vector v;
326+
double angle, x, z;
327+
double amount = temp * 64;
328+
double inc = (2 * Math.PI) / amount;
329+
for (int i = 0; i < amount; i++) {
330+
angle = i * inc;
331+
x = temp * Math.cos(angle);
332+
z = temp * Math.sin(angle);
333+
v = new Vector(x, 0, z);
334+
b = center.getWorld().getBlockAt(centerLocation.add(v));
335+
centerLocation.subtract(v);
336+
337+
if (b.getType() == Material.AIR || b.getType() == Material.LONG_GRASS) {
338+
Block under = b.getRelative(BlockFace.DOWN);
339+
if (under.getType() == Material.AIR || under.getType() == Material.LONG_GRASS) b = under;
340+
} else if (b.getRelative(BlockFace.UP).getType() == Material.AIR || b.getRelative(BlockFace.UP).getType() == Material.LONG_GRASS) {
341+
b = b.getRelative(BlockFace.UP);
342+
}
343+
344+
if (b.getType() != Material.AIR && b.getType() != Material.LONG_GRASS) continue;
345+
346+
if (blocks.contains(b)) continue;
347+
for (Player p : nearby) Util.sendFakeBlockChange(p, b, matNova);
348+
blocks.add(b);
349+
if (locationSpell != null) locationSpell.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
350+
}
351+
352+
}
353+
354+
public void stop() {
355+
for (Block b : blocks) {
356+
for (Player p : nearby) Util.restoreFakeBlockChange(p, b);
357+
if (spellOnEnd != null) spellOnEnd.castAtLocation(caster, b.getLocation().add(0.5, 0, 0.5), power);
358+
}
359+
blocks.clear();
360+
MagicSpells.cancelTask(taskId);
361+
}
362+
363+
}
364+
365+
}

0 commit comments

Comments
 (0)