Tweak all Necromancer abilities so they function well together and give the Necromancer a semi-AI for movement

This commit is contained in:
AlexTheCoder 2016-07-05 17:08:38 -04:00
parent b409009069
commit 8c93f92a92
10 changed files with 231 additions and 89 deletions

View File

@ -275,7 +275,7 @@ public abstract class EventCreature<T extends LivingEntity> implements Listener
spawnEntity();
}
if (UtilMath.offset2d(_entity.getLocation(), _spawnLocation) > 30)
if (UtilMath.offset2d(_entity.getLocation(), _spawnLocation) > 24)
{
_entity.setVelocity(UtilAlg.getTrajectory(_entity.getLocation(), _spawnLocation).normalize().multiply(2));
}

View File

@ -1,12 +1,15 @@
package mineplex.minecraft.game.core.boss.necromancer;
import mineplex.core.blockrestore.BlockRestore;
import mineplex.core.common.util.F;
import mineplex.core.disguise.DisguiseManager;
import mineplex.core.projectile.ProjectileManager;
import mineplex.minecraft.game.core.boss.EventCreature;
import mineplex.minecraft.game.core.boss.EventState;
import mineplex.minecraft.game.core.boss.WorldEvent;
import mineplex.minecraft.game.core.boss.necromancer.abilities.WraithDeathEvent;
import mineplex.minecraft.game.core.boss.necromancer.minion.MinionType;
import mineplex.minecraft.game.core.boss.necromancer.minion.WraithCreature;
import mineplex.minecraft.game.core.condition.ConditionManager;
import mineplex.minecraft.game.core.damage.DamageManager;
@ -24,21 +27,21 @@ public class NecromancerBoss extends WorldEvent
@Override
protected void customStart()
{
Bukkit.broadcastMessage("Custom Start");
Bukkit.broadcastMessage(F.main(getName(), "The evils of the world have manifested in the form of the " + getName() + "! Become the champion of Light and destroy him!"));
spawnNecromancer(getCenterLocation());
setState(EventState.LIVE);
announceStart();
}
/**
* Check if this slime boss has been defeated
* Check if this necromancer boss has been defeated
*/
private void checkDeath()
{
if (getCreatures().size() == 0)
{
setState(EventState.COMPLETE);
Bukkit.broadcastMessage("FINISHED!");
Bukkit.broadcastMessage(F.main(getName(), "The demonic " + getName() + " has been slain!"));
}
}
@ -51,6 +54,12 @@ public class NecromancerBoss extends WorldEvent
{
checkDeath();
}
if (creature instanceof WraithCreature)
{
WraithDeathEvent event = new WraithDeathEvent((WraithCreature)creature);
Bukkit.getPluginManager().callEvent(event);
Bukkit.broadcastMessage("Calling Wraith Death");
}
}
public EventCreature spawnMinion(MinionType type, Location location)

View File

