Ponies that cast magic now have horns

This commit is contained in:
Sollace 2023-05-29 11:54:54 +01:00
parent a444395c28
commit f3d251c1d0
9 changed files with 160 additions and 4 deletions

View file

@ -28,6 +28,10 @@ public interface Ability<T extends Hit> {
*/ */
int getCooldownTime(Pony player); int getCooldownTime(Pony player);
default int getColor(Pony player) {
return -1;
}
/** /**
* Called when an ability is about to be triggered. This event occurs on both the client and server so check {@code Pony#isClient} if you need to know which one you're on. * Called when an ability is about to be triggered. This event occurs on both the client and server so check {@code Pony#isClient} if you need to know which one you're on.
* <p> * <p>

View file

@ -64,6 +64,10 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
return stats.values(); return stats.values();
} }
public Optional<Stat> getActiveStat() {
return stats.values().stream().filter(stat -> stat.getFillProgress() > 0).findFirst();
}
public Stat getStat(AbilitySlot slot) { public Stat getStat(AbilitySlot slot) {
return stats.computeIfAbsent(slot, Stat::new); return stats.computeIfAbsent(slot, Stat::new);
} }
@ -246,7 +250,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
return Optional.empty(); return Optional.empty();
} }
protected synchronized Optional<Ability<?>> getActiveAbility() { public synchronized Optional<Ability<?>> getActiveAbility() {
return activeAbility.filter(ability -> { return activeAbility.filter(ability -> {
return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && player.getCompositeRace().any(ability::canUse)); return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && player.getCompositeRace().any(ability::canUse));
}); });

View file

@ -80,6 +80,22 @@ public class UnicornCastingAbility implements Ability<Hit> {
return !spell.getResult().isAccepted() || spell.getValue().isOn(player) ? 2 : 4; return !spell.getResult().isAccepted() || spell.getValue().isOn(player) ? 2 : 4;
} }
@Override
public int getColor(Pony player) {
TypedActionResult<ItemStack> amulet = getAmulet(player);
if (amulet.getResult().isAccepted()) {
return 0x000000;
}
Hand hand = player.asEntity().isSneaking() ? Hand.OFF_HAND : Hand.MAIN_HAND;
TypedActionResult<CustomisedSpellType<?>> newSpell = player.getCharms().getSpellInHand(hand);
if (newSpell.getResult() != ActionResult.FAIL) {
return newSpell.getValue().type().getColor();
}
return -1;
}
@Override @Override
public void apply(Pony player, Hit data) { public void apply(Pony player, Hit data) {
if (!player.canCast()) { if (!player.canCast()) {

View file

@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -36,6 +37,11 @@ public class UnicornDispellAbility implements Ability<Pos> {
return race.canCast() || race == Race.CHANGELING; return race.canCast() || race == Race.CHANGELING;
} }
@Override
public int getColor(Pony player) {
return SpellType.PORTAL.getColor();
}
@Override @Override
public Identifier getIcon(Pony player, boolean swap) { public Identifier getIcon(Pony player, boolean swap) {
Identifier id = Abilities.REGISTRY.getId(this); Identifier id = Abilities.REGISTRY.getId(this);

View file

@ -5,6 +5,7 @@ import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -60,6 +61,11 @@ public class UnicornTeleportAbility implements Ability<Pos> {
return pos.distanceTo(player) / 10; return pos.distanceTo(player) / 10;
} }
@Override
public int getColor(Pony player) {
return SpellType.PORTAL.getColor();
}
@Override @Override
public Pos tryActivate(Pony player) { public Pos tryActivate(Pony player) {

View file

@ -16,11 +16,22 @@ public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell; SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell;
SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell; SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
SpellPredicate<?> IS_NOT_PLACED = IS_PLACED.negate();
default <Q extends Spell> SpellPredicate<Q> and(SpellPredicate<Q> predicate) { default <Q extends Spell> SpellPredicate<Q> and(SpellPredicate<Q> predicate) {
SpellPredicate<T> self = this; SpellPredicate<T> self = this;
return s -> { return s -> self.test(s) && predicate.test(s);
return self.test(s) && predicate.test(s); }
};
default <Q extends Spell> SpellPredicate<? extends Spell> or(SpellPredicate<Q> predicate) {
SpellPredicate<T> self = this;
return s -> self.test(s) || predicate.test(s);
}
@Override
default SpellPredicate<?> negate() {
SpellPredicate<T> self = this;
return s -> !self.test(s);
} }
default boolean isOn(Caster<?> caster) { default boolean isOn(Caster<?> caster) {

View file

@ -61,6 +61,7 @@ public interface URenderers {
AccessoryFeatureRenderer.register(BraceletFeatureRenderer::new); AccessoryFeatureRenderer.register(BraceletFeatureRenderer::new);
AccessoryFeatureRenderer.register(AmuletFeatureRenderer::new); AccessoryFeatureRenderer.register(AmuletFeatureRenderer::new);
AccessoryFeatureRenderer.register(WingsFeatureRenderer::new); AccessoryFeatureRenderer.register(WingsFeatureRenderer::new);
AccessoryFeatureRenderer.register(HornFeatureRenderer::new);
AccessoryFeatureRenderer.register(IcarusWingsFeatureRenderer::new); AccessoryFeatureRenderer.register(IcarusWingsFeatureRenderer::new);
AccessoryFeatureRenderer.register(BatWingsFeatureRenderer::new); AccessoryFeatureRenderer.register(BatWingsFeatureRenderer::new);
AccessoryFeatureRenderer.register(GlassesFeatureRenderer::new); AccessoryFeatureRenderer.register(GlassesFeatureRenderer::new);

View file

@ -0,0 +1,108 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.ability.AbilityDispatcher.Stat;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelData;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.model.ModelPartBuilder;
import net.minecraft.client.model.ModelPartData;
import net.minecraft.client.model.ModelTransform;
import net.minecraft.client.model.TexturedModelData;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModelPartNames;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.MathHelper;
public class HornFeatureRenderer<E extends LivingEntity> implements AccessoryFeatureRenderer.Feature<E> {
public static final Identifier TEXTURE = Unicopia.id("textures/models/horn/unicorn.png");
private final HornModel model;
private final FeatureRendererContext<E, ? extends BipedEntityModel<E>> context;
public HornFeatureRenderer(FeatureRendererContext<E, ? extends BipedEntityModel<E>> context) {
this.context = context;
model = new HornModel(HornModel.getData(Dilation.NONE).createModel());
}
protected boolean canRender(E entity) {
return entity instanceof PlayerEntity player && Pony.of(player).getObservedSpecies().canCast();
}
@Override
public void render(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, E entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
if (canRender(entity)) {
model.setAngles(context.getModel());
model.setState(false);
model.render(stack, ItemRenderer.getArmorGlintConsumer(renderContext, RenderLayer.getArmorCutoutNoCull(TEXTURE), false, false), lightUv, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
Pony.of(entity).flatMap(pony -> {
return pony.getAbilities().getActiveStat()
.flatMap(Stat::getActiveAbility)
.map(ability -> ability.getColor(pony))
.filter(i -> i != -1).or(() -> pony.getSpellSlot().get(SpellPredicate.IS_NOT_PLACED, false).map(spell -> spell.getType().getColor()));
}).ifPresent(color -> {
model.setState(true);
model.render(stack, ItemRenderer.getArmorGlintConsumer(renderContext, RenderLayers.getMagicColored(color), false, false), lightUv, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
});
}
}
public static class HornModel extends Model {
private final ModelPart part;
public HornModel(ModelPart tree) {
super(RenderLayer::getEntityTranslucent);
part = tree.getChild(EntityModelPartNames.HEAD);
}
public static TexturedModelData getData(Dilation dilation) {
ModelData data = new ModelData();
ModelPartData root = data.getRoot();
ModelPartData head = root.addChild(EntityModelPartNames.HEAD, ModelPartBuilder.create()
.uv(0, 0)
.cuboid(-4, -8, -4, 8, 8, 8, dilation), ModelTransform.NONE);
head.addChild("horn", ModelPartBuilder.create()
.uv(0, 3)
.cuboid(-0.5F, -11, -3.5F, 1, 4, 1, dilation), ModelTransform.rotation(29 * MathHelper.RADIANS_PER_DEGREE, 0, 0));
head.addChild("magic", ModelPartBuilder.create()
.uv(0, 3)
.cuboid(-0.5F, -11, -3.5F, 1, 4, 1, dilation.add(0.5F)), ModelTransform.rotation(29 * MathHelper.RADIANS_PER_DEGREE, 0, 0));
return TexturedModelData.of(data, 64, 64);
}
public void setAngles(BipedEntityModel<?> biped) {
part.copyTransform(biped.getHead());
}
public void setState(boolean magic) {
part.hidden = true;
part.getChild("horn").visible = !magic;
part.getChild("magic").visible = magic;
}
@Override
public void render(MatrixStack matrixStack, VertexConsumer vertexConsumer, int i, int j, float f, float g, float h, float k) {
part.render(matrixStack, vertexConsumer, i, j, f, g, h, k);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB