That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** A <b>curse</b> is a permanent potion effect, which is displayed in the inventory with a special background and
|
||||
* no timer. It also allows for longer potion effect names by wrapping them onto two lines. */
|
||||
public class Curse extends PotionMagicEffect {
|
||||
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation(Wizardry.MODID, "textures/gui/curse_background.png");
|
||||
|
||||
public Curse(boolean isBadEffect, int liquidColour, ResourceLocation texture){
|
||||
super(isBadEffect, liquidColour, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInvText(PotionEffect effect){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getCurativeItems(){
|
||||
return new ArrayList<>(); // Cannot be cured!
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc){
|
||||
|
||||
mc.renderEngine.bindTexture(BACKGROUND);
|
||||
electroblob.wizardry.client.DrawingUtils.drawTexturedRect(x, y, 0, 0, 140, 32, 256, 256);
|
||||
|
||||
super.renderInventoryEffect(x, y, effect, mc);
|
||||
|
||||
String name = net.minecraft.client.resources.I18n.format(this.getName());
|
||||
|
||||
// Amplifier 0 (which would be I) is not rendered and the tooltips only go up to X (amplifier 9)
|
||||
// The vanilla implementation uses elseifs and only goes up to 4... how lazy.
|
||||
if(effect.getAmplifier() > 0 && effect.getAmplifier() < 10){
|
||||
name = name + " " + net.minecraft.client.resources.I18n.format("enchantment.level." + (effect.getAmplifier() + 1));
|
||||
}
|
||||
|
||||
List<String> lines = mc.fontRenderer.listFormattedStringToWidth(name, 100);
|
||||
|
||||
int i=0;
|
||||
for(String line : lines){
|
||||
int h = lines.size() == 1 ? 5 : i * (mc.fontRenderer.FONT_HEIGHT + 1);
|
||||
mc.fontRenderer.drawStringWithShadow(line, (float)(x + 10 + 18), (float)(y + 6 + h), 0xbf00ee);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha){
|
||||
|
||||
net.minecraft.client.renderer.GlStateManager.color(1, 1, 1, 1);
|
||||
mc.renderEngine.bindTexture(BACKGROUND);
|
||||
electroblob.wizardry.client.DrawingUtils.drawTexturedRect(x, y, 141, 0, 24, 24, 256, 256);
|
||||
super.renderHUDEffect(x, y, effect, mc, alpha);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.util.FoodStats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class CurseEnfeeblement extends Curse {
|
||||
|
||||
// Yay more reflection
|
||||
private static final Field foodTimer;
|
||||
|
||||
static {
|
||||
foodTimer = ObfuscationReflectionHelper.findField(FoodStats.class, "field_75123_d");
|
||||
foodTimer.setAccessible(true);
|
||||
}
|
||||
|
||||
public CurseEnfeeblement(boolean isBadEffect, int liquiidColour){
|
||||
super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_curse_of_enfeeblement.png"));
|
||||
// This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet.
|
||||
this.setPotionName("potion." + Wizardry.MODID + ":curse_of_enfeeblement");
|
||||
this.registerPotionAttributeModifier(SharedMonsterAttributes.MAX_HEALTH,
|
||||
"2e8c378e-3d51-4ba1-b02c-591b5d968a05", -0.2, 1);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
|
||||
// Players are the only entities with natural regeneration
|
||||
// This can't be done in performEffect as that method only gets called every 20 ticks or so
|
||||
// Don't bother trying to prevent it unless the player is full enough
|
||||
if(event.player.isPotionActive(WizardryPotions.curse_of_enfeeblement) && event.player.getFoodStats().getFoodLevel() > 17){
|
||||
try{
|
||||
// Constantly setting this to zero prevents natural regeneration
|
||||
foodTimer.set(event.player.getFoodStats(), 0);
|
||||
}catch(IllegalAccessException e){
|
||||
Wizardry.logger.error("Error setting player food timer: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class CurseUndeath extends Curse {
|
||||
|
||||
public CurseUndeath(boolean isBadEffect, int liquiidColour){
|
||||
super(isBadEffect, liquiidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_curse_of_undeath.png"));
|
||||
// This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet.
|
||||
this.setPotionName("potion." + Wizardry.MODID + ":curse_of_undeath");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady(int duration, int amplifier){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performEffect(EntityLivingBase entitylivingbase, int strength){
|
||||
|
||||
// Adapted from EntityZombie
|
||||
if(entitylivingbase.world.isDaytime() && !entitylivingbase.world.isRemote){
|
||||
|
||||
float f = entitylivingbase.getBrightness();
|
||||
|
||||
if(f > 0.5F && entitylivingbase.world.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F
|
||||
&& entitylivingbase.world.canSeeSky(new BlockPos(entitylivingbase.posX,
|
||||
entitylivingbase.posY + (double)entitylivingbase.getEyeHeight(), entitylivingbase.posZ))){
|
||||
|
||||
boolean flag = true;
|
||||
ItemStack itemstack = entitylivingbase.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
|
||||
|
||||
if(!itemstack.isEmpty()){
|
||||
if(itemstack.isItemStackDamageable()){
|
||||
|
||||
itemstack.setItemDamage(itemstack.getItemDamage() + entitylivingbase.world.rand.nextInt(2));
|
||||
|
||||
if(itemstack.getItemDamage() >= itemstack.getMaxDamage()){
|
||||
entitylivingbase.renderBrokenItemStack(itemstack);
|
||||
entitylivingbase.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if(flag){
|
||||
entitylivingbase.setFire(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.potion.PotionUtils;
|
||||
import net.minecraft.world.World;
|
||||
@@ -10,16 +9,23 @@ import net.minecraftforge.event.entity.living.PotionColorCalculationEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles.
|
||||
* To hide the vanilla 'swirly' particles, set the potion's liquid colour to 0 (black). By default, potions that implement
|
||||
* this interface no longer mix their colour with other potions.
|
||||
* Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles.<br>
|
||||
* <br>
|
||||
* To hide the vanilla 'swirly' particles, set the potion's liquid colour to 0 (black). By default, potions that
|
||||
* implement this interface do not mix their colour with other potions.<br>
|
||||
* <br>
|
||||
* Potions that implement this interface also implement {@link ISyncedPotion} since any custom particles require syncing
|
||||
* to disappear correctly when the effect ends; if syncing is not required, override
|
||||
* {@link ISyncedPotion#shouldSync(EntityLivingBase)} to return false.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public interface ICustomPotionParticles {
|
||||
public interface ICustomPotionParticles extends ISyncedPotion {
|
||||
|
||||
/**
|
||||
* Called from the event handler to spawn a <b>single</b> custom potion particle. To get an instance of
|
||||
@@ -65,4 +71,5 @@ public interface ICustomPotionParticles {
|
||||
p -> !(p instanceof ICustomPotionParticles && !((ICustomPotionParticles)p).shouldMixColour()))
|
||||
.collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.network.play.server.SPacketEntityEffect;
|
||||
import net.minecraft.network.play.server.SPacketRemoveEntityEffect;
|
||||
import net.minecraftforge.event.entity.living.PotionEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/**
|
||||
* Interface for potion effects that need syncing to ensure client and server side are consistent. Simply implement
|
||||
* this interface and the potion will be synced automatically.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public interface ISyncedPotion {
|
||||
|
||||
/** The distance from an entity with this effect within which players will receive potion update packets. */
|
||||
double SYNC_RADIUS = 64;
|
||||
|
||||
/** Returns true if this potion should sync with nearby clients when added to / removed from an entity and on
|
||||
* expiry, false if not. The host entity is provided in case syncing is entity-dependent. Defaults to true. */
|
||||
default boolean shouldSync(EntityLivingBase host){
|
||||
return true;
|
||||
}
|
||||
|
||||
// The following event handlers fix the inconsistencies caused by clients not syncing correctly
|
||||
// These packets are only sent for players with potion effects in vanilla, and only to that player's client
|
||||
|
||||
// This one is only actually necessary if the effect gets added via a server-side method e.g. commands
|
||||
// Unfortunately there's no way of checking that, so we'll just have to live with the extra packets
|
||||
@SubscribeEvent
|
||||
public static void onPotionAddedEvent(PotionEvent.PotionAddedEvent event){
|
||||
|
||||
if(event.getPotionEffect().getPotion() instanceof ISyncedPotion
|
||||
&& ((ISyncedPotion)event.getPotionEffect().getPotion()).shouldSync(event.getEntityLiving())){
|
||||
|
||||
if(!event.getEntityLiving().world.isRemote){
|
||||
event.getEntityLiving().world.playerEntities.stream()
|
||||
.filter(p -> p.getDistanceSq(event.getEntityLiving()) < SYNC_RADIUS * SYNC_RADIUS)
|
||||
// Apparently unchecked casting in a lambda expression doesn't generate a warning. Who knew?
|
||||
// (We know this cast is safe though)
|
||||
.forEach(p -> ((EntityPlayerMP)p).connection.sendPacket(new SPacketEntityEffect(
|
||||
event.getEntity().getEntityId(), event.getPotionEffect())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionExpiryEvent(PotionEvent.PotionExpiryEvent event){
|
||||
|
||||
if(event.getPotionEffect().getPotion() instanceof ISyncedPotion
|
||||
&& ((ISyncedPotion)event.getPotionEffect().getPotion()).shouldSync(event.getEntityLiving())){
|
||||
|
||||
if(!event.getEntityLiving().world.isRemote){
|
||||
event.getEntityLiving().world.playerEntities.stream()
|
||||
.filter(p -> p.getDistanceSq(event.getEntityLiving()) < SYNC_RADIUS * SYNC_RADIUS)
|
||||
.forEach(p -> ((EntityPlayerMP)p).connection.sendPacket(new SPacketRemoveEntityEffect(
|
||||
event.getEntity().getEntityId(), event.getPotionEffect().getPotion())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionRemoveEvent(PotionEvent.PotionRemoveEvent event){
|
||||
|
||||
if(event.getPotionEffect().getPotion() instanceof ISyncedPotion
|
||||
&& ((ISyncedPotion)event.getPotionEffect().getPotion()).shouldSync(event.getEntityLiving())){
|
||||
|
||||
if(!event.getEntityLiving().world.isRemote){
|
||||
event.getEntityLiving().world.playerEntities.stream()
|
||||
.filter(p -> p.getDistanceSq(event.getEntityLiving()) < SYNC_RADIUS * SYNC_RADIUS)
|
||||
.forEach(p -> ((EntityPlayerMP)p).connection.sendPacket(new SPacketRemoveEntityEffect(
|
||||
event.getEntity().getEntityId(), event.getPotionEffect().getPotion())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class PotionContainment extends PotionMagicEffect {
|
||||
|
||||
public static final String ENTITY_TAG = "containmentPos";
|
||||
|
||||
public PotionContainment(boolean isBadEffect, int liquidColour){
|
||||
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_containment.png"));
|
||||
this.setPotionName("potion." + Wizardry.MODID + ":containment");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady(int duration, int amplifier){
|
||||
return true; // Execute the effect every tick
|
||||
}
|
||||
|
||||
public static float getContainmentDistance(int effectStrength){
|
||||
return 15 - effectStrength * 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performEffect(EntityLivingBase target, int strength){
|
||||
|
||||
float maxDistance = getContainmentDistance(strength);
|
||||
|
||||
// Initialise the containment position to the entity's position if it wasn't set already
|
||||
if(!target.getEntityData().hasKey(ENTITY_TAG)){
|
||||
target.getEntityData().setTag(ENTITY_TAG, NBTUtil.createPosTag(new BlockPos(target.getPositionVector().subtract(0.5, 0.5, 0.5))));
|
||||
}
|
||||
|
||||
Vec3d origin = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(target.getEntityData().getCompoundTag(ENTITY_TAG)));
|
||||
|
||||
double x = target.posX, y = target.posY, z = target.posZ;
|
||||
|
||||
// Containment fields are cubes so we're dealing with each axis separately
|
||||
if(target.getEntityBoundingBox().maxX > origin.x + maxDistance) x = origin.x + maxDistance - target.width/2;
|
||||
if(target.getEntityBoundingBox().minX < origin.x - maxDistance) x = origin.x - maxDistance + target.width/2;
|
||||
|
||||
if(target.getEntityBoundingBox().maxY > origin.y + maxDistance) y = origin.y + maxDistance - target.height;
|
||||
if(target.getEntityBoundingBox().minY < origin.y - maxDistance) y = origin.y - maxDistance;
|
||||
|
||||
if(target.getEntityBoundingBox().maxZ > origin.z + maxDistance) z = origin.z + maxDistance - target.width/2;
|
||||
if(target.getEntityBoundingBox().minZ < origin.z - maxDistance) z = origin.z - maxDistance + target.width/2;
|
||||
|
||||
if(x != target.posX || y != target.posY || z != target.posZ){
|
||||
|
||||
// if(target.world.isRemote){
|
||||
//
|
||||
// if(x != target.posX){
|
||||
// for(int i = 0; i < 20; i++){
|
||||
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
|
||||
// x,
|
||||
// target.getEntityBoundingBox().minY + target.height * target.world.rand.nextFloat(),
|
||||
// target.posZ + target.width * (target.world.rand.nextFloat() - 0.5f))
|
||||
// .face(EnumFacing.EAST).clr(0.8f, 0.9f, 1).spawn(target.world);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(y != target.posY){
|
||||
// for(int i = 0; i < 20; i++){
|
||||
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
|
||||
// target.posX + target.width * (target.world.rand.nextFloat() - 0.5f),
|
||||
// y,
|
||||
// target.posZ + target.width * (target.world.rand.nextFloat() - 0.5f))
|
||||
// .face(EnumFacing.UP).clr(0.8f, 0.9f, 1).spawn(target.world);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(z != target.posZ){
|
||||
// for(int i = 0; i < 20; i++){
|
||||
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
|
||||
// target.posX + target.width * (target.world.rand.nextFloat() - 0.5f),
|
||||
// target.getEntityBoundingBox().minY + target.height * target.world.rand.nextFloat(),
|
||||
// z)
|
||||
// .face(EnumFacing.SOUTH).clr(0.8f, 0.9f, 1).spawn(target.world);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
WizardryUtilities.undoGravity(target);
|
||||
target.addVelocity(0.35 * Math.signum(x - target.posX), 0.35 * Math.signum(y - target.posY), 0.35 * Math.signum(z - target.posZ));
|
||||
target.setPositionAndUpdate(x, y, z);
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
//
|
||||
// target.world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
|
||||
// WizardrySounds.SPELLS, 0.3f, 1f, false);
|
||||
|
||||
}
|
||||
|
||||
// Need to do this here because it's the only way to hook into potion ending both client- and server-side
|
||||
if(target.getActivePotionEffect(this).getDuration() <= 1) target.getEntityData().removeTag(ENTITY_TAG);
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
if(event.getEntityLiving().getEntityData().hasKey(ENTITY_TAG)
|
||||
&& !event.getEntityLiving().isPotionActive(WizardryPotions.containment)){
|
||||
event.getEntityLiving().getEntityData().removeTag(ENTITY_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.entity.construct.EntityDecay;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class PotionDecay extends PotionMagicEffect {
|
||||
@@ -40,8 +35,8 @@ public class PotionDecay extends PotionMagicEffect {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performEffect(EntityLivingBase target, int strength){
|
||||
target.attackEntityFrom(DamageSource.WITHER, 1);
|
||||
public void performEffect(EntityLivingBase host, int strength){
|
||||
host.attackEntityFrom(DamageSource.WITHER, 1);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class PotionFrost extends PotionMagicEffect implements ICustomPotionParticles {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.enchantment.EnchantmentFrostWalker;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class PotionFrostStep extends PotionMagicEffect implements ICustomPotionParticles {
|
||||
|
||||
private static final Field prevBlockPos = ObfuscationReflectionHelper.findField(EntityLivingBase.class, "field_184620_bC");
|
||||
|
||||
public PotionFrostStep(boolean isBadEffect, int liquidColour){
|
||||
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_frost_step.png"));
|
||||
this.setPotionName("potion." + Wizardry.MODID + ":frost_step");
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean isReady(int duration, int amplifier){
|
||||
// return true; // Execute the effect every tick
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void spawnCustomParticle(World world, double x, double y, double z){
|
||||
ParticleBuilder.create(Type.SNOW).pos(x, y, z).time(15 + world.rand.nextInt(5)).spawn(world);
|
||||
}
|
||||
|
||||
// Use LivingUpdateEvent instead of performEffect because it gets called before the actual frost walker processing
|
||||
// performEffect is called afterwards, at which point prevBlockPos has already been set to the current position
|
||||
// regardless of whether the player is wearing frost walker boots or not
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
EntityLivingBase host = event.getEntityLiving();
|
||||
|
||||
if(host.isPotionActive(WizardryPotions.frost_step)){
|
||||
// Mimics the behaviour of the frost walker enchantment itself
|
||||
if(!host.world.isRemote){
|
||||
|
||||
BlockPos currentPos = new BlockPos(host);
|
||||
|
||||
try{
|
||||
|
||||
if(!currentPos.equals(prevBlockPos.get(host))){
|
||||
|
||||
prevBlockPos.set(host, currentPos);
|
||||
|
||||
int strength = host.getActivePotionEffect(WizardryPotions.frost_step).getAmplifier();
|
||||
|
||||
EnchantmentFrostWalker.freezeNearby(host, host.world, currentPos, strength);
|
||||
|
||||
if(host instanceof EntityPlayer && ItemArtefact.isArtefactActive((EntityPlayer)host, WizardryItems.charm_lava_walking)){
|
||||
freezeNearbyLava(host, host.world, currentPos, strength);
|
||||
}
|
||||
}
|
||||
|
||||
}catch(IllegalAccessException e){
|
||||
Wizardry.logger.error("Error accessing living entity previous block pos:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Copied from {@link EnchantmentFrostWalker#freezeNearby(EntityLivingBase, World, BlockPos, int)} and modified
|
||||
* to turn lava to obsidian crust blocks instead. */
|
||||
private static void freezeNearbyLava(EntityLivingBase living, World world, BlockPos pos, int level){
|
||||
|
||||
if(living.onGround){
|
||||
|
||||
float f = (float)Math.min(16, 2 + level);
|
||||
BlockPos.MutableBlockPos pos1 = new BlockPos.MutableBlockPos(0, 0, 0);
|
||||
|
||||
for(BlockPos.MutableBlockPos pos2 : BlockPos.getAllInBoxMutable(pos.add((double)(-f), -1.0D, (double)(-f)), pos.add((double)f, -1.0D, (double)f))){
|
||||
|
||||
if(pos2.distanceSqToCenter(living.posX, living.posY, living.posZ) <= (double)(f * f)){
|
||||
|
||||
pos1.setPos(pos2.getX(), pos2.getY() + 1, pos2.getZ());
|
||||
IBlockState state1 = world.getBlockState(pos1);
|
||||
|
||||
if(state1.getMaterial() == Material.AIR){
|
||||
|
||||
IBlockState state2 = world.getBlockState(pos2);
|
||||
|
||||
if(state2.getMaterial() == Material.LAVA && (state2.getBlock() == Blocks.LAVA || state2.getBlock() == Blocks.FLOWING_LAVA) && state2.getValue(BlockLiquid.LEVEL) == 0 && world.mayPlace(WizardryBlocks.obsidian_crust, pos2, false, EnumFacing.DOWN, null)){
|
||||
world.setBlockState(pos2, WizardryBlocks.obsidian_crust.getDefaultState());
|
||||
world.scheduleUpdate(pos2.toImmutable(), WizardryBlocks.obsidian_crust, MathHelper.getInt(living.getRNG(), 60, 120));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
@@ -42,7 +41,7 @@ public class PotionMagicEffect extends Potion {
|
||||
@SideOnly(Side.CLIENT)
|
||||
protected void drawIcon(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc){
|
||||
mc.renderEngine.bindTexture(texture);
|
||||
DrawingUtils.drawTexturedRect(x, y, 0, 0, 18, 18, 18, 18);
|
||||
electroblob.wizardry.client.DrawingUtils.drawTexturedRect(x, y, 0, 0, 18, 18, 18, 18);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketEndSlowTime;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
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.ParticleBuilder;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
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.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 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 PotionSlowTime(boolean isBadEffect, int liquidColour){
|
||||
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_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 = WizardryUtilities.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 = WizardryUtilities.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){
|
||||
|
||||
// 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 = WizardryUtilities.getEntitiesWithinRadius(getEffectRadius() + 3, host.posX, host.posY, host.posZ, host.world, Entity.class);
|
||||
targetsBeyondRange.removeAll(targetsInRange);
|
||||
targetsBeyondRange.forEach(e -> e.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() == net.minecraft.client.Minecraft.getMinecraft().player){
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SlowTime.SHADER);
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionExpiryEvent(PotionEvent.PotionExpiryEvent event){
|
||||
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){
|
||||
unblockNearbyEntities(event.getEntityLiving());
|
||||
if(!event.getEntity().world.isRemote){
|
||||
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionRemoveEvent(PotionEvent.PotionRemoveEvent event){
|
||||
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){
|
||||
unblockNearbyEntities(event.getEntityLiving());
|
||||
if(!event.getEntity().world.isRemote){
|
||||
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user