@ -5,14 +5,16 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import mineplex.core.common.events.EntityVelocityChangeEvent;
import mineplex.core.common.util.UtilAlg;
import mineplex.core.common.util.UtilBlock;
import mineplex.core.common.util.UtilEnt;
import mineplex.core.common.util.UtilMath;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.common.util.UtilTime;
import mineplex.core.updater.UpdateType;
import mineplex.core.updater.event.UpdateEvent;
import mineplex.minecraft.game.core.boss.BossAbility;
@ -35,13 +37,18 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
public class NecromancerCreature extends EventCreature<Skeleton>
{
private ArrayList<BossAbility> _currentAbilities = new ArrayList<BossAbility>();
private int _lastAbility;
private HashMap<Class, Long> _cooldowns = new HashMap<Class, Long>();
private boolean _hasUsedWraiths = false;
private boolean _hasUsedWraiths;
private List<Location> _movePoints;
private Location _movingTo;
private boolean _moving;
private long _lastMoved;
public NecromancerCreature(NecromancerBoss boss, Location location, double maxHealth)
{
@ -111,8 +118,8 @@ public class NecromancerCreature extends EventCreature<Skeleton>
if (_lastAbility-- <= 0 && canDoNew && UtilBlock.solid(getEntity().getLocation().getBlock().getRelative(BlockFace.DOWN)))
{
HashMap<Class, Integer> weight = new HashMap<Class, Integer>();
HashMap<Player, Double> dist = new HashMap<Player, Double>();
HashMap<Class<? extends BossAbility>, Integer> weight = new HashMap<>();
HashMap<Player, Double> dist = new HashMap<>();
for (Player player : UtilPlayer.getNearby(getEntity().getLocation(), 50, true))
{
@ -125,13 +132,14 @@ public class NecromancerCreature extends EventCreature<Skeleton>
if (!dist.isEmpty())
{
{// Strike and Pulse
ArrayList<Player> players = getPlayers(dist, UtilMath.r(10) == 0 ? 10 : 6);
ArrayList<Player> players = getPlayers(dist, UtilMath.r(10) == 0 ? 20 : 16);
ArrayList<Player> near = getPlayers(dist, 5);
if (!players.isEmpty())
{
if (players.size() >= 4 && new Random().nextDouble() <= .45)
if (!near.isEmpty() && near.size() >= 4 && new Random().nextDouble() <= .45)
{
weight.put(NecromancerPulse.class, 998);
weight.put(NecromancerPulse.class, 999);
}
else
{
@ -140,23 +148,36 @@ public class NecromancerCreature extends EventCreature<Skeleton>
}
}
{// Smite
ArrayList<Player> players = getPlayers(dist, 10);
ArrayList<Player> players = getPlayers(dist, 15);
if (!players.isEmpty())
{
weight.put(NecromancerSmite.class, 6);
}
}
if (getHealthPercent() < .7)
{//Hellish Flood
weight.put(NecromancerHellishFlood.class, 6);
}
if (getHealth() <= 90)
{// Wraith Summon
if (!_hasUsedWraiths)
ArrayList<Player> players = getPlayers(dist, 20);
double score = 0;
for (Player player : players)
{
weight.clear();
weight.put(NecromancerWraithSummon.class, 999);
score += (18 - dist.get(player)) / 2;
}
if (players.size() >= 4 || (!players.isEmpty() && players.get(0).isOp()))
{
score += 17;
}
if (score > 0)
{
weight.put(NecromancerHellishFlood.class, (int) Math.ceil(score));
}
}
Bukkit.broadcastMessage(_hasUsedWraiths + "");
if (getHealth() <= 90 && !_hasUsedWraiths)
{// Wraith Summon
_hasUsedWraiths = true;
weight.clear();
weight.put(NecromancerWraithSummon.class, 999);
}
}
@ -173,6 +194,25 @@ public class NecromancerCreature extends EventCreature<Skeleton>
}
}
if (_moving)
{
Iterator<Class<? extends BossAbility>> trying = weight.keySet().iterator();
while (trying.hasNext())
{
Class<? extends BossAbility> abilityClass = trying.next();
try
{
BossAbility ability = abilityClass.newInstance();
if (!ability.canMove())
{
trying.remove();
}
}
catch (Exception e) {}
}
}
BossAbility ability = null;
if (!weight.isEmpty())
@ -188,7 +228,7 @@ public class NecromancerCreature extends EventCreature<Skeleton>
{
int luckyNumber = UtilMath.r(i);
for (Entry<Class, Integer> entry : weight.entrySet())
for (Entry<Class<? extends BossAbility>, Integer> entry : weight.entrySet())
{
luckyNumber -= entry.getValue();
@ -196,7 +236,7 @@ public class NecromancerCreature extends EventCreature<Skeleton>
{
try
{
ability = (BossAbility) entry.getKey().getConstructor(NecromancerCreature.class).newInstance(this);
ability = entry.getKey().getConstructor(NecromancerCreature.class).newInstance(this);
if (ability.getTarget() == null || ability.hasFinished())
{
@ -224,11 +264,6 @@ public class NecromancerCreature extends EventCreature<Skeleton>
Bukkit.getPluginManager().registerEvents(ability, getEvent().getPlugin());
System.out.print("Necromancer is using " + ability.getClass().getSimpleName());
if (ability instanceof NecromancerWraithSummon)
{
_hasUsedWraiths = true;
}
_currentAbilities.add(ability);
}
@ -240,6 +275,63 @@ public class NecromancerCreature extends EventCreature<Skeleton>
{
ability.tick();
}
boolean canMove = true;
for (BossAbility ability : _currentAbilities)
{
if (!ability.canMove())
{
canMove = false;
}
}
if (_moving)
{
if (_movingTo == null)
{
_movingTo = selectWalkTarget();
}
if (UtilMath.offset(getEntity().getLocation(), _movingTo) > 1.3)
{
_lastMoved = System.currentTimeMillis();
_movingTo = null;
_moving = false;
return;
}
UtilEnt.LookAt(getEntity(), _movingTo);
Vector walk = UtilAlg.getTrajectory(getEntity().getLocation(), _movingTo);
walk.multiply(walk.length());
getEntity().setVelocity(walk);
}
else
{
if (!UtilTime.elapsed(_lastMoved, 7000) || !canMove)
{
return;
}
_movingTo = selectWalkTarget();
_moving = true;
}
}
private Location selectWalkTarget()
{
if (_movePoints.isEmpty())
{
generateWalkPoints(getEntity().getLocation());
}
Location selected = _movePoints.get(new Random().nextInt(_movePoints.size()));
_movePoints.remove(selected);
return selected;
}
private void generateWalkPoints(Location base)
{
_movePoints.add(base.clone().add(5 + UtilMath.random(1, 3), 0, 5 + UtilMath.random(1, 3)));
_movePoints.add(base.clone().add(-5 + UtilMath.random(1, 3), 0, 5 + UtilMath.random(1, 3)));
_movePoints.add(base.clone().add(5 + UtilMath.random(1, 3), 0, -5 + UtilMath.random(1, 3)));
_movePoints.add(base.clone().add(-5 + UtilMath.random(1, 3), 0, -5 + UtilMath.random(1, 3)));
}
private ArrayList<Player> getPlayers(HashMap<Player, Double> map, double maxDist)
@ -297,13 +389,4 @@ public class NecromancerCreature extends EventCreature<Skeleton>
event.SetCancelled("Boss Invulnerability");
}
}
@EventHandler
public void onVelocity(EntityVelocityChangeEvent event)
{
if (event.getEntity().getEntityId() == getEntity().getEntityId())
{
event.setCancelled(true);
}
}
}

View File

@ -13,6 +13,7 @@ import mineplex.minecraft.game.core.boss.necromancer.NecromancerBoss;
import mineplex.minecraft.game.core.boss.necromancer.NecromancerCreature;
import mineplex.minecraft.game.core.boss.necromancer.minion.MinionType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Skeleton;
@ -20,12 +21,13 @@ public class NecromancerHellishFlood extends BossAbility<NecromancerCreature, Sk
{
private static final int WAVE_COUNT = 3;
private static final int WAVE_SIZE = 5;
private static final MinionType[] POSSIBLE_MINIONS = new MinionType[] {MinionType.WARRIOR, MinionType.WRAITH};
private static final MinionType[] POSSIBLE_MINIONS = new MinionType[] {MinionType.WARRIOR, MinionType.ARCHER};
private static final long WAVE_DELAY = 1000;
private ConcurrentHashMap<Integer, MinionType[]> _waves = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, MinionType[]> _waves = new ConcurrentHashMap<>();
private long _lastSpawned;
private int _current;
private int _ticks;
public NecromancerHellishFlood(NecromancerCreature creature)
{
@ -54,7 +56,13 @@ public class NecromancerHellishFlood extends BossAbility<NecromancerCreature, Sk
{
wave[i] = POSSIBLE_MINIONS[new Random().nextInt(length)];
}
_waves.put(number, wave);
_waves.put("Wave " + number, wave);
}
@Override
public int getCooldown()
{
return 30;
}
@Override
@ -72,23 +80,26 @@ public class NecromancerHellishFlood extends BossAbility<NecromancerCreature, Sk
@Override
public boolean hasFinished()
{
return !_waves.isEmpty();
return _waves.isEmpty() && _ticks > ((WAVE_DELAY / 1000) * 20 * (WAVE_COUNT - 1));
}
@Override
public void setFinished()
{
_waves.clear();
_ticks = 60;
}
@Override
public void tick()
{
_ticks++;
if (UtilTime.elapsed(_lastSpawned, WAVE_DELAY))
{
if (_current <= _waves.size())
Bukkit.broadcastMessage("Current: " + _current + ", Size: " + WAVE_COUNT);
if (_current <= WAVE_COUNT)
{
for (MinionType type : _waves.get(_current))
for (MinionType type : _waves.get("Wave " + _current))
{
Location toSpawn = getLocation().clone();
toSpawn.add(UtilMath.random(3, 6), 0, UtilMath.random(3, 6));
@ -100,7 +111,9 @@ public class NecromancerHellishFlood extends BossAbility<NecromancerCreature, Sk
}
UtilParticle.PlayParticleToAll(ParticleType.WITCH_MAGIC, getEntity().getLocation(), null, 0, 2, ViewDist.MAX);
UtilParticle.PlayParticleToAll(ParticleType.SMOKE, getEntity().getLocation(), null, 0, 2, ViewDist.MAX);
_waves.remove(_current);
Bukkit.broadcastMessage(_waves.size() + " - Initial");
_waves.remove("Wave " + _current);
Bukkit.broadcastMessage(_waves.size() + " - After");
_current++;
_lastSpawned = System.currentTimeMillis();
}

View File

@ -12,6 +12,7 @@ import mineplex.minecraft.game.core.boss.BossAbility;
import mineplex.minecraft.game.core.boss.necromancer.NecromancerCreature;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.entity.Skeleton;
@ -21,6 +22,7 @@ public class NecromancerPulse extends BossAbility<NecromancerCreature, Skeleton>
private static final long TOTAL_ATTACK_DURATION = 3000;
private long _start, _lastIncrement;
private int _radius;
private Location _center;
public NecromancerPulse(NecromancerCreature creature)
{
@ -28,6 +30,7 @@ public class NecromancerPulse extends BossAbility<NecromancerCreature, Skeleton>
_start = System.currentTimeMillis();
_radius = 2;
_lastIncrement = System.currentTimeMillis();
_center = creature.getEntity().getLocation();
}
private int getRadius()
@ -38,7 +41,7 @@ public class NecromancerPulse extends BossAbility<NecromancerCreature, Skeleton>
@Override
public int getCooldown()
{
return 5;
return 7;
}
@Override
@ -79,7 +82,7 @@ public class NecromancerPulse extends BossAbility<NecromancerCreature, Skeleton>
double x = getRadius() * Math.cos(token);
double z = getRadius() * Math.sin(token);
UtilParticle.PlayParticleToAll(ParticleType.WITCH_MAGIC, getEntity().getLocation().add(x, 0.3, z), null, 0, 1, ViewDist.MAX);
UtilParticle.PlayParticleToAll(ParticleType.WITCH_MAGIC, _center.clone().add(x, 0.3, z), null, 0, 1, ViewDist.MAX);
}
for (Player player : UtilPlayer.getInRadius(getEntity().getLocation(), getRadius()).keySet())

View File

@ -85,7 +85,7 @@ public class NecromancerSmite extends BossAbility<NecromancerCreature, Skeleton>
else
{
_shot = true;
for (Player player : UtilPlayer.getInRadius(getEntity().getLocation(), 10).keySet())
for (Player player : UtilPlayer.getInRadius(getEntity().getLocation(), 15).keySet())
{
if (player.isDead() || !player.isValid() || !player.isOnline())
{

View File

@ -25,7 +25,7 @@ import com.google.common.collect.Lists;
public class NecromancerStrike extends BossAbility<NecromancerCreature, Skeleton>
{
private static final double MAX_RANGE = 10;
private static final double MAX_RANGE = 20;
private static final Integer MAX_TARGETS = 3;
private boolean _shot;
@ -124,13 +124,13 @@ public class NecromancerStrike extends BossAbility<NecromancerCreature, Skeleton
@Override
public int getCooldown()
{
return 3;
return 0;
}
@Override
public boolean canMove()
{
return false;
return true;
}
@Override

View File

@ -4,9 +4,9 @@ import java.util.concurrent.ConcurrentHashMap;
import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilParticle;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.common.util.UtilParticle.ParticleType;
import mineplex.core.common.util.UtilParticle.ViewDist;
import mineplex.core.common.util.UtilPlayer;
import mineplex.minecraft.game.core.boss.BossAbility;
import mineplex.minecraft.game.core.boss.necromancer.NecromancerBoss;
import mineplex.minecraft.game.core.boss.necromancer.NecromancerCreature;
@ -21,7 +21,6 @@ import org.bukkit.entity.Player;
import org.bukkit.entity.Skeleton;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.util.Vector;
public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Skeleton>
@ -31,6 +30,7 @@ public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Sk
private ConcurrentHashMap<WraithCreature, String> _wraiths = new ConcurrentHashMap<>();
private Location[] _spawns;
private int _ticks;
public NecromancerWraithSummon(NecromancerCreature creature)
{
@ -43,32 +43,7 @@ public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Sk
getEntity().getLocation().add(DISTANCE_FROM_NECROMANCER, 0, DISTANCE_FROM_NECROMANCER * -1),
getEntity().getLocation().add(DISTANCE_FROM_NECROMANCER * -1, 0, DISTANCE_FROM_NECROMANCER * -1)
};
if (WRAITH_AMOUNT > 0)
{
for (int i = 0; i < WRAITH_AMOUNT; i++)
{
int spawnIndex = i;
if (spawnIndex >= _spawns.length)
{
spawnIndex = spawnIndex % _spawns.length;
}
spawnWraith(_spawns[spawnIndex], i + 1);
}
for (Player player : UtilPlayer.getInRadius(getEntity().getLocation(), 80).keySet())
{
if (player.isDead() || !player.isValid() || !player.isOnline())
{
continue;
}
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR)
{
continue;
}
player.sendMessage(F.main(getBoss().getEvent().getName(), "You must slay all " + WRAITH_AMOUNT + " wraiths before continuing to fight the Necromancer!"));
}
}
Bukkit.broadcastMessage("Instantiated Wraith");
}
private String getNumberString(Integer number)
@ -126,7 +101,7 @@ public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Sk
@Override
public boolean hasFinished()
{
return !_wraiths.isEmpty();
return _wraiths.isEmpty() && _ticks > 40;
}
@Override
@ -137,11 +112,42 @@ public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Sk
wraith.remove();
}
_wraiths.clear();
_ticks = 41;
}
@Override
public void tick()
{
if (_ticks == 0)
{
if (WRAITH_AMOUNT > 0)
{
for (int i = 0; i < WRAITH_AMOUNT; i++)
{
int spawnIndex = i;
if (spawnIndex >= _spawns.length)
{
spawnIndex = spawnIndex % _spawns.length;
}
spawnWraith(_spawns[spawnIndex], i + 1);
}
for (Player player : UtilPlayer.getInRadius(getEntity().getLocation(), 80).keySet())
{
if (player.isDead() || !player.isValid() || !player.isOnline())
{
continue;
}
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR)
{
continue;
}
player.sendMessage(F.main(getBoss().getEvent().getName(), "You must slay all " + WRAITH_AMOUNT + " wraiths before continuing to fight the Necromancer!"));
}
}
}
_ticks++;
if (!hasFinished())
{
int ticks = 10;
@ -168,14 +174,12 @@ public class NecromancerWraithSummon extends BossAbility<NecromancerCreature, Sk
}
@EventHandler
public void onWraithDie(EntityDeathEvent event)
public void onWraithDie(WraithDeathEvent event)
{
for (WraithCreature wraith : _wraiths.keySet())
String number = _wraiths.remove(event.getWraith());
if (number != null)
{
if (wraith.getEntity().equals(event.getEntity()))
{
Bukkit.broadcastMessage(F.main(getBoss().getEvent().getName(), "The " + _wraiths.remove(wraith) + " wraith has been slain!"));
}
Bukkit.broadcastMessage(F.main(getBoss().getEvent().getName(), "The " + number + " wraith has been slain!"));
}
}

View File

@ -0,0 +1,33 @@
package mineplex.minecraft.game.core.boss.necromancer.abilities;
import mineplex.minecraft.game.core.boss.necromancer.minion.WraithCreature;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class WraithDeathEvent extends Event
{
private static final HandlerList handlers = new HandlerList();
private WraithCreature _wraith;
public WraithDeathEvent(WraithCreature wraith)
{
_wraith = wraith;
}
public WraithCreature getWraith()
{
return _wraith;
}
public HandlerList getHandlers()
{
return handlers;
}
public static HandlerList getHandlerList()
{
return handlers;
}
}

View File

@ -100,10 +100,7 @@ public class WraithCreature extends EventCreature<Zombie>
}
// Damage
event.AddMod(damager.getName(), "Mystical Darkness", 2, false);
// Condition
getEvent().getCondition().Factory().Poison("Mystical Darkness", damagee, damager, 5, 0, false, true, false);
event.AddMult(damager.getName(), "Mystical Darkness", 2, false);
}
@EventHandler
@ -136,7 +133,7 @@ public class WraithCreature extends EventCreature<Zombie>
UtilParticle.PlayParticleToAll(ParticleType.SMOKE, zombie.getLocation(), 0, 0, 0, 0, 5, ViewDist.MAX);
zombie.getWorld().playSound(zombie.getLocation(), Sound.ENDERMAN_TELEPORT, 1f, 2f);
zombie.teleport(zombie.getTarget().getLocation().add(UtilMath.random(0, 1.5), 0, UtilMath.random(0, 1.5)));
zombie.teleport(zombie.getTarget().getLocation().add(Math.random() + 1, 0, Math.random() + 1));
UtilParticle.PlayParticleToAll(ParticleType.SMOKE, zombie.getLocation(), 0, 0, 0, 0, 5, ViewDist.MAX);
zombie.getWorld().playSound(zombie.getLocation(), Sound.ENDERMAN_TELEPORT, 1f, 2f);
}