195 lines
8.5 KiB
Java
195 lines
8.5 KiB
Java
package electroblob.wizardry.potion;
|
|
|
|
import electroblob.wizardry.Wizardry;
|
|
import electroblob.wizardry.item.ItemArtefact;
|
|
import electroblob.wizardry.registry.Spells;
|
|
import electroblob.wizardry.registry.WizardryItems;
|
|
import electroblob.wizardry.registry.WizardryPotions;
|
|
import electroblob.wizardry.spell.SlowTime;
|
|
import electroblob.wizardry.spell.Spell;
|
|
import electroblob.wizardry.util.EntityUtils;
|
|
import electroblob.wizardry.util.ParticleBuilder;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.EntityLivingBase;
|
|
import net.minecraft.entity.IProjectile;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.entity.projectile.EntityArrow;
|
|
import net.minecraft.util.ResourceLocation;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
|
import net.minecraftforge.event.entity.living.PotionEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
|
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
@Mod.EventBusSubscriber
|
|
public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
|
|
|
|
// FIXME: Minecarts with entities in them (and, I suspect, any other ridden entities) go crazy when time-slowed
|
|
|
|
public static final String NBT_KEY = "time_slowed";
|
|
|
|
public PotionSlowTime(boolean isBadEffect, int liquidColour){
|
|
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/slow_time.png"));
|
|
this.setPotionName("potion." + Wizardry.MODID + ":slow_time");
|
|
}
|
|
|
|
private static double getEffectRadius(){
|
|
return Spells.slow_time.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
|
}
|
|
|
|
public static void unblockNearbyEntities(EntityLivingBase host){
|
|
List<Entity> targetsBeyondRange = EntityUtils.getEntitiesWithinRadius(getEffectRadius() + 3, host.posX, host.posY, host.posZ, host.world, Entity.class);
|
|
targetsBeyondRange.forEach(e -> e.updateBlocked = false);
|
|
}
|
|
|
|
// Not done in performEffect because it's client-inconsistent; it only fires on the client of the player with the
|
|
// potion effect, and doesn't fire on the client at all for non-players
|
|
private static void performEffectConsistent(EntityLivingBase host, int strength){
|
|
|
|
boolean stopTime = host instanceof EntityPlayer && ItemArtefact.isArtefactActive((EntityPlayer)host, WizardryItems.charm_stop_time);
|
|
|
|
int interval = strength * 4 + 6;
|
|
|
|
// Mark all entities within range
|
|
List<Entity> targetsInRange = EntityUtils.getEntitiesWithinRadius(getEffectRadius(), host.posX, host.posY, host.posZ, host.world, Entity.class);
|
|
targetsInRange.remove(host);
|
|
// Other entities with the slow time effect are unaffected
|
|
targetsInRange.removeIf(t -> t instanceof EntityLivingBase && ((EntityLivingBase)t).isPotionActive(WizardryPotions.slow_time));
|
|
if(!Wizardry.settings.slowTimeAffectsPlayers) targetsInRange.removeIf(t -> t instanceof EntityPlayer);
|
|
targetsInRange.removeIf(t -> t instanceof EntityArrow && t.isEntityInsideOpaqueBlock());
|
|
|
|
for(Entity entity : targetsInRange){
|
|
|
|
entity.getEntityData().setBoolean(NBT_KEY, true);
|
|
|
|
// If time is stopped, block all updates; otherwise block all updates except every [interval] ticks
|
|
entity.updateBlocked = stopTime || host.ticksExisted % interval != 0;
|
|
|
|
if(!stopTime && entity.world.isRemote){
|
|
|
|
// Client-side movement interpolation (smoothing)
|
|
|
|
if(entity.onGround) entity.motionY = 0; // Don't ask. It just works.
|
|
|
|
// if(entity instanceof EntityLivingBase){
|
|
// ((EntityLivingBase)entity).prevLimbSwingAmount = ((EntityLivingBase)entity).limbSwingAmount;
|
|
// ((EntityLivingBase)entity).swingProgress = ((EntityLivingBase)entity).prevSwingProgress;
|
|
// ((EntityLivingBase)entity).renderYawOffset = ((EntityLivingBase)entity).prevRenderYawOffset;
|
|
// ((EntityLivingBase)entity).rotationYawHead = ((EntityLivingBase)entity).prevRotationYawHead;
|
|
// }
|
|
|
|
if(entity.updateBlocked){
|
|
// When the update is blocked, the entity is moved 1/interval times the distance it would have moved
|
|
double x = entity.posX + entity.motionX * 1d / (double)interval;
|
|
double y = entity.posY + entity.motionY * 1d / (double)interval;
|
|
double z = entity.posZ + entity.motionZ * 1d / (double)interval;
|
|
|
|
entity.prevPosX = entity.posX;
|
|
entity.prevPosY = entity.posY;
|
|
entity.prevPosZ = entity.posZ;
|
|
|
|
entity.posX = x;
|
|
entity.posY = y;
|
|
entity.posZ = z;
|
|
|
|
}else{
|
|
// When the update is not blocked, the entity is moved BACK 1-1/interval times the distance it moved
|
|
// This is because the entity already covered most of that distance when its update was blocked
|
|
entity.posX += entity.motionX * 1d / (double)interval;
|
|
entity.posY += entity.motionY * 1d / (double)interval;
|
|
entity.posZ += entity.motionZ * 1d / (double)interval;
|
|
|
|
double x = entity.posX - entity.motionX * 1d / (double)interval;
|
|
double y = entity.posY - entity.motionY * 1d / (double)interval;
|
|
double z = entity.posZ - entity.motionZ * 1d / (double)interval;
|
|
|
|
entity.prevPosX = x;
|
|
entity.prevPosY = y;
|
|
entity.prevPosZ = z;
|
|
}
|
|
}
|
|
|
|
if(entity.world.isRemote && host.ticksExisted % 2 == 0){
|
|
int lifetime = 15;
|
|
double dx = (entity.world.rand.nextDouble() - 0.5D) * 2 * (double)entity.width;
|
|
double dy = (entity.world.rand.nextDouble() - 0.5D) * 2 * (double)entity.width;
|
|
double dz = (entity.world.rand.nextDouble() - 0.5D) * 2 * (double)entity.width;
|
|
double x = entity.posX + dx;
|
|
double y = entity instanceof IProjectile ? entity.posY + dy : entity.posY + entity.height/2 + dy;
|
|
double z = entity.posZ + dz;
|
|
ParticleBuilder.create(ParticleBuilder.Type.DUST)
|
|
.pos(x, y, z)
|
|
.vel(-dx/lifetime, -dy/lifetime, -dz/lifetime)
|
|
.clr(0x5be3bb).time(15).spawn(entity.world);
|
|
}
|
|
}
|
|
|
|
// Un-mark all entities that have just left range
|
|
List<Entity> targetsBeyondRange = EntityUtils.getEntitiesWithinRadius(getEffectRadius() + 3, host.posX, host.posY, host.posZ, host.world, Entity.class);
|
|
targetsBeyondRange.removeAll(targetsInRange);
|
|
targetsBeyondRange.forEach(e -> e.updateBlocked = false);
|
|
|
|
}
|
|
|
|
/**
|
|
* Goes through every entity in the given world and does the following:<br>
|
|
* 1. Checks if they have the slow time NBT tag<br>
|
|
* 2. If so, scans the area nearby for players or NPCs with the slow time effect<br>
|
|
* 3. If none are found, removes the slow time NBT tag and unblocks the entity's updates
|
|
*/
|
|
public static void cleanUpEntities(World world){
|
|
// Had trouble with accessing loadedTileEntityList from tick events causing random CMEs so I'm making a
|
|
// copy of this too just in case
|
|
List<Entity> loadedEntityList = new ArrayList<>(world.loadedEntityList);
|
|
|
|
for(Entity entity : loadedEntityList){
|
|
if(entity.getEntityData().getBoolean(NBT_KEY)){
|
|
// Currently only players can cast slow time, but you could apply the effect to NPCs with commands
|
|
List<EntityLivingBase> nearby = EntityUtils.getLivingWithinRadius(getEffectRadius(), entity.posX, entity.posY, entity.posZ, entity.world);
|
|
if(nearby.stream().noneMatch(e -> e.isPotionActive(WizardryPotions.slow_time))){
|
|
entity.getEntityData().removeTag(NBT_KEY);
|
|
entity.updateBlocked = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
|
|
|
EntityLivingBase entity = event.getEntityLiving();
|
|
|
|
if(entity.isPotionActive(WizardryPotions.slow_time)){
|
|
performEffectConsistent(entity, entity.getActivePotionEffect(WizardryPotions.slow_time).getAmplifier());
|
|
}
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onPotionAddedEvent(PotionEvent.PotionAddedEvent event){
|
|
if(event.getEntity().world.isRemote && event.getPotionEffect().getPotion() == WizardryPotions.slow_time
|
|
&& event.getEntity() instanceof EntityPlayer){
|
|
Wizardry.proxy.loadShader((EntityPlayer)event.getEntity(), SlowTime.SHADER);
|
|
Wizardry.proxy.playBlinkEffect((EntityPlayer)event.getEntity());
|
|
}
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void tick(TickEvent.WorldTickEvent event){
|
|
if(!event.world.isRemote && event.phase == TickEvent.Phase.END) cleanUpEntities(event.world);
|
|
}
|
|
|
|
// We still need this as well as tick events because the player hasn't moved anywhere, they just logged out
|
|
// In fact, it won't really matter since the tick event fixes it on login anyway, but if the mod is uninstalled or
|
|
// something else weird happens...
|
|
@SubscribeEvent
|
|
public static void onPlayerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event){
|
|
if(event.player.updateBlocked) event.player.updateBlocked = false;
|
|
}
|
|
|
|
}
|