Gems slowly lose their power over time; added a gem to transfer power over long distances

This commit is contained in:
Sollace 2018-09-26 17:53:12 +02:00
parent 736e9a3f11
commit 55b050a992
13 changed files with 352 additions and 53 deletions

View file

@ -7,7 +7,6 @@ import com.minelittlepony.unicopia.UItems;
import com.minelittlepony.unicopia.item.ICastable;
import com.minelittlepony.unicopia.network.EffectSync;
import com.minelittlepony.unicopia.spell.ICaster;
import com.minelittlepony.unicopia.spell.ILevelled;
import com.minelittlepony.unicopia.spell.IMagicEffect;
import com.minelittlepony.unicopia.spell.SpellRegistry;
@ -31,7 +30,7 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class EntitySpell extends EntityLiving implements IMagicals, ICaster<EntityLivingBase>, ILevelled {
public class EntitySpell extends EntityLiving implements IMagicals, ICaster<EntityLivingBase> {
private EntityLivingBase owner = null;
@ -57,14 +56,20 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
enablePersistence();
}
@Override
public boolean isInRangeToRenderDist(double distance) {
if (getCurrentLevel() > 0) {
distance /= getCurrentLevel();
}
return super.isInRangeToRenderDist(distance);
}
@Override
public void setEffect(IMagicEffect effect) {
effectDelegate.set(effect);
}
@Override
public IMagicEffect getEffect() {
return effectDelegate.get();
}
@ -162,6 +167,19 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
super.onUpdate();
}
}
if (getCurrentLevel() > 0 && !world.isRemote && world.rand.nextInt(200) == 0) {
addLevels(-1);
if (getCurrentLevel() <= 0) {
setDead();
}
}
if (overLevelCap()) {
if (world.rand.nextInt(10) == 0) {
spawnExplosionParticle();
}
}
}
@Override
@ -175,6 +193,7 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
//super.updateFallState(y, onGround = this.onGround = true, state, pos);
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount) {
if (!world.isRemote) {
setDead();
@ -200,6 +219,7 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
}
}
@Override
public void setDead() {
if (hasEffect()) {
getEffect().setDead();
@ -207,6 +227,7 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
super.setDead();
}
@Override
public EnumActionResult applyPlayerInteraction(EntityPlayer player, Vec3d vec, EnumHand hand) {
if (Predicates.MAGI.test(player)) {
ItemStack currentItem = player.getHeldItem(EnumHand.MAIN_HAND);
@ -240,7 +261,7 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
addLevels(1);
if (!world.isRemote) {
if ((rand.nextFloat() * getCurrentLevel()) > 10 || overLevelCap()) {
if (overLevelCap() || (rand.nextFloat() * getCurrentLevel()) > 10) {
world.createExplosion(this, posX, posY, posZ, getCurrentLevel()/2, true);
setDead();
return false;
@ -257,7 +278,7 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
@Override
public int getMaxLevel() {
return getEffect().getMaxLevel();
return hasEffect() ? getEffect().getMaxLevel() : 0;
}
@Override
@ -267,7 +288,13 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster<Enti
@Override
public void setCurrentLevel(int level) {
dataManager.set(LEVEL, Math.min(level, 0));
level = Math.max(level, 0);
if (hasEffect()) {
getEffect().setCurrentLevel(level);
level = getEffect().getCurrentLevel();
}
dataManager.set(LEVEL, level);
}
public boolean overLevelCap() {

View file

@ -87,6 +87,14 @@ public class ModelGem extends ModelBase {
floatOffset += spell.hoverStart;
floatOffset *= 180 / (float)Math.PI;
GlStateManager.pushMatrix();
if (spell.overLevelCap()) {
GlStateManager.translate(Math.sin(stutter) / 5, 0, Math.cos(stutter) / 5);
GlStateManager.rotate((float)Math.sin(stutter), 0, 1, 0);
}
GlStateManager.rotate(floatOffset, 0, 1, 0);
body.render(scale);
@ -99,10 +107,35 @@ public class ModelGem extends ModelBase {
Color.glColor(SpellRegistry.instance().getSpellTint(spell.getEffect().getName()), 1);
GlStateManager.scale(1.2F, 1.2F, 1.2F);
GlStateManager.translate(0, -0.2F, 0);
body.render(scale);
int tiers = Math.min(spell.getCurrentLevel(), 5);
for (int i = 0; i <= tiers; i++) {
float grow = (1 + i) * 0.2F;
GlStateManager.scale(1 + grow, 1 + grow, 1 + grow);
GlStateManager.translate(0, -grow, 0);
renderOverlay(grow, scale);
if (i == 5) {
GlStateManager.pushMatrix();
GlStateManager.rotate(-floatOffset * 0.9F, 0, 1, 0);
GlStateManager.translate(0.6F, 0.8F, 0);
GlStateManager.scale(0.4F, 0.4F, 0.4F);
renderOverlay(grow, scale);
GlStateManager.popMatrix();
}
}
GlStateManager.popMatrix();
for (int i = spell.getCurrentLevel(); i > 0; i--) {
GlStateManager.pushMatrix();
GlStateManager.rotate(floatOffset / i, 0, 1, 0);
GlStateManager.translate(0.6F, 0, 0);
renderOverlay(0.6F, scale);
GlStateManager.popMatrix();
}
setLightingConditionsBrightness(entity.getBrightnessForRender());
@ -112,6 +145,13 @@ public class ModelGem extends ModelBase {
GlStateManager.popMatrix();
}
protected void renderOverlay(float grow, float scale) {
body.render(scale);
}
private void setLightingConditionsBrightness(int brightness) {
int texX = brightness % 0x10000;
int texY = brightness / 0x10000;

View file

@ -33,7 +33,7 @@ public class EffectSync<T extends EntityLivingBase> {
String id = comp.getString("effect_id");
if (effect == null || id != effect.getName()) {
effect = SpellRegistry.instance().createEffectFromNBT(comp);
} else {
} else if (owned.getEntity().world.isRemote) {
effect.readFromNBT(comp);
}
}

View file

@ -144,10 +144,10 @@ class PlayerCapabilities implements IPlayer, ICaster<EntityPlayer> {
setEffect(null);
} else {
if (entity.getEntityWorld().isRemote) { // && entity.getEntityWorld().getWorldTime() % 10 == 0
getEffect().render(this);
getEffect().renderOnPerson(this);
}
if (!getEffect().update(this)) {
if (!getEffect().updateOnPerson(this)) {
setEffect(null);
}
}
@ -249,4 +249,13 @@ class PlayerCapabilities implements IPlayer, ICaster<EntityPlayer> {
public EntityPlayer getOwner() {
return entity;
}
@Override
public int getCurrentLevel() {
return 0;
}
@Override
public void setCurrentLevel(int level) {
}
}

View file

@ -2,16 +2,16 @@ package com.minelittlepony.unicopia.power;
import org.lwjgl.input.Keyboard;
import com.google.gson.annotations.Expose;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.player.IPlayer;
import com.minelittlepony.unicopia.player.PlayerSpeciesList;
import com.minelittlepony.unicopia.power.data.Hit;
import com.minelittlepony.unicopia.spell.SpellShield;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
public class PowerMagic implements IPower<PowerMagic.Magic> {
public class PowerMagic implements IPower<Hit> {
@Override
public String getKeyName() {
@ -39,23 +39,23 @@ public class PowerMagic implements IPower<PowerMagic.Magic> {
}
@Override
public Magic tryActivate(EntityPlayer player, World w) {
return new Magic(0);
public Hit tryActivate(EntityPlayer player, World w) {
return new Hit();
}
@Override
public Class<Magic> getPackageType() {
return Magic.class;
public Class<Hit> getPackageType() {
return Hit.class;
}
@Override
public void apply(EntityPlayer player, Magic data) {
public void apply(EntityPlayer player, Hit data) {
IPlayer prop = PlayerSpeciesList.instance().getPlayer(player);
if (prop.getEffect() instanceof SpellShield) {
prop.setEffect(null);
} else {
prop.setEffect(new SpellShield(data.type));
prop.setEffect(new SpellShield());
}
}
@ -68,14 +68,4 @@ public class PowerMagic implements IPower<PowerMagic.Magic> {
public void postApply(IPlayer player) {
}
class Magic implements IData {
@Expose
public int type;
public Magic(int strength) {
type = strength;
}
}
}

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.render;
import com.minelittlepony.unicopia.entity.EntitySpell;
import com.minelittlepony.unicopia.model.ModelGem;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
@ -15,14 +16,22 @@ public class RenderGem extends RenderLiving<EntitySpell> {
super(rendermanagerIn, new ModelGem(), 0);
}
@Override
protected ResourceLocation getEntityTexture(EntitySpell entity) {
return gem;
}
@Override
public boolean shouldRender(EntitySpell livingEntity, ICamera camera, double camX, double camY, double camZ) {
return true;
}
@Override
protected float getDeathMaxRotation(EntitySpell entity) {
return 0;
}
@Override
protected boolean canRenderName(EntitySpell targetEntity) {
return super.canRenderName(targetEntity) && (targetEntity.getAlwaysRenderNameTagForRender()
|| targetEntity.hasCustomName() && targetEntity == renderManager.pointedEntity);

View file

@ -7,7 +7,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface ICaster<E extends EntityLivingBase> extends IOwned<E> {
public interface ICaster<E extends EntityLivingBase> extends IOwned<E>, ILevelled {
void setEffect(IMagicEffect effect);
IMagicEffect getEffect();

View file

@ -25,7 +25,9 @@ public interface IMagicEffect extends InbtSerialisable, ILevelled {
* @param source The entity we are currently attached to.
* @return true to keep alive
*/
boolean update(ICaster<?> caster);
default boolean updateOnPerson(ICaster<?> caster) {
return update(caster, getCurrentLevel());
}
/**
* Called every tick when attached to a gem.
@ -41,8 +43,8 @@ public interface IMagicEffect extends InbtSerialisable, ILevelled {
*
* @param source The entity we are currently attached to.
*/
default void render(ICaster<?> source) {
default void renderOnPerson(ICaster<?> source) {
render(source, getCurrentLevel());
}
/**
@ -52,9 +54,7 @@ public interface IMagicEffect extends InbtSerialisable, ILevelled {
* @param source The entity we are attached to.
* @param level Current spell level
*/
default void render(ICaster<?> source, int level) {
}
void render(ICaster<?> source, int level);
/**
* Return true to allow the gem update and move.

View file

@ -0,0 +1,141 @@
package com.minelittlepony.unicopia.spell;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.particle.Particles;
import com.minelittlepony.unicopia.entity.EntitySpell;
import com.minelittlepony.util.shape.IShape;
import com.minelittlepony.util.shape.Line;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public class SpellCharge extends AbstractSpell {
private int desiredLevel = 0;
boolean searching = true;
private UUID targettedEntityId;
private EntitySpell targettedEntity;
private static final AxisAlignedBB searchArea = new AxisAlignedBB(-15, -15, -15, 15, 15, 15);
@Override
public String getName() {
return "charge";
}
@Override
public void render(ICaster<?> source, int level) {
if (source.getWorld().rand.nextInt(4 + level * 4) == 0) {
EntitySpell target = getTarget(source);
if (target != null) {
Vec3d start = source.getEntity().getPositionVector();
Vec3d end = target.getPositionVector();
IShape line = new Line(start, end);
Random rand = source.getWorld().rand;
for (int i = 0; i < line.getVolumeOfSpawnableSpace(); i++) {
Vec3d pos = line.computePoint(rand);
Particles.instance().spawnParticle(Unicopia.MAGIC_PARTICLE, false,
pos.x + start.x, pos.y + start.y, pos.z + start.z,
0, 0, 0);
}
}
}
}
protected boolean canTargetEntity(Entity e) {
return e instanceof EntitySpell && ((EntitySpell)e).hasEffect();
}
protected void setTarget(EntitySpell e) {
searching = false;
targettedEntity = e;
targettedEntityId = e.getUniqueID();
}
protected EntitySpell getTarget(ICaster<?> source) {
if (targettedEntity == null && targettedEntityId != null) {
source.getWorld().getEntities(EntitySpell.class, e -> e.getUniqueID().equals(targettedEntityId)).stream().findFirst().ifPresent(this::setTarget);
}
if (targettedEntity != null && targettedEntity.isDead) {
targettedEntity = null;
targettedEntityId = null;
searching = true;
}
return targettedEntity;
}
@Override
public boolean update(ICaster<?> source, int level) {
if (searching) {
BlockPos origin = source.getOrigin();
List<Entity> list = source.getWorld().getEntitiesInAABBexcluding(source.getEntity(),
searchArea.offset(origin), this::canTargetEntity).stream().sorted((a, b) -> {
return (int)(a.getDistanceSq(origin) - b.getDistanceSq(origin));
}).collect(Collectors.toList());
if (list.size() > 0) {
setTarget((EntitySpell)list.get(0));
}
} else {
EntitySpell target = getTarget(source);
if (target != null && !target.overLevelCap() && level > 0) {
source.addLevels(-1);
target.addLevels(1);
}
}
return !isDead;
}
@Override
public int getMaxLevel() {
return 2;
}
@Override
public int getCurrentLevel() {
return desiredLevel;
}
@Override
public void setCurrentLevel(int level) {
desiredLevel = level;
}
@Override
public void writeToNBT(NBTTagCompound compound) {
if (targettedEntityId != null) {
compound.setUniqueId("target", targettedEntityId);
}
compound.setInteger("level", desiredLevel);
}
@Override
public void readFromNBT(NBTTagCompound compound) {
if (compound.hasKey("target")) {
targettedEntityId = compound.getUniqueId("target");
}
desiredLevel = compound.getInteger("level");
}
}

View file

@ -24,6 +24,7 @@ public class SpellRegistry {
private SpellRegistry() {
registerSpell("shield", 0xffff00, SpellShield::new);
registerSpell("charge", 0x0000ff, SpellCharge::new);
}
public IMagicEffect getSpellFromName(String name) {

View file

@ -25,13 +25,6 @@ public class SpellShield extends AbstractSpell {
private int strength = 0;
public SpellShield() {
}
public SpellShield(int type) {
setCurrentLevel(type);
}
@Override
public int getCurrentLevel() {
return strength;
@ -49,19 +42,12 @@ public class SpellShield extends AbstractSpell {
@Override
public int getMaxLevel() {
return -1;
}
@Override
public void render(ICaster<?> source) {
spawnParticles(source, 4 + (strength * 2));
return 17;
}
@Override
public void render(ICaster<?> source, int level) {
if (source.getWorld().rand.nextInt(4 + level * 4) == 0) {
spawnParticles(source, 4 + (level * 2));
}
spawnParticles(source, 4 + (level * 2));
}
protected void spawnParticles(ICaster<?> source, int strength) {
@ -82,7 +68,7 @@ public class SpellShield extends AbstractSpell {
}
@Override
public boolean update(ICaster<?> source) {
public boolean updateOnPerson(ICaster<?> source) {
update(source, strength);
if (source.getEntity().getEntityWorld().getWorldTime() % 50 == 0) {

View file

@ -0,0 +1,94 @@
package com.minelittlepony.util.shape;
import java.util.Random;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
/**
* A lonely Line. The simplest form of shape.
*
*/
public class Line implements IShape {
double len;
double dX;
double dY;
double dZ;
double sX;
double sY;
double sZ;
private float yaw = 0;
private float pitch = 0;
/**
* Creates a line with a given length, starting point, and gradient represented
* by another point.
*
* @param length Length of this line
* @param startX Offset X from origin
* @param startY Offset Y from origin
* @param startZ Offset Z from origin
* @param deltaX Change in X
* @param deltaY Change in Y
* @param deltaZ Change in Z
*/
public Line(double length, double startX, double startY, double startZ, double deltaX, double deltaY, double deltaZ) {
len = length;
dX = deltaX;
dY = deltaY;
dZ = deltaZ;
sX = startX;
sY = startY;
sZ = startZ;
}
public Line(Vec3d start, Vec3d end) {
Vec3d lenV = end.subtract(start);
len = lenV.length();
sX = start.x;
sY = start.y;
sZ = start.z;
dX = lenV.x / len;
dY = lenV.y / len;
dZ = lenV.z / len;
}
public double getVolumeOfSpawnableSpace() {
return len;
}
public double getXOffset() {
return sX;
}
public double getYOffset() {
return sY;
}
public double getZOffset() {
return sZ;
}
public Vec3d computePoint(Random rand) {
double distance = MathHelper.nextDouble(rand, 0, len);
return (new Vec3d(distance * dX, distance * dY, distance * dZ)).rotateYaw(yaw).rotatePitch(pitch);
}
public Line setRotation(float u, float v) {
yaw = u;
pitch = v;
return this;
}
public boolean isPointInside(Vec3d point) {
point = point.rotateYaw(-yaw).rotatePitch(-pitch);
return point.x/dX == point.y/dY && point.x/dX == point.z/dZ;
}
}

View file

@ -21,6 +21,8 @@ item.gem.name=Gem
item.corrupted_gem.name=Fractured Gem
item.gem.shield.name=Gem of Repulsion
item.corrupted_gem.shield.name=Corrupt Gem of Repulsion
item.gem.charge.name=Channeling Gem
item.corrupted_gem.charge.name=Gem of Draining
item.gem.fire.name=Gem of Flame
item.corrupted_gem.fire.name=Corrupt Gem of Burning
item.gem.inferno.name=Gem of Inferno