Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.10] More event hooks and morph "healing" fix #64

Merged
merged 3 commits into from
Aug 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/main/java/mchorse/metamorph/api/MorphAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static boolean morph(EntityPlayer player, AbstractMorph morph, boolean fo
return false;
}

MorphEvent event = new MorphEvent(player, morph, force);
MorphEvent.Pre event = new MorphEvent.Pre(player, morph, force);

if (MinecraftForge.EVENT_BUS.post(event))
{
Expand All @@ -63,6 +63,10 @@ public static boolean morph(EntityPlayer player, AbstractMorph morph, boolean fo
Dispatcher.sendTo(new PacketMorph(morph), (EntityPlayerMP) player);
Dispatcher.updateTrackers(player, new PacketMorphPlayer(player.getEntityId(), morph));
}

if (morphed) {
MinecraftForge.EVENT_BUS.post(new MorphEvent.Post(player, event.morph, force));
}

return morphed;
}
Expand All @@ -80,19 +84,24 @@ public static boolean acquire(EntityPlayer player, AbstractMorph morph)
return false;
}

AcquireMorphEvent event = new AcquireMorphEvent(player, morph);
AcquireMorphEvent.Pre event = new AcquireMorphEvent.Pre(player, morph);

if (MinecraftForge.EVENT_BUS.post(event))
{
return false;
}

boolean acquired = Morphing.get(player).acquireMorph(event.morph);

if (!player.worldObj.isRemote && acquired)
{
Dispatcher.sendTo(new PacketAcquireMorph(event.morph), (EntityPlayerMP) player);
}

if (acquired)
{
MinecraftForge.EVENT_BUS.post(new AcquireMorphEvent.Post(player, event.morph));
}

return acquired;
}
Expand Down
27 changes: 26 additions & 1 deletion src/main/java/mchorse/metamorph/api/MorphHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;

import mchorse.metamorph.Metamorph;
import mchorse.metamorph.api.events.SpawnGhostEvent;
import mchorse.metamorph.api.morphs.AbstractMorph;
import mchorse.metamorph.capabilities.morphing.IMorphing;
import mchorse.metamorph.capabilities.morphing.Morphing;
Expand All @@ -13,13 +14,15 @@
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.entity.living.LivingAttackEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
import net.minecraftforge.fml.relauncher.Side;

