Added support for thrown spells. Projectiles with a spell attached.

This commit is contained in:
Sollace 2019-03-05 14:29:16 +02:00
parent 41923f82e1
commit 28e294803e
14 changed files with 247 additions and 71 deletions

View file

@ -1,7 +1,14 @@
package com.minelittlepony.unicopia.entity; package com.minelittlepony.unicopia.entity;
import com.minelittlepony.unicopia.item.ITossable; import com.minelittlepony.unicopia.network.EffectSync;
import com.minelittlepony.unicopia.spell.ICaster;
import com.minelittlepony.unicopia.spell.IMagicEffect;
import com.minelittlepony.unicopia.spell.SpellAffinity;
import com.minelittlepony.unicopia.spell.SpellRegistry;
import com.minelittlepony.unicopia.tossable.ITossable;
import com.minelittlepony.unicopia.tossable.ITossableItem;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile; import net.minecraft.entity.IProjectile;
import net.minecraft.entity.projectile.EntitySnowball; import net.minecraft.entity.projectile.EntitySnowball;
@ -16,13 +23,19 @@ import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
public class EntityProjectile extends EntitySnowball { public class EntityProjectile extends EntitySnowball implements IMagicals, ICaster<EntityLivingBase> {
private static final DataParameter<ItemStack> ITEM = EntityDataManager private static final DataParameter<ItemStack> ITEM = EntityDataManager
.createKey(EntityProjectile.class, DataSerializers.ITEM_STACK); .createKey(EntityProjectile.class, DataSerializers.ITEM_STACK);
private static final DataParameter<Float> DAMAGE = EntityDataManager private static final DataParameter<Float> DAMAGE = EntityDataManager
.createKey(EntityProjectile.class, DataSerializers.FLOAT); .createKey(EntityProjectile.class, DataSerializers.FLOAT);
private static final DataParameter<NBTTagCompound> EFFECT = EntityDataManager
.createKey(EntitySpell.class, DataSerializers.COMPOUND_TAG);
private final EffectSync<EntityLivingBase> effectDelegate = new EffectSync<>(this, EFFECT);
public EntityProjectile(World world) { public EntityProjectile(World world) {
super(world); super(world);
} }
@ -39,12 +52,57 @@ public class EntityProjectile extends EntitySnowball {
protected void entityInit() { protected void entityInit() {
getDataManager().register(ITEM, ItemStack.EMPTY); getDataManager().register(ITEM, ItemStack.EMPTY);
getDataManager().register(DAMAGE, (float)0); getDataManager().register(DAMAGE, (float)0);
getDataManager().register(EFFECT, new NBTTagCompound());
} }
public ItemStack getItem() { public ItemStack getItem() {
ItemStack stack = getDataManager().get(ITEM); ItemStack stack = getDataManager().get(ITEM);
return stack == null ? ItemStack.EMPTY : stack; return stack == null ? ItemStack.EMPTY : stack;
}
@Override
public Entity getEntity() {
return this;
}
@Override
public void setOwner(EntityLivingBase owner) {
thrower = owner;
}
@Override
public EntityLivingBase getOwner() {
return getThrower();
}
@Override
public int getCurrentLevel() {
return 1;
}
@Override
public void setCurrentLevel(int level) {
}
@Override
public SpellAffinity getAffinity() {
return hasEffect() ? SpellAffinity.NEUTRAL : getEffect().getAffinity();
}
@Override
public void setEffect(IMagicEffect effect) {
effectDelegate.set(effect);
}
@Override
public <T extends IMagicEffect> T getEffect(Class<T> type, boolean update) {
return effectDelegate.get(type, update);
}
@Override
public boolean hasEffect() {
return effectDelegate.has();
} }
public void setItem(ItemStack stack) { public void setItem(ItemStack stack) {
@ -71,6 +129,27 @@ public class EntityProjectile extends EntitySnowball {
} else { } else {
setItem(itemstack); setItem(itemstack);
} }
if (compound.hasKey("effect")) {
setEffect(SpellRegistry.instance().createEffectFromNBT(compound.getCompoundTag("effect")));
}
}
@Override
public void onUpdate() {
super.onUpdate();
if (hasEffect()) {
if (getEffect().getDead()) {
setDead();
} else {
getEffect().update(this);
}
if (world.isRemote) {
getEffect().render(this);
}
}
} }
@Override @Override
@ -93,6 +172,10 @@ public class EntityProjectile extends EntitySnowball {
if (!itemstack.isEmpty()) { if (!itemstack.isEmpty()) {
compound.setTag("Item", itemstack.writeToNBT(new NBTTagCompound())); compound.setTag("Item", itemstack.writeToNBT(new NBTTagCompound()));
} }
if (hasEffect()) {
compound.setTag("effect", SpellRegistry.instance().serializeEffectToNBT(getEffect()));
}
} }
@Override @Override
@ -101,8 +184,15 @@ public class EntityProjectile extends EntitySnowball {
if (result.typeOfHit == RayTraceResult.Type.BLOCK) { if (result.typeOfHit == RayTraceResult.Type.BLOCK) {
Item item = getItem().getItem(); Item item = getItem().getItem();
if (item instanceof ITossable) { if (item instanceof ITossableItem) {
((ITossable)item).onImpact(world, result.getBlockPos(), world.getBlockState(result.getBlockPos())); ((ITossableItem)item).onImpact(world, result.getBlockPos(), world.getBlockState(result.getBlockPos()));
}
if (hasEffect()) {
IMagicEffect effect = this.getEffect();
if (effect instanceof ITossable) {
((ITossable<?>)effect).onImpact(world, result.getBlockPos(), world.getBlockState(result.getBlockPos()));
}
} }
} }
@ -110,8 +200,6 @@ public class EntityProjectile extends EntitySnowball {
result.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, getThrower()), getThrowDamage()); result.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, getThrower()), getThrowDamage());
} }
if (!world.isRemote) { if (!world.isRemote) {
world.setEntityState(this, (byte)3); world.setEntityState(this, (byte)3);
setDead(); setDead();

View file

@ -154,14 +154,8 @@ public class ItemSpell extends Item implements ICastable {
@Override @Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) { public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) {
Entity target = VecHelper.getLookedAtEntity(player, 5);
ItemStack stack = player.getHeldItem(hand); ItemStack stack = player.getHeldItem(hand);
if (target == null) {
return new ActionResult<ItemStack>(EnumActionResult.PASS, stack);
}
if (!SpellRegistry.stackHasEnchantment(stack)) { if (!SpellRegistry.stackHasEnchantment(stack)) {
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack); return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
} }
@ -169,7 +163,7 @@ public class ItemSpell extends Item implements ICastable {
IUseAction effect = SpellRegistry.instance().getUseActionFrom(stack); IUseAction effect = SpellRegistry.instance().getUseActionFrom(stack);
if (effect != null) { if (effect != null) {
SpellCastResult result = effect.onUse(stack, getAffinity(stack), player, world, target); SpellCastResult result = effect.onUse(stack, getAffinity(stack), player, world, VecHelper.getLookedAtEntity(player, 5));
if (result != SpellCastResult.NONE) { if (result != SpellCastResult.NONE) {
if (result == SpellCastResult.PLACE && !player.capabilities.isCreativeMode) { if (result == SpellCastResult.PLACE && !player.capabilities.isCreativeMode) {

View file

@ -8,6 +8,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.minelittlepony.unicopia.Predicates; import com.minelittlepony.unicopia.Predicates;
import com.minelittlepony.unicopia.tossable.ITossableItem;
import com.minelittlepony.util.lang.ClientLocale; import com.minelittlepony.util.lang.ClientLocale;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@ -32,7 +33,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
public class ItemStaff extends ItemSword implements ITossable { public class ItemStaff extends ItemSword implements ITossableItem {
protected static final UUID ATTACK_REACH_MODIFIER = UUID.fromString("FA235E1C-4280-A865-B01B-CBAE9985ACA3"); protected static final UUID ATTACK_REACH_MODIFIER = UUID.fromString("FA235E1C-4280-A865-B01B-CBAE9985ACA3");

View file

@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.item;
import com.minelittlepony.unicopia.forgebullshit.IMultiItem; import com.minelittlepony.unicopia.forgebullshit.IMultiItem;
import com.minelittlepony.unicopia.player.PlayerSpeciesList; import com.minelittlepony.unicopia.player.PlayerSpeciesList;
import com.minelittlepony.unicopia.tossable.ITossableItem;
import net.minecraft.block.material.Material; import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs; import net.minecraft.creativetab.CreativeTabs;
@ -17,7 +19,7 @@ import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
public class ItemTomato extends ItemFood implements ITossable, IMultiItem { public class ItemTomato extends ItemFood implements ITossableItem, IMultiItem {
private final String name; private final String name;

View file

@ -0,0 +1,16 @@
package com.minelittlepony.unicopia.spell;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface IRangedEffect extends IMagicEffect {
void onImpact(World world, BlockPos pos, IBlockState state);
default SoundEvent getThrowSound(ItemStack stack) {
return SoundEvents.ENTITY_SNOWBALL_THROW;
}
}

View file

@ -0,0 +1,33 @@
package com.minelittlepony.unicopia.spell;
import com.minelittlepony.unicopia.entity.EntityProjectile;
import com.minelittlepony.unicopia.init.UItems;
import com.minelittlepony.unicopia.tossable.ITossable;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundCategory;
import net.minecraft.world.World;
public interface ITossedEffect extends IMagicEffect, ITossable<ICaster<?>> {
default void toss(ICaster<?> caster) {
World world = caster.getWorld();
Entity entity = caster.getOwner();
world.playSound(null, entity.posX, entity.posY, entity.posZ, getThrowSound(caster), SoundCategory.NEUTRAL, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F));
if (!world.isRemote) {
EntityProjectile projectile = new EntityProjectile(world, caster.getOwner());
projectile.setItem(new ItemStack(UItems.spell));
projectile.setThrowDamage(getThrowDamage(caster));
projectile.setOwner(caster.getOwner());
projectile.setEffect(this);
projectile.shoot(entity, entity.rotationPitch, entity.rotationYaw, 0, 1.5F, 1);
world.spawnEntity(projectile);
}
}
}

View file

@ -1,6 +1,6 @@
package com.minelittlepony.unicopia.spell; package com.minelittlepony.unicopia.spell;
import javax.annotation.Nonnull; import javax.annotation.Nullable;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
@ -43,5 +43,5 @@ public interface IUseAction {
* *
* @return ActionResult for the type of action to perform * @return ActionResult for the type of action to perform
*/ */
SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nonnull Entity hitEntity); SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nullable Entity hitEntity);
} }

View file

@ -2,13 +2,22 @@ package com.minelittlepony.unicopia.spell;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.minelittlepony.util.shape.Sphere; import com.minelittlepony.util.shape.Sphere;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class SpellAwkward extends AbstractSpell { public class SpellAwkward extends AbstractSpell implements ITossedEffect, IUseAction {
private final List<String> names = Lists.newArrayList(EnumParticleTypes.getParticleNames()); private final List<String> names = Lists.newArrayList(EnumParticleTypes.getParticleNames());
@ -60,4 +69,22 @@ public class SpellAwkward extends AbstractSpell {
default: return true; default: return true;
} }
} }
@Override
public void onImpact(World world, BlockPos pos, IBlockState state) {
// noop
}
@Override
public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) {
return SpellCastResult.PLACE;
}
@Override
public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nullable Entity hitEntity) {
CasterUtils.toCaster(player).ifPresent(this::toss);
return SpellCastResult.NONE;
}
} }

View file

@ -1,6 +1,6 @@
package com.minelittlepony.unicopia.spell; package com.minelittlepony.unicopia.spell;
import javax.annotation.Nonnull; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Predicates; import com.minelittlepony.unicopia.Predicates;
import com.minelittlepony.unicopia.entity.IMagicals; import com.minelittlepony.unicopia.entity.IMagicals;
@ -114,7 +114,11 @@ public class SpellFire extends AbstractSpell.RangedAreaSpell implements IUseActi
} }
@Override @Override
public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nonnull Entity hitEntity) { public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nullable Entity hitEntity) {
if (hitEntity == null) {
return SpellCastResult.NONE;
}
return applyEntitySingle(player, world, hitEntity) ? SpellCastResult.DEFAULT : SpellCastResult.NONE; return applyEntitySingle(player, world, hitEntity) ? SpellCastResult.DEFAULT : SpellCastResult.NONE;
} }

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.spell; package com.minelittlepony.unicopia.spell;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.init.UMaterials; import com.minelittlepony.unicopia.init.UMaterials;
import com.minelittlepony.util.MagicalDamageSource; import com.minelittlepony.util.MagicalDamageSource;
import com.minelittlepony.util.PosHelper; import com.minelittlepony.util.PosHelper;
@ -96,7 +98,7 @@ public class SpellIce extends AbstractSpell.RangedAreaSpell implements IUseActio
} }
@Override @Override
public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, Entity hitEntity) { public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nullable Entity hitEntity) {
if (hitEntity != null && applyEntitySingle(player, hitEntity)) { if (hitEntity != null && applyEntitySingle(player, hitEntity)) {
return SpellCastResult.DEFAULT; return SpellCastResult.DEFAULT;
} }

View file

@ -4,7 +4,6 @@ import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.entity.EntitySpell; import com.minelittlepony.unicopia.entity.EntitySpell;
@ -127,7 +126,7 @@ public class SpellPortal extends AbstractSpell.RangedAreaSpell implements IUseAc
} }
@Override @Override
public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nonnull Entity hitEntity) { public SpellCastResult onUse(ItemStack stack, SpellAffinity affinity, EntityPlayer player, World world, @Nullable Entity hitEntity) {
return SpellCastResult.NONE; return SpellCastResult.NONE;
} }

