Entities enticed by want it need it now have hearts over their eyes

This commit is contained in:
Sollace 2023-09-13 23:15:21 +01:00
parent 159a870599
commit 63d6e96da4
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
11 changed files with 258 additions and 7 deletions

View file

@ -0,0 +1,82 @@
package com.minelittlepony.unicopia.client.render;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Stack;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.MathHelper;
public class ModelPartHooks {
private static final float PIXEL_SCALE = 0.0625F; // 1/16;
private static Stack<Set<EnqueudHeadRender>> renderCalls = new Stack<>();
public static void startCollecting() {
renderCalls.push(new LinkedHashSet<>());
}
public static Set<EnqueudHeadRender> stopCollecting() {
return renderCalls.isEmpty() ? Set.of() : renderCalls.pop();
}
public static void onHeadRendered(ModelPart part, MatrixStack matrices) {
if (part.hidden || !part.visible || renderCalls.isEmpty()) {
return;
}
var head = renderCalls.peek();
if (head.size() > 5) {
renderCalls.pop();
return;
}
final var bestCandidate = new EnqueudHeadRender();
matrices.push();
part.forEachCuboid(matrices, (entry, name, index, cube) -> {
float x = cube.maxX - cube.minX;
float y = cube.maxY - cube.minY;
float z = cube.maxZ - cube.minZ;
float volume = div(Math.abs(x * y * z), Math.abs(x + y + z)) * 3F;
if (volume > 0 && volume > bestCandidate.volume) {
bestCandidate.cube = cube;
bestCandidate.transformation = entry;
bestCandidate.volume = volume;
bestCandidate.maxSideLength = Math.max(Math.max(x, z), y);
}
});
matrices.pop();
if (bestCandidate.transformation != null) {
head.add(bestCandidate);
}
}
static float div(float a, float b) {
return b == 0 ? 0 : a / b;
}
public static final class EnqueudHeadRender {
private ModelPart.Cuboid cube;
private MatrixStack.Entry transformation;
private float volume;
private float maxSideLength;
public void transform(MatrixStack matrices, float cubeSize) {
matrices.peek().getNormalMatrix().set(transformation.getNormalMatrix());
matrices.peek().getPositionMatrix().set(transformation.getPositionMatrix());
float x = MathHelper.lerp(0.5F, cube.minX, cube.maxX);
float y = MathHelper.lerp(0.5F, cube.minY, cube.maxY);
float z = MathHelper.lerp(0.5F, cube.minZ, cube.maxZ);
float scale = (maxSideLength / cubeSize) * PIXEL_SCALE;
matrices.translate(x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
matrices.scale(scale, scale, scale);
//matrices.peek().getPositionMatrix().scaleAround(scale, x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
//matrices.translate(cube.minX * PIXEL_SCALE, cube.minY * PIXEL_SCALE, cube.minZ * PIXEL_SCALE);
}
}
}

View file

@ -0,0 +1,68 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.ModelData;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.model.ModelPartBuilder;
import net.minecraft.client.model.ModelTransform;
import net.minecraft.client.model.TexturedModelData;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
public class SmittenEyesRenderer {
public static final SmittenEyesRenderer INSTANCE = new SmittenEyesRenderer();
private static final Identifier TEXTURE = Unicopia.id("textures/entity/smitten_eyes.png");
private final MinecraftClient client = MinecraftClient.getInstance();
private final ModelPart model;
SmittenEyesRenderer() {
ModelData data = new ModelData();
data.getRoot().addChild("hearts", ModelPartBuilder.create()
.uv(0, 0)
.cuboid(-4, -4, -4, 8.0f, 8.0f, 8.0f, Dilation.NONE), ModelTransform.NONE);
model = TexturedModelData.of(data, 32, 32).createModel();
}
public void render(Creature pony, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityCutout(TEXTURE));
ModelPartHooks.stopCollecting().forEach(head -> {
matrices.push();
head.transform(matrices, 0.95F);
if (MineLPDelegate.getInstance().getRace(pony.asEntity()).isEquine()) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-90), 0, 1.2F, 0);
}
float scale = 1F + (1.3F + MathHelper.sin(pony.asEntity().age / 3F) * 0.06F);
matrices.scale(scale, scale, scale);
matrices.translate(0, 0.05F, 0);
model.render(matrices, buffer, light, overlay, 1, 1, 1, 1);
if (client.getEntityRenderDispatcher().shouldRenderHitboxes()) {
VertexConsumer lines = vertices.getBuffer(RenderLayer.getLines());
WorldRenderer.drawBox(matrices, lines, new Box(-0.25, 0, -0.25, 0.25, 0.25, 0.25), 1, 1, 0, 1);
WorldRenderer.drawBox(matrices, lines, new Box(-0.25, -0.25, -0.25, 0.25, 0, 0.25), 1, 0, 1, 1);
}
matrices.pop();
});
}
public boolean isSmitten(Creature pony) {
return pony.isSmitten() || WantItNeedItEnchantment.getLevel(pony.asEntity()) > 0;
}
}

View file

@ -20,6 +20,7 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*; import net.minecraft.client.render.*;
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.render.entity.LivingEntityRenderer; import net.minecraft.client.render.entity.LivingEntityRenderer;
@ -146,6 +147,8 @@ public class WorldRenderDelegate {
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
} else if (pony instanceof Creature creature && SmittenEyesRenderer.INSTANCE.isSmitten(creature)) {
ModelPartHooks.startCollecting();
} }
matrices.translate(-x, -y - owner.getHeight() / 2, -z); matrices.translate(-x, -y - owner.getHeight() / 2, -z);
@ -178,7 +181,7 @@ public class WorldRenderDelegate {
PehkUtil.clearScale(ee); PehkUtil.clearScale(ee);
}); });
afterEntityRender(pony, matrices); afterEntityRender(pony, matrices, light);
PehkUtil.clearScale(e); PehkUtil.clearScale(e);
return true; return true;
} }
@ -189,11 +192,16 @@ public class WorldRenderDelegate {
return false; return false;
} }
public void afterEntityRender(Equine<?> pony, MatrixStack matrices) { public void afterEntityRender(Equine<?> pony, MatrixStack matrices, int light) {
if (recurseFrosting) { if (recurseFrosting) {
return; return;
} }
if (pony instanceof Creature creature && SmittenEyesRenderer.INSTANCE.isSmitten(creature)) {
Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
SmittenEyesRenderer.INSTANCE.render(creature, matrices, immediate, light, 0);
}
if (pony instanceof ItemImpl || pony instanceof Living) { if (pony instanceof ItemImpl || pony instanceof Living) {
matrices.pop(); matrices.pop();

View file

@ -41,6 +41,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT); public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Integer> EATING = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData<Integer> EATING = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Boolean> DISCORDED = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.BOOLEAN); private static final TrackedData<Boolean> DISCORDED = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
private static final TrackedData<Boolean> SMITTEN = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
public static void boostrap() {} public static void boostrap() {}
@ -56,6 +57,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
private EatMuffinGoal eatMuffinGoal; private EatMuffinGoal eatMuffinGoal;
private boolean discordedChanged = true; private boolean discordedChanged = true;
private int smittenTicks;
private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> { private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> {
return Equine.of(e) return Equine.of(e)
@ -70,6 +72,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
entity.getDataTracker().startTracking(MASTER, owner.toNBT()); entity.getDataTracker().startTracking(MASTER, owner.toNBT());
entity.getDataTracker().startTracking(EATING, 0); entity.getDataTracker().startTracking(EATING, 0);
entity.getDataTracker().startTracking(DISCORDED, false); entity.getDataTracker().startTracking(DISCORDED, false);
entity.getDataTracker().startTracking(SMITTEN, false);
addTicker(physics); addTicker(physics);
addTicker(this::updateConsumption); addTicker(this::updateConsumption);
@ -92,6 +95,15 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
return entity.getDataTracker().get(DISCORDED); return entity.getDataTracker().get(DISCORDED);
} }
public boolean isSmitten() {
return entity.getDataTracker().get(SMITTEN);
}
public void setSmitten(boolean smitten) {
smittenTicks = smitten ? 20 : 0;
entity.getDataTracker().set(SMITTEN, smitten);
}
public void setDiscorded(boolean discorded) { public void setDiscorded(boolean discorded) {
entity.getDataTracker().set(DISCORDED, discorded); entity.getDataTracker().set(DISCORDED, discorded);
discordedChanged = true; discordedChanged = true;
@ -131,7 +143,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
DynamicTargetGoal targetter = new DynamicTargetGoal((MobEntity)entity); DynamicTargetGoal targetter = new DynamicTargetGoal((MobEntity)entity);
targets.add(1, targetter); targets.add(1, targetter);
if (!Unicopia.getConfig().wantItNeedItEntityExcludelist.get().contains(EntityType.getId(entity.getType()).toString())) { if (!Unicopia.getConfig().wantItNeedItEntityExcludelist.get().contains(EntityType.getId(entity.getType()).toString())) {
goals.add(1, new WantItTakeItGoal((MobEntity)entity, targetter)); goals.add(1, new WantItTakeItGoal(this, targetter));
} }
if (entity.getType().getSpawnGroup() == SpawnGroup.MONSTER) { if (entity.getType().getSpawnGroup() == SpawnGroup.MONSTER) {
goals.add(3, new BreakHeartGoal((MobEntity)entity, targetter)); goals.add(3, new BreakHeartGoal((MobEntity)entity, targetter));
@ -199,6 +211,12 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
initDiscordedAi(); initDiscordedAi();
} }
if (!isClient() && smittenTicks > 0) {
if (--smittenTicks <= 0) {
setSmitten(false);
}
}
return super.beforeUpdate(); return super.beforeUpdate();
} }

View file

@ -4,6 +4,10 @@ import java.util.List;
import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
@ -56,6 +60,16 @@ public class ItemImpl implements Equine<ItemEntity> {
setSpecies(Race.HUMAN); setSpecies(Race.HUMAN);
setSpecies(race); setSpecies(race);
} }
if (WantItNeedItEnchantment.getLevel(entity) > 0) {
var random = entity.getWorld().random;
if (random.nextInt(15) == 0) {
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, entity.getPos().add(
VecHelper.sphere(random).get().add(0, 1, 0)
), 0.2F), entity, 1);
}
}
} }
ItemStack stack = entity.getStack(); ItemStack stack = entity.getStack();

