Skip to content

Commit

Permalink
Add more special mob interactions, fix cooldown animations, add phant…
Browse files Browse the repository at this point in the history
…om to flight tag
  • Loading branch information
Draylar committed Jun 28, 2020
1 parent fba3c8f commit a1e8837
Show file tree
Hide file tree
Showing 17 changed files with 375 additions and 11 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ yarn_mappings=1.16+build.1
loader_version=0.8.8+build.202

# Mod Properties
mod_version=1.0.3
mod_version=1.1.0
maven_group=draylar
archives_base_name=identity

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/draylar/identity/Identity.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private void registerAbilityItemUseHandler() {
ItemStack heldStack = player.getStackInHand(hand);

// ensure cooldown is valid (0) for custom use action
if(heldStack.getCooldown() <= 0) {
if(player.getItemCooldownManager().getCooldownProgress(heldStack.getItem(), 0) <= 0) {
IdentityAbility ability = IdentityAbilities.get((EntityType<? extends LivingEntity>) identity.getType(), heldStack.getItem());

if(ability != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ public static void init() {

fireball.setOwner(player);
world.spawnEntity(fireball);
stack.setCooldown(60);
player.getItemCooldownManager().set(stack.getItem(), 60);
});

IdentityAbilities.register(EntityType.BLAZE, Items.BLAZE_POWDER, (player, world, stack, hand) -> {

SmallFireballEntity smallFireball = new SmallFireballEntity(
world,
player.getX(),
Expand All @@ -58,7 +59,7 @@ public static void init() {

smallFireball.setOwner(player);
world.spawnEntity(smallFireball);
stack.setCooldown(20);
player.getItemCooldownManager().set(stack.getItem(), 20);
});
}

Expand Down
18 changes: 18 additions & 0 deletions src/main/java/draylar/identity/config/IdentityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@ public class IdentityConfig implements ConfigData {

@Comment(value = "Whether identities equip the armor (chestplate, leggings, elytra) worn by the underlying player.")
public boolean identitiesEquipArmor = true;

@Comment(value = "Whether hostile mobs ignore players with hostile mob identities.")
public boolean hostilesIgnoreHostileIdentityPlayer = true;

@Comment(value = "Whether a hostile mob will stop targeting you after switching to a hostile mob identity.")
public boolean hostilesForgetNewHostileIdentityPlayer = false;

@Comment(value = "Whether Wolves will attack Players with an identity that the Wolf would normally hunt (Sheep, Fox, Skeleton).")
public boolean wolvesAttackIdentityPrey = true;

@Comment(value = "Whether owned Wolves will attack Players with an identity that the Wolf would normally hunt (Sheep, Fox, Skeleton).")
public boolean ownedWolvesAttackIdentityPrey = false;

@Comment(value = "Whether Villagers will run from Players morphed as identities villagers normally run from (Zombies).")
public boolean villagersRunFromIdentities = true;

@Comment(value = "Whether Wolves will attack Players with an identity that the Wolf would normally hunt (Fish, Chicken).")
public boolean foxesAttackIdentityPrey = true;
}
72 changes: 72 additions & 0 deletions src/main/java/draylar/identity/mixin/FollowTargetGoalMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package draylar.identity.mixin;

import draylar.identity.Identity;
import draylar.identity.registry.Components;
import net.minecraft.entity.EntityGroup;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.FollowTargetGoal;
import net.minecraft.entity.ai.goal.TrackTargetGoal;
import net.minecraft.entity.boss.WitherEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(FollowTargetGoal.class)
public abstract class FollowTargetGoalMixin extends TrackTargetGoal {

@Shadow protected LivingEntity targetEntity;

public FollowTargetGoalMixin(MobEntity mob, boolean checkVisibility) {
super(mob, checkVisibility);
}

@Inject(
method = "start",
at = @At("HEAD"),
cancellable = true
)
private void ignoreMorphedPlayers(CallbackInfo ci) {
if (Identity.CONFIG.hostilesIgnoreHostileIdentityPlayer && this.mob instanceof HostileEntity && this.targetEntity instanceof PlayerEntity) {
PlayerEntity targetPlayer = (PlayerEntity) this.targetEntity;
LivingEntity identity = Components.CURRENT_IDENTITY.get(targetPlayer).getIdentity();

// withers should ignore undead
if(this.mob instanceof WitherEntity && identity.getGroup().equals(EntityGroup.UNDEAD)) {
super.stop();
ci.cancel();
}

// hostile mobs (besides wither) should not target players morphed as hostile mobs
else if (!(this.mob instanceof WitherEntity) && identity instanceof HostileEntity) {
super.stop();
ci.cancel();
}
}
}

@Override
public boolean shouldContinue() {
// check cancelling for hostiles
if(Identity.CONFIG.hostilesIgnoreHostileIdentityPlayer && Identity.CONFIG.hostilesForgetNewHostileIdentityPlayer && this.mob instanceof HostileEntity && this.targetEntity instanceof PlayerEntity) {
PlayerEntity targetPlayer = (PlayerEntity) this.targetEntity;
LivingEntity identity = Components.CURRENT_IDENTITY.get(targetPlayer).getIdentity();

// withers should ignore undead
if(this.mob instanceof WitherEntity && identity.getGroup().equals(EntityGroup.UNDEAD)) {
return false;
}

// hostile mobs (besides wither) should not target players morphed as hostile mobs
else if (!(this.mob instanceof WitherEntity) && identity instanceof HostileEntity) {
return false;
}
}

return super.shouldContinue();
}
}
43 changes: 43 additions & 0 deletions src/main/java/draylar/identity/mixin/FoxEntityMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package draylar.identity.mixin;

import draylar.identity.Identity;
import draylar.identity.registry.Components;
import draylar.identity.registry.EntityTags;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.FollowTargetGoal;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.passive.FishEntity;
import net.minecraft.entity.passive.FoxEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(FoxEntity.class)
public abstract class FoxEntityMixin extends AnimalEntity {

private FoxEntityMixin(EntityType<? extends AnimalEntity> entityType, World world) {
super(entityType, world);
}

@Inject(
method = "initGoals",
at = @At("RETURN")
)
private void addPlayerTarget(CallbackInfo ci) {
this.targetSelector.add(7, new FollowTargetGoal<>(this, PlayerEntity.class, 10, false, false, player -> {
// ensure foxes can attack players with an identity similar to their normal prey
if(!Identity.CONFIG.foxesAttackIdentityPrey) {
return false;
}

// foxes can target players if their identity is in the fox_prey tag, or if they are an entity that extends FishEntity
// todo: add baby turtle targeting
LivingEntity identity = Components.CURRENT_IDENTITY.get(player).getIdentity();
return identity != null && EntityTags.FOX_PREY.contains(identity.getType()) || identity instanceof FishEntity;
}));
}
}
7 changes: 4 additions & 3 deletions src/main/java/draylar/identity/mixin/LivingEntityMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity {
Expand Down Expand Up @@ -68,10 +69,10 @@ private void onDeath(DamageSource source, CallbackInfo ci) {
)
private void cancelAirIncrement(LivingEntity livingEntity, int air) {
if ((Object) this instanceof PlayerEntity) {
LivingEntity Identity = Components.CURRENT_IDENTITY.get(this).getIdentity();
LivingEntity identity = Components.CURRENT_IDENTITY.get(this).getIdentity();

if (Identity != null) {
if (Identity instanceof WaterCreatureEntity && !(Identity instanceof DolphinEntity)) {
if (identity != null) {
if (identity instanceof WaterCreatureEntity && !(identity instanceof DolphinEntity)) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.PhantomEntity;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand Down Expand Up @@ -51,11 +52,18 @@ private void redirectRender(LivingEntityRenderer renderer, LivingEntity livingEn
identity.prevBodyYaw = livingEntity.prevBodyYaw;
identity.headYaw = livingEntity.headYaw;
identity.prevHeadYaw = livingEntity.prevHeadYaw;
identity.pitch = livingEntity.pitch;
identity.prevPitch = livingEntity.prevPitch;
identity.age = livingEntity.age;
identity.preferredHand = livingEntity.preferredHand;

// phantoms' pitch is inverse for whatever reason
if(identity instanceof PhantomEntity) {
identity.pitch = -livingEntity.pitch;
identity.prevPitch = -livingEntity.prevPitch;
} else {
identity.pitch = livingEntity.pitch;
identity.prevPitch = livingEntity.prevPitch;
}

// equip held items on identity
if(Identity.CONFIG.identitiesEquipItems) {
identity.equipStack(EquipmentSlot.MAINHAND, livingEntity.getEquippedStack(EquipmentSlot.MAINHAND));
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/draylar/identity/mixin/VillagerEntityMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package draylar.identity.mixin;

import draylar.identity.registry.Components;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(VillagerEntity.class)
public abstract class VillagerEntityMixin {

@Shadow protected abstract void sayNo();

@Inject(
method = "interactMob",
at = @At("HEAD"),
cancellable = true
)
private void onInteract(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
LivingEntity identity = Components.CURRENT_IDENTITY.get(player).getIdentity();

if(identity != null && identity.isUndead()) {
this.sayNo();
cir.setReturnValue(ActionResult.SUCCESS);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package draylar.identity.mixin;

import com.google.common.collect.ImmutableMap;
import draylar.identity.Identity;
import draylar.identity.registry.Components;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.brain.sensor.VillagerHostilesSensor;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(VillagerHostilesSensor.class)
public class VillagerHostilesSensorMixin {

@Shadow @Final private static ImmutableMap<EntityType<?>, Float> SQUARED_DISTANCES_FOR_DANGER;

@Inject(
method = "isHostile",
at = @At("HEAD"),
cancellable = true
)
private void checkHostileIdentity(LivingEntity entity, CallbackInfoReturnable<Boolean> cir) {
if(entity instanceof PlayerEntity) {
// check if we should be performing this from config
if(Identity.CONFIG.villagersRunFromIdentities) {
LivingEntity identity = Components.CURRENT_IDENTITY.get(entity).getIdentity();

// check if identity is valid & if it is a type villagers run from
if (identity != null && SQUARED_DISTANCES_FOR_DANGER.containsKey(identity.getType())) {
cir.setReturnValue(true);
}
}
}
}

@Inject(
method = "isCloseEnoughForDanger",
at = @At("HEAD"),
cancellable = true
)
private void checkPlayerDanger(LivingEntity villager, LivingEntity potentialPlayer, CallbackInfoReturnable<Boolean> cir) {
// should only be called if the above mixin passes, so we can assume the config option is true
if(potentialPlayer instanceof PlayerEntity) {
LivingEntity identity = Components.CURRENT_IDENTITY.get(potentialPlayer).getIdentity();

// check if identity is valid & if it is a type villagers run from
if (identity != null && SQUARED_DISTANCES_FOR_DANGER.containsKey(identity.getType())) {
float f = SQUARED_DISTANCES_FOR_DANGER.get(identity.getType());
cir.setReturnValue(potentialPlayer.squaredDistanceTo(villager) <= (double) (f * f));
} else {
cir.setReturnValue(false);
}
}
}
}
54 changes: 54 additions & 0 deletions src/main/java/draylar/identity/mixin/WitherEntityMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package draylar.identity.mixin;

import draylar.identity.registry.Components;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.boss.WitherEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.util.ArrayList;
import java.util.List;

@Mixin(WitherEntity.class)
public abstract class WitherEntityMixin extends HostileEntity {

private WitherEntityMixin(EntityType<? extends HostileEntity> entityType, World world) {
super(entityType, world);
}

@Inject(
method = "mobTick",
at = @At(value = "INVOKE", target = "Ljava/util/List;isEmpty()Z"),
locals = LocalCapture.CAPTURE_FAILHARD
)
private void removeInvalidPlayerTargets(CallbackInfo ci, int j, List<LivingEntity> list, int l) {
List<LivingEntity> toRemove = new ArrayList();

list.forEach(entity -> {
if(entity instanceof PlayerEntity) {
LivingEntity identity = Components.CURRENT_IDENTITY.get(entity).getIdentity();

// potentially ignore undead identity players
if(identity != null && identity.isUndead()) {
if(this.getTarget() != null) {
// if this wither's target is not equal to the current entity
if(!this.getTarget().getUuid().equals(entity.getUuid())) {
toRemove.add(entity);
}
} else {
toRemove.add(entity);
}
}
}
});

list.removeAll(toRemove);
}
}
Loading

0 comments on commit a1e8837

Please sign in to comment.