Implement shapeshifting for changelings

This commit is contained in:
Sollace 2019-02-02 18:50:15 +02:00
parent 08aacefc26
commit c9354d7acb
13 changed files with 386 additions and 44 deletions

View file

@ -56,6 +56,10 @@ public class UClient {
return false;
}
public int getViewMode() {
return 0;
}
public void preInit(FMLPreInitializationEvent event) {}
public void init(FMLInitializationEvent event) {}

View file

@ -22,6 +22,7 @@ import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.FOVUpdateEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
@ -100,6 +101,21 @@ public class UnicopiaClient extends UClient {
return getPlayer().getGameProfile().getId().equals(player.getGameProfile().getId());
}
@Override
public int getViewMode() {
return Minecraft.getMinecraft().gameSettings.thirdPersonView;
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
public static void preEntityRender(RenderLivingEvent.Pre<?> event) {
if (event.getEntity() instanceof EntityPlayer) {
if (PlayerSpeciesList.instance().getPlayer((EntityPlayer)event.getEntity()).isInvisible()) {
event.setCanceled(true);
}
}
}
@SideOnly(Side.CLIENT)
@Override
public void preInit(FMLPreInitializationEvent event) {

View file

@ -28,11 +28,18 @@ public class EffectSync<T extends EntityLivingBase> {
NBTTagCompound comp = owned.getEntity().getDataManager().get(param);
if (comp == null || !comp.hasKey("effect_id")) {
effect = null;
if (effect != null) {
effect.setDead();
effect = null;
}
} else {
String id = comp.getString("effect_id");
if (effect == null || !effect.getName().contentEquals(id)) {
if (effect != null) {
effect.setDead();
effect = null;
}
effect = SpellRegistry.instance().createEffectFromNBT(comp);
} else if (owned.getEntity().world.isRemote) {
effect.readFromNBT(comp);
@ -43,6 +50,9 @@ public class EffectSync<T extends EntityLivingBase> {
}
public void set(IMagicEffect effect) {
if (this.effect != null && this.effect != effect) {
this.effect.setDead();
}
this.effect = effect;
if (effect == null) {

View file

@ -54,6 +54,10 @@ public interface IPlayer extends ICaster<EntityPlayer>, IRaceContainer<EntityPla
boolean stepOnCloud();
boolean isInvisible();
void setInvisible(boolean invisible);
void onFall(float distance, float damageMultiplier);
void beforeUpdate(EntityPlayer entity);

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.player;
import javax.annotation.Nullable;
import com.minelittlepony.jumpingcastle.api.Target;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.network.MsgPlayerAbility;
@ -15,12 +17,11 @@ class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable<EntityPlayer
private final IPlayer player;
private boolean abilityTriggered = false;
private int warmup = 0;
private int cooldown = 0;
@Nullable
private IPower<?> activeAbility = null;
public PlayerAbilityDelegate(IPlayer player) {
@ -28,23 +29,21 @@ class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable<EntityPlayer
}
boolean canSwitchStates() {
return abilityTriggered && cooldown <= 0;
return (warmup == 0 && cooldown == 0) || activeAbility == null;
}
@Override
public void tryUseAbility(IPower<?> power) {
public synchronized void tryUseAbility(IPower<?> power) {
if (canSwitchStates() || activeAbility != power) {
abilityTriggered = false;
activeAbility = power;
warmup = 0;
cooldown = power.getCooldownTime(player);
warmup = power.getWarmupTime(player);
cooldown = 0;
}
}
@Override
public void tryClearAbility() {
if (canSwitchStates() || activeAbility != null) {
abilityTriggered = false;
public synchronized void tryClearAbility() {
if (canSwitchStates()) {
activeAbility = null;
warmup = 0;
cooldown = 0;
@ -57,34 +56,28 @@ class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable<EntityPlayer
}
@Override
public void onUpdate(EntityPlayer entity) {
public synchronized void onUpdate(EntityPlayer entity) {
if (activeAbility != null && activeAbility.canUse(player.getPlayerSpecies())) {
if (!abilityTriggered) {
if (warmup < activeAbility.getWarmupTime(player)) {
activeAbility.preApply(player);
warmup++;
} else if (player.isClientPlayer()) {
if (activeAbility.canActivate(entity.getEntityWorld(), player)) {
abilityTriggered = activateAbility(entity);
if (!abilityTriggered) {
activeAbility = null;
cooldown = 0;
}
} else {
activeAbility = null;
cooldown = 0;
}
if (warmup > 0) {
warmup--;
activeAbility.preApply(player);
} else if (player.isClientPlayer()) {
if (activateAbility()) {
cooldown = activeAbility.getCooldownTime(player);
} else {
cooldown = 0;
}
} else if (cooldown > 0) {
activeAbility.postApply(player);
}
if (cooldown > 0 && activeAbility != null) {
cooldown--;
activeAbility.postApply(player);
}
}
}
@Override
public void writeToNBT(NBTTagCompound compound) {
compound.setBoolean("triggered", abilityTriggered);
compound.setInteger("warmup", warmup);
compound.setInteger("cooldown", cooldown);
@ -95,10 +88,9 @@ class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable<EntityPlayer
@Override
public void readFromNBT(NBTTagCompound compound) {
activeAbility = null;
abilityTriggered = compound.getBoolean("triggered");
warmup = compound.getInteger("warmup");
cooldown = compound.getInteger("cooldown");
activeAbility = null;
if (compound.hasKey("activeAbility")) {
PowersRegistry.instance().getPowerFromName(compound.getString("activeAbility")).ifPresent(p -> {
@ -107,11 +99,15 @@ class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable<EntityPlayer
}
}
protected boolean activateAbility(EntityPlayer entity) {
IData data = activeAbility.tryActivate(entity, entity.getEntityWorld());
protected boolean activateAbility() {
if (activeAbility == null || !activeAbility.canActivate(player.getWorld(), player)) {
return false;
}
IData data = activeAbility.tryActivate(player.getOwner(), player.getWorld());
if (data != null) {
Unicopia.channel.send(new MsgPlayerAbility(entity, activeAbility, data), Target.SERVER);
Unicopia.channel.send(new MsgPlayerAbility(player.getOwner(), activeAbility, data), Target.SERVER);
}
return data != null;

View file

@ -69,6 +69,8 @@ class PlayerCapabilities implements IPlayer {
private boolean dirty = false;
private boolean invisible = false;
PlayerCapabilities(EntityPlayer player) {
setOwner(player);
@ -128,6 +130,16 @@ class PlayerCapabilities implements IPlayer {
getOwner().getDataManager().set(ENERGY, Math.max(0, energy));
}
@Override
public boolean isInvisible() {
return invisible;
}
@Override
public void setInvisible(boolean invisible) {
this.invisible = invisible;
}
@Override
public SpellAffinity getAffinity() {
return SpellAffinity.NEUTRAL;
@ -195,16 +207,12 @@ class PlayerCapabilities implements IPlayer {
gravity.onUpdate(entity);
if (hasEffect()) {
if (!getPlayerSpecies().canCast()) {
setEffect(null);
} else {
if (entity.getEntityWorld().isRemote) {
getEffect().renderOnPerson(this);
}
if (entity.getEntityWorld().isRemote) {
getEffect().renderOnPerson(this);
}
if (!getEffect().updateOnPerson(this)) {
setEffect(null);
}
if (!getEffect().updateOnPerson(this)) {
setEffect(null);
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.power;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.input.IKeyBind;
import com.minelittlepony.unicopia.particle.Particles;
@ -99,6 +101,7 @@ public interface IPower<T extends IData> extends IKeyBind {
* @param w The player's world
* @return Data to be sent, or null if activation failed
*/
@Nullable
T tryActivate(EntityPlayer player, World w);
Class<T> getPackageType();

View file

@ -0,0 +1,68 @@
package com.minelittlepony.unicopia.power;
import javax.annotation.Nullable;
import org.lwjgl.input.Keyboard;
import com.minelittlepony.unicopia.UParticles;
import com.minelittlepony.unicopia.player.IPlayer;
import com.minelittlepony.unicopia.player.PlayerSpeciesList;
import com.minelittlepony.unicopia.power.data.Hit;
import com.minelittlepony.unicopia.spell.IMagicEffect;
import com.minelittlepony.unicopia.spell.SpellDisguise;
import com.minelittlepony.util.vector.VecHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.SoundCategory;
import net.minecraft.world.World;
public class PowerDisguise extends PowerFeed {
@Override
public String getKeyName() {
return "unicopia.power.diguise";
}
@Override
public int getKeyCode() {
return Keyboard.KEY_P;
}
@Nullable
@Override
public Hit tryActivate(EntityPlayer player, World w) {
return new Hit();
}
@Override
public void apply(EntityPlayer player, Hit data) {
Entity looked = VecHelper.getLookedAtEntity(player, 17);
IPlayer iplayer = PlayerSpeciesList.instance().getPlayer(player);
player.world.playSound(null, player.getPosition(), SoundEvents.E_PARROT_IM_POLAR_BEAR, SoundCategory.PLAYERS, 1.4F, 0.4F);
IMagicEffect effect = iplayer.getEffect();
if (effect instanceof SpellDisguise && !effect.getDead()) {
((SpellDisguise)effect).setDisguise(looked);
} else if (looked != null) {
iplayer.setEffect(new SpellDisguise().setDisguise(looked));
}
iplayer.sendCapabilities(true);
}
@Override
public void preApply(IPlayer player) {
player.addEnergy(2);
IPower.spawnParticles(UParticles.MAGIC_PARTICLE, player, 5);
}
@Override
public void postApply(IPlayer player) {
player.setEnergy(0);
IPower.spawnParticles(UParticles.MAGIC_PARTICLE, player, 5);
}
}

View file

@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.power;
import java.util.List;
import javax.annotation.Nullable;
import org.lwjgl.input.Keyboard;
import com.minelittlepony.unicopia.Race;
@ -51,6 +53,7 @@ public class PowerFeed implements IPower<Hit> {
return playerSpecies == Race.CHANGELING;
}
@Nullable
@Override
public Hit tryActivate(EntityPlayer player, World w) {
if (player.getHealth() < player.getMaxHealth() || player.canEat(false)) {

View file

@ -31,6 +31,7 @@ public class PowersRegistry {
registerPower(new PowerGrow());
registerPower(new PowerFeed());
registerPower(new PowerCarry());
registerPower(new PowerDisguise());
}
public boolean hasRegisteredPower(int keyCode) {

View file

@ -0,0 +1,227 @@
package com.minelittlepony.unicopia.spell;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.UClient;
import com.minelittlepony.unicopia.player.IPlayer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.nbt.NBTTagCompound;
public class SpellDisguise extends AbstractSpell {
@Nonnull
private String entityId = "";
@Nullable
private Entity entity;
@Nullable
private NBTTagCompound entityNbt;
@Override
public String getName() {
return "disguise";
}
@Override
public int getTint() {
return 0;
}
public SpellDisguise setDisguise(@Nullable Entity entity) {
this.entityNbt = null;
if (this.entity != null) {
this.entity.setDead();
}
this.entity = null;
this.entityId = "";
if (entity != null) {
this.entityId = EntityList.getKey(entity).toString();
this.entityNbt = entity.writeToNBT(new NBTTagCompound());
this.entityNbt.setString("id", entityId);
}
return this;
}
@Override
public boolean update(ICaster<?> source, int level) {
if (entity == null && entityNbt != null) {
entity = EntityList.createEntityFromNBT(entityNbt, source.getWorld());
if (entity != null && source.getWorld().isRemote) {
source.getWorld().spawnEntity(entity);
}
entityNbt = null;
}
EntityLivingBase owner = source.getOwner();
if (owner == null) {
return true;
}
if (entity != null) {
if (entity instanceof EntityLiving) {
EntityLiving l = (EntityLiving)entity;
l.setNoAI(true);
l.rotationYawHead = owner.rotationYawHead;
l.prevRotationYawHead = owner.prevRotationYawHead;
l.renderYawOffset = owner.renderYawOffset;
l.prevRenderYawOffset = owner.prevRenderYawOffset;
l.limbSwing = owner.limbSwing;
l.limbSwingAmount = owner.limbSwingAmount;
l.prevLimbSwingAmount = owner.prevLimbSwingAmount;
l.swingingHand = owner.swingingHand;
l.swingProgress = owner.swingProgress;
l.swingProgressInt = owner.swingProgressInt;
l.isSwingInProgress = owner.isSwingInProgress;
for (EntityEquipmentSlot i : EntityEquipmentSlot.values()) {
l.setItemStackToSlot(i, owner.getItemStackFromSlot(i));
}
if (l.world.rand.nextInt(1000) < l.livingSoundTime++) {
l.playLivingSound();
l.livingSoundTime = -l.getTalkInterval();
}
}
entity.copyLocationAndAnglesFrom(owner);
entity.setNoGravity(true);
entity.prevPosX = owner.prevPosX;
entity.prevPosY = owner.prevPosY;
entity.prevPosZ = owner.prevPosZ;
entity.motionX = owner.motionX;
entity.motionY = owner.motionY;
entity.motionZ = owner.motionZ;
entity.prevRotationPitch = owner.prevRotationPitch;
entity.prevRotationYaw = owner.prevRotationYaw;
entity.distanceWalkedOnStepModified = owner.distanceWalkedOnStepModified;
entity.distanceWalkedModified = owner.distanceWalkedModified;
entity.prevDistanceWalkedModified = owner.prevDistanceWalkedModified;
owner.height = entity.height;
entity.setSneaking(owner.isSneaking());
entity.setInvisible(false);
if (source instanceof IPlayer) {
((IPlayer) source).setInvisible(true);
}
owner.setInvisible(true);
if (owner instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer)owner;
player.eyeHeight = entity.getEyeHeight();
if (UClient.instance().isClientPlayer(player)) {
entity.setAlwaysRenderNameTag(false);
entity.setCustomNameTag("");
if (UClient.instance().getViewMode() == 0) {
entity.setInvisible(true);
entity.posY = -10;
}
} else {
entity.setAlwaysRenderNameTag(true);
entity.setCustomNameTag(player.getName());
}
}
if (!(source instanceof IPlayer) || ((IPlayer) source).getPlayerSpecies() == Race.CHANGELING) {
return true;
}
}
owner.setInvisible(false);
if (source instanceof IPlayer) {
((IPlayer) source).setInvisible(false);
}
return false;
}
@Override
public void setDead() {
super.setDead();
if (entity != null) {
entity.setDead();
entity = null;
}
}
@Override
public void render(ICaster<?> source, int level) {
}
@Override
public void writeToNBT(NBTTagCompound compound) {
compound.setString("entityId", entityId);
compound.setBoolean("dead", getDead());
if (entityNbt != null) {
compound.setTag("entity", entityNbt);
} else if (entity != null) {
NBTTagCompound entityTag = new NBTTagCompound();
entity.writeToNBT(entityTag);
entityTag.setString("id", entityId);
compound.setTag("entity", entityTag);
}
}
@Override
public void readFromNBT(NBTTagCompound compound) {
String newId = compound.getString("entityId");
if (!newId.contentEquals(entityId)) {
entityNbt = null;
if (entity != null) {
entity.setDead();
entity = null;
}
}
if (compound.hasKey("entity")) {
entityId = newId;
entityNbt = compound.getCompoundTag("entity");
if (entity != null) {
entity.readFromNBT(entityNbt);
}
}
}
}

View file

@ -32,6 +32,7 @@ public class SpellRegistry {
registerSpell(SpellIce::new);
registerSpell(SpellPortal::new);
registerSpell(SpellVortex::new);
registerSpell(SpellDisguise::new);
}
public IMagicEffect getSpellFromName(String name) {

View file

@ -38,6 +38,7 @@ public class VecHelper {
/**
* Gets the entity the player is currently looking at, or null.
*/
@Nullable
public static Entity getLookedAtEntity(EntityLivingBase e, int reach) {
RayTraceResult objectMouseOver = getObjectMouseOver(e, reach, 1);