View file

@ -2,11 +2,14 @@ package com.minelittlepony.unicopia.entity.ai;
import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment; import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.EquipmentSlot;
@ -26,8 +29,11 @@ public class WantItTakeItGoal extends BreakHeartGoal {
protected int cooldown; protected int cooldown;
public WantItTakeItGoal(MobEntity mob, DynamicTargetGoal targetter) { private final Creature creature;
super(mob, targetter);
public WantItTakeItGoal(Creature creature, DynamicTargetGoal targetter) {
super((MobEntity)creature.asEntity(), targetter);
this.creature = creature;
} }
@Override @Override
@ -41,7 +47,7 @@ public class WantItTakeItGoal extends BreakHeartGoal {
@Override @Override
protected void attackTarget(Entity target, double reach, double distance) { protected void attackTarget(Entity target, double reach, double distance) {
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, mob, 0.2F), mob, 1); ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, mob.getPos().add(VecHelper.sphere(mob.getWorld().random).get()), 0.2F), mob, 1);
double speed = 0.8D; double speed = 0.8D;
@ -59,6 +65,7 @@ public class WantItTakeItGoal extends BreakHeartGoal {
mob.getNavigation().startMovingTo(target, speed); mob.getNavigation().startMovingTo(target, speed);
cooldown = Math.max(cooldown - 1, 0); cooldown = Math.max(cooldown - 1, 0);
creature.setSmitten(true);
if (distance <= reach) { if (distance <= reach) {
if (target instanceof LivingEntity) { if (target instanceof LivingEntity) {

View file

@ -0,0 +1,5 @@
package com.minelittlepony.unicopia.entity.duck;
public interface Hookable {
void enableHooks();
}

View file

@ -27,6 +27,6 @@ abstract class MixinEntityRenderDispatcher {
@Inject(method = RENDER, at = @At("RETURN")) @Inject(method = RENDER, at = @At("RETURN"))
private <E extends Entity> void afterRender(E entity, double x, double y, double z, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo info) { private <E extends Entity> void afterRender(E entity, double x, double y, double z, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo info) {
Equine.of(entity).ifPresent(eq -> WorldRenderDelegate.INSTANCE.afterEntityRender(eq, matrices)); Equine.of(entity).ifPresent(eq -> WorldRenderDelegate.INSTANCE.afterEntityRender(eq, matrices, light));
} }
} }

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.mixin.client;
import java.util.List;
import java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.client.render.ModelPartHooks;
import com.minelittlepony.unicopia.entity.duck.Hookable;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.model.ModelPart.Cuboid;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.EntityModelPartNames;
import net.minecraft.client.util.math.MatrixStack;
@Mixin(ModelPart.class)
abstract class MixinModelPart implements Hookable {
@Unique
private boolean isHeadPart;
@Shadow
private boolean visible;
@Inject(method = "<init>", at = @At("RETURN"))
private void onModelPart(List<Cuboid> cuboids, Map<String, ModelPart> children, CallbackInfo info) {
if (((Object)children.getOrDefault(EntityModelPartNames.HEAD, null)) instanceof Hookable hook) {
hook.enableHooks();
}
}
@Override
public void enableHooks() {
isHeadPart = true;
}
@Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;IIFFFF)V", at = @At("RETURN"))
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha, CallbackInfo info) {
if (visible && isHeadPart) {
ModelPartHooks.onHeadRendered((ModelPart)(Object)this, matrices);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -65,6 +65,7 @@
"client.MixinItemModels", "client.MixinItemModels",
"client.MixinKeyboardInput", "client.MixinKeyboardInput",
"client.MixinLivingEntityRenderer", "client.MixinLivingEntityRenderer",
"client.MixinModelPart",
"client.MixinMouse", "client.MixinMouse",
"client.MixinPlayerEntityRenderer", "client.MixinPlayerEntityRenderer",
"client.MixinTooltipComponent", "client.MixinTooltipComponent",