View file

@ -0,0 +1,35 @@
package com.minelittlepony.unicopia.tossable;
import net.minecraft.block.BlockDispenser;
import net.minecraft.dispenser.BehaviorDefaultDispenseItem;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
class DispenserBehaviour extends BehaviorDefaultDispenseItem {
@Override
public ItemStack dispenseStack(IBlockSource source, ItemStack stack) {
ITossableItem tossable = (ITossableItem)stack.getItem();
if (tossable.canBeThrown(stack)) {
return shootStack(source, stack);
}
return super.dispenseStack(source, stack);
}
public ItemStack shootStack(IBlockSource source, ItemStack stack) {
return ((ITossableItem)stack.getItem()).toss(source.getWorld(),
BlockDispenser.getDispensePosition(source),
(EnumFacing)source.getBlockState().getValue(BlockDispenser.FACING),
stack, getProjectileInaccuracy(), getProjectileVelocity());
}
protected float getProjectileInaccuracy() {
return 6.0F;
}
protected float getProjectileVelocity() {
return 1.1F;
}
}

View file

@ -0,0 +1,19 @@
package com.minelittlepony.unicopia.tossable;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface ITossable<T> {
void onImpact(World world, BlockPos pos, IBlockState state);
default SoundEvent getThrowSound(T stack) {
return SoundEvents.ENTITY_SNOWBALL_THROW;
}
default int getThrowDamage(T stack) {
return 0;
}
}

View file

@ -1,45 +1,29 @@
package com.minelittlepony.unicopia.item; package com.minelittlepony.unicopia.tossable;
import com.minelittlepony.unicopia.entity.EntityProjectile; import com.minelittlepony.unicopia.entity.EntityProjectile;
import net.minecraft.block.BlockDispenser; import net.minecraft.block.BlockDispenser;
import net.minecraft.block.state.IBlockState;
import net.minecraft.dispenser.BehaviorDefaultDispenseItem;
import net.minecraft.dispenser.IBehaviorDispenseItem; import net.minecraft.dispenser.IBehaviorDispenseItem;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.dispenser.IPosition; import net.minecraft.dispenser.IPosition;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatList; import net.minecraft.stats.StatList;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
public interface ITossable { public interface ITossableItem extends ITossable<ItemStack> {
IBehaviorDispenseItem dispenserBehavior = new ITossable.DispenserBehaviour(); IBehaviorDispenseItem dispenserBehavior = new DispenserBehaviour();
boolean canBeThrown(ItemStack stack); boolean canBeThrown(ItemStack stack);
void onImpact(World world, BlockPos pos, IBlockState state);
default Item setDispenseable() { default Item setDispenseable() {
BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject((Item)(Object)this, dispenserBehavior); BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject((Item)(Object)this, dispenserBehavior);
return (Item)(Object)this; return (Item)(Object)this;
} }
default SoundEvent getThrowSound(ItemStack stack) {
return SoundEvents.ENTITY_SNOWBALL_THROW;
}
default int getThrowDamage(ItemStack stack) {
return 0;
}
default void toss(World world, ItemStack itemstack, EntityPlayer player) { default void toss(World world, ItemStack itemstack, EntityPlayer player) {
if (!player.capabilities.isCreativeMode) { if (!player.capabilities.isCreativeMode) {
itemstack.shrink(1); itemstack.shrink(1);
@ -74,32 +58,4 @@ public interface ITossable {
return stack; return stack;
} }
class DispenserBehaviour extends BehaviorDefaultDispenseItem {
@Override
public ItemStack dispenseStack(IBlockSource source, ItemStack stack) {
ITossable tossable = (ITossable)stack.getItem();
if (tossable.canBeThrown(stack)) {
return shootStack(source, stack);
}
return super.dispenseStack(source, stack);
}
public ItemStack shootStack(IBlockSource source, ItemStack stack) {
return ((ITossable)stack.getItem()).toss(source.getWorld(),
BlockDispenser.getDispensePosition(source),
(EnumFacing)source.getBlockState().getValue(BlockDispenser.FACING),
stack, getProjectileInaccuracy(), getProjectileVelocity());
}
protected float getProjectileInaccuracy() {
return 6.0F;
}
protected float getProjectileVelocity() {
return 1.1F;
}
}
} }