/**
* Server event handler
Expand Down Expand Up @@ -57,6 +60,19 @@ public void onPlayerTick(PlayerTickEvent event)
IMorphing capability = Morphing.get(player);

this.runFutureTasks(player);

// A sanity check to prevent "healing" health when morphing to and from a mob with essentially zero health
// We have to do it every tick because you never know when another mod could change the max health
if (capability != null)
{
// If the current health ratio makes sense, store that ratio in the capability
float maxHealth = player.getMaxHealth();
if (maxHealth > IMorphing.REASONABLE_HEALTH_VALUE) {
float healthRatio = player.getHealth() / maxHealth;
capability.setLastHealthRatio(healthRatio);
}

}

if (capability == null || !capability.isMorphed())
{
Expand Down Expand Up @@ -133,10 +149,19 @@ public void onPlayerKillEntity(LivingDeathEvent event)

if (!Metamorph.proxy.config.prevent_ghosts || !capability.acquiredMorph(morph))
{
EntityMorph morphEntity = new EntityMorph(player.worldObj, player.getUniqueID(), morph);
SpawnGhostEvent spawnGhostEvent = new SpawnGhostEvent.Pre(player, morph);
if (MinecraftForge.EVENT_BUS.post(spawnGhostEvent) || spawnGhostEvent.morph == null)
{
return;
}
morph = spawnGhostEvent.morph;

EntityMorph morphEntity = new EntityMorph(player.worldObj, player.getUniqueID(), morph);

morphEntity.setPositionAndRotation(target.posX, target.posY + target.height / 2, target.posZ, target.rotationYaw, target.rotationPitch);
player.worldObj.spawnEntityInWorld(morphEntity);

MinecraftForge.EVENT_BUS.post(new SpawnGhostEvent.Post(player, morph));
}
}

Expand Down
20 changes: 18 additions & 2 deletions src/main/java/mchorse/metamorph/api/events/AcquireMorphEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Acquire morph event
*
* This event is fired when a player is about to acquire a morph. This is not
* {@link AcquireMorphEvent.Pre} is fired when a player is about to acquire a morph. This is not
* necessarily means that player already has this morph. Use {@link #hasMorph()}
* method to figure out whether player already has given morph.
*
Expand All @@ -18,8 +18,9 @@
*
* If you cancel this event, player won't acquire a morph, however, if player
* already has this morph, it will be completely useless.
*
* {@link AcquireMorphEvent.Post} is fired after a player successfully acquires a new morph.
*/
@Cancelable
public class AcquireMorphEvent extends Event
{
public EntityPlayer player;
Expand All @@ -38,4 +39,19 @@ public boolean hasMorph()
{
return Morphing.get(this.player).acquiredMorph(this.morph);
}

@Cancelable
public static class Pre extends AcquireMorphEvent
{
public Pre(EntityPlayer player, AbstractMorph morph) {
super(player, morph);
}
}

public static class Post extends AcquireMorphEvent
{
public Post(EntityPlayer player, AbstractMorph morph) {
super(player, morph);
}
}
}
20 changes: 18 additions & 2 deletions src/main/java/mchorse/metamorph/api/events/MorphEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/**
* Morph event
*
* This event occurs when player gets morphed or demorphed. If player gets
* {@link MorphEvent.Pre} occurs when player gets morphed or demorphed. If player gets
* demorphed then {@link #morph} is null. Check for player's worldObj property
* to get on which side this event is triggered.
*
Expand All @@ -19,8 +19,9 @@
* You can also modify {@link #force} parameter which is responsible for
* forcing morphing (if not forced, player will morph only in case if he has
* acquired morph like given).
*
* {@link MorphEvent.Post} is fired after a player successfully morphs or demorphs.
*/
@Cancelable
public class MorphEvent extends Event
{
public EntityPlayer player;
Expand All @@ -41,4 +42,19 @@ public boolean isDemorphing()
{
return this.morph == null;
}

@Cancelable
public static class Pre extends MorphEvent
{
public Pre(EntityPlayer player, AbstractMorph morph, boolean force) {
super(player, morph, force);
}
}

public static class Post extends MorphEvent
{
public Post(EntityPlayer player, AbstractMorph morph, boolean force) {
super(player, morph, force);
}
}
}
52 changes: 52 additions & 0 deletions src/main/java/mchorse/metamorph/api/events/SpawnGhostEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package mchorse.metamorph.api.events;

import mchorse.metamorph.api.morphs.AbstractMorph;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;

/**
* Morph spawn event
*
* {@link SpawnGhostEvent.Pre} occurs when a player kills a monster and a ghost is
* about to spawn from it. The event will not occur if the config option
* prevent_kill_acquire is set to true, nor will it occur if the config option
* prevent_ghosts is set to true while the player already has the morph.
*
* {@link #player} is the player responsible for killing the monster which spawned
* the morph.
*
* {@link #morph} is an instance of (@link #AbstractMorph} which represents the
* monster that was just killed. If you change this, a different morph ghost will
* spawn. If you set this to null, no morph will spawn.
*
* Canceling the event will prevent the ghost from spawning.
*
* {@link SpawnGhostEvent.Post} is fired after the ghost successfully spawns.
*/
public class SpawnGhostEvent extends Event
{
public EntityPlayer player;
public AbstractMorph morph;

public SpawnGhostEvent(EntityPlayer player, AbstractMorph morph)
{
this.player = player;
this.morph = morph;
}

@Cancelable
public static class Pre extends SpawnGhostEvent
{
public Pre(EntityPlayer player, AbstractMorph morph) {
super(player, morph);
}
}

public static class Post extends SpawnGhostEvent
{
public Post(EntityPlayer player, AbstractMorph morph) {
super(player, morph);
}
}
}
30 changes: 27 additions & 3 deletions src/main/java/mchorse/metamorph/api/morphs/AbstractMorph.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import mchorse.metamorph.api.abilities.IAction;
import mchorse.metamorph.api.abilities.IAttackAbility;
import mchorse.metamorph.capabilities.morphing.IMorphing;
import mchorse.metamorph.capabilities.morphing.Morphing;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
Expand Down Expand Up @@ -205,11 +206,34 @@ protected void setHealth(EntityLivingBase target, int health)
{
return;
}

float ratio = target.getHealth() / target.getMaxHealth();
float proportionalHealth = Math.round(health * ratio);

float maxHealth = target.getMaxHealth();
float currentHealth = target.getHealth();
float ratio = currentHealth / maxHealth;

// A sanity check to prevent "healing" health when morphing to and from a mob with essentially zero health
if (target instanceof EntityPlayer)
{
IMorphing capability = Morphing.get((EntityPlayer)target);
if (capability != null)
{
// Check if a health ratio makes sense for the old health value
if (maxHealth > IMorphing.REASONABLE_HEALTH_VALUE)
{
// If it makes sense, store that ratio in the capability
capability.setLastHealthRatio(ratio);
}
else if (health > IMorphing.REASONABLE_HEALTH_VALUE)
{
// If it doesn't make sense, BUT the new max health makes sense, retrieve the ratio from the capability and use that instead
ratio = capability.getLastHealthRatio();
}
}
}

this.setMaxHealth(target, health);
// We need to retrieve the max health of the target after modifiers are applied to get a sensible value
float proportionalHealth = Math.round(target.getMaxHealth() * ratio);
target.setHealth(proportionalHealth <= 0 ? 1 : proportionalHealth);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/
public interface IMorphing
{
public static final float REASONABLE_HEALTH_VALUE = Float.MIN_VALUE*100;

/**
* Add a morph
*/
Expand Down Expand Up @@ -80,4 +82,14 @@ public interface IMorphing
* Copy data from other morph
*/
public void copy(IMorphing morphing, EntityPlayer player);

/**
* Get the last recorded finite health fraction of the player
*/
public float getLastHealthRatio();

/**
* Determines what the player's new health will be if the player morphs out of a morph with very low health
*/
public void setLastHealthRatio(float lastHealthRatio);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public class Morphing implements IMorphing
* Current used morph
*/
private AbstractMorph morph;

/**
* (health / max health) is stored here when the new max health ends up
* very close to zero, and retrieved when the fraction is meaningful again
*/
private float lastHealthRatio;

public static IMorphing get(EntityPlayer player)
{
Expand Down Expand Up @@ -203,4 +209,16 @@ public void copy(IMorphing morphing, EntityPlayer player)
this.setCurrentMorph(morphing.getCurrentMorph(), player, true);
this.setFavorites(morphing.getFavorites());
}

@Override
public float getLastHealthRatio()
{
return lastHealthRatio;
}

@Override
public void setLastHealthRatio(float lastHealthRatio)
{
this.lastHealthRatio = lastHealthRatio;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import mchorse.metamorph.api.morphs.AbstractMorph;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagFloat;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumFacing;
Expand All @@ -30,6 +31,7 @@ public NBTBase writeNBT(Capability<IMorphing> capability, IMorphing instance, En
NBTTagCompound tag = new NBTTagCompound();
NBTTagList acquired = new NBTTagList();
NBTTagList favorites = new NBTTagList();
tag.setTag("lastHealthRatio", new NBTTagFloat(instance.getLastHealthRatio()));

if (instance.getCurrentMorph() != null)
{
Expand Down Expand Up @@ -67,6 +69,7 @@ public void readNBT(Capability<IMorphing> capability, IMorphing instance, EnumFa
NBTTagList acquired = tag.getTagList("Morphs", 10);
NBTTagList favorites = tag.getTagList("Favorites", 3);
NBTTagCompound morphTag = tag.getCompoundTag("Morph");
instance.setLastHealthRatio(tag.getFloat("lastHealthRatio"));

if (!tag.hasNoTags())
{
Expand Down