Implement proper composition of abilities when wearing the alicorn amulet

This commit is contained in:
Sollace 2022-12-09 23:55:53 +00:00
parent da107e7b81
commit a1268e8209
12 changed files with 47 additions and 24 deletions

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia; package com.minelittlepony.unicopia;
import java.util.Set; import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -197,6 +198,16 @@ public final class Race implements Affine {
public static Set<Race> allPermitted(PlayerEntity player) { public static Set<Race> allPermitted(PlayerEntity player) {
return REGISTRY.stream().filter(r -> r.isPermitted(player)).collect(Collectors.toSet()); return REGISTRY.stream().filter(r -> r.isPermitted(player)).collect(Collectors.toSet());
} }
public record Composite (Race physical, Race pseudo) {
public boolean includes(Race race) {
return physical == race || pseudo == race;
}
public boolean any(Predicate<Race> test) {
return test.test(physical) || test.test(pseudo);
}
}
} }

View file

@ -11,17 +11,16 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.util.Registries; import com.minelittlepony.unicopia.util.Registries;
import net.minecraft.util.Identifier; import net.minecraft.util.*;
import net.minecraft.util.Util;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
public interface Abilities { public interface Abilities {
Registry<Ability<?>> REGISTRY = Registries.createSimple(Unicopia.id("abilities")); Registry<Ability<?>> REGISTRY = Registries.createSimple(Unicopia.id("abilities"));
Map<AbilitySlot, Set<Ability<?>>> BY_SLOT = new EnumMap<>(AbilitySlot.class); Map<AbilitySlot, Set<Ability<?>>> BY_SLOT = new EnumMap<>(AbilitySlot.class);
BiFunction<AbilitySlot, Race, List<Ability<?>>> BY_SLOT_AND_RACE = Util.memoize((slot, race) -> { BiFunction<AbilitySlot, Race.Composite, List<Ability<?>>> BY_SLOT_AND_COMPOSITE_RACE = Util.memoize((slot, race) -> {
return BY_SLOT.computeIfAbsent(slot, s -> new LinkedHashSet<>()) return BY_SLOT.computeIfAbsent(slot, s -> new LinkedHashSet<>())
.stream() .stream()
.filter(a -> a.canUse(race)) .filter(a -> race.any(a::canUse))
.toList(); .toList();
}); });

View file

@ -224,7 +224,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
} }
public Optional<Ability<?>> getAbility(long page) { public Optional<Ability<?>> getAbility(long page) {
List<Ability<?>> found = Abilities.BY_SLOT_AND_RACE.apply(slot, player.getSpecies()); List<Ability<?>> found = Abilities.BY_SLOT_AND_COMPOSITE_RACE.apply(slot, player.getCompositeRace());
if (found.isEmpty()) { if (found.isEmpty()) {
return Optional.empty(); return Optional.empty();
} }
@ -233,7 +233,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
} }
public long getMaxPage() { public long getMaxPage() {
return Abilities.BY_SLOT_AND_RACE.apply(slot, player.getSpecies()).size(); return Abilities.BY_SLOT_AND_COMPOSITE_RACE.apply(slot, player.getCompositeRace()).size();
} }
protected synchronized Optional<Ability<?>> setActiveAbility(@Nullable Ability<?> power) { protected synchronized Optional<Ability<?>> setActiveAbility(@Nullable Ability<?> power) {
@ -249,7 +249,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
protected synchronized Optional<Ability<?>> getActiveAbility() { protected synchronized Optional<Ability<?>> getActiveAbility() {
return activeAbility.filter(ability -> { return activeAbility.filter(ability -> {
return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && ability.canUse(player.getSpecies())); return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && player.getCompositeRace().any(ability::canUse));
}); });
} }

View file

@ -84,7 +84,7 @@ public class DismissSpellScreen extends GameGui {
DrawableUtil.drawArc(matrices, 160, 1600, 0, DrawableUtil.TAU, 0x00000020, false); DrawableUtil.drawArc(matrices, 160, 1600, 0, DrawableUtil.TAU, 0x00000020, false);
super.render(matrices, mouseX, mouseY, delta); super.render(matrices, mouseX, mouseY, delta);
DrawableUtil.renderRaceIcon(matrices, pony.getSpecies(), 0, 0, 16); DrawableUtil.renderRaceIcon(matrices, pony.getObservedSpecies(), 0, 0, 16);
matrices.pop(); matrices.pop();
DrawableUtil.drawLine(matrices, mouseX, mouseY - 4, mouseX, mouseY + 4, 0xFFAAFF99); DrawableUtil.drawLine(matrices, mouseX, mouseY - 4, mouseX, mouseY + 4, 0xFFAAFF99);

View file

@ -14,6 +14,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellTyp
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.KeyBindingsHandler; import com.minelittlepony.unicopia.client.KeyBindingsHandler;
import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.client.sound.*;
import com.minelittlepony.unicopia.entity.AmuletSelectors;
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.entity.effect.UEffects;
@ -125,7 +126,7 @@ public class UHud extends DrawableHelper {
matrices.pop(); matrices.pop();
if (pony.getActualSpecies().canCast()) { if (pony.getActualSpecies().canCast() || AmuletSelectors.ALICORN_AMULET.test(pony.getMaster())) {
renderSpell(pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 15 - xDirection * 13, hudY + 3); renderSpell(pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 15 - xDirection * 13, hudY + 3);
renderSpell(pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 15 - xDirection * 2, hudY - 3); renderSpell(pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 15 - xDirection * 2, hudY - 3);
} }
@ -217,7 +218,7 @@ public class UHud extends DrawableHelper {
ItemStack glasses = GlassesItem.getForEntity(client.player); ItemStack glasses = GlassesItem.getForEntity(client.player);
boolean hasSunglasses = glasses.getItem() == UItems.SUNGLASSES; boolean hasSunglasses = glasses.getItem() == UItems.SUNGLASSES;
if (hasEffect || (!hasSunglasses && pony.getSpecies() == Race.BAT && SunBlindnessStatusEffect.hasSunExposure(client.player))) { if (hasEffect || (!hasSunglasses && pony.getObservedSpecies() == Race.BAT && SunBlindnessStatusEffect.hasSunExposure(client.player))) {
float i = hasEffect ? (client.player.getStatusEffect(UEffects.SUN_BLINDNESS).getDuration() - tickDelta) / SunBlindnessStatusEffect.MAX_DURATION : 0; float i = hasEffect ? (client.player.getStatusEffect(UEffects.SUN_BLINDNESS).getDuration() - tickDelta) / SunBlindnessStatusEffect.MAX_DURATION : 0;
float pulse = (1 + (float)Math.sin(client.player.age / 108F)) * 0.25F; float pulse = (1 + (float)Math.sin(client.player.age / 108F)) * 0.25F;

View file

@ -11,10 +11,11 @@ public class ProfileTooltip {
public static Tooltip get(Pony pony) { public static Tooltip get(Pony pony) {
return () -> { return () -> {
return List.of( return List.of(
Text.literal(String.format("Level %d ", pony.getLevel().get() + 1)).append(pony.getSpecies().getDisplayName()).formatted(pony.getSpecies().getAffinity().getColor()), Text.literal(String.format("Level %d ", pony.getLevel().get() + 1)).append(pony.getActualSpecies().getDisplayName()).formatted(pony.getSpecies().getAffinity().getColor()),
Text.literal(String.format("Mana: %d%%", (int)(pony.getMagicalReserves().getMana().getPercentFill() * 100))), Text.literal(String.format("Mana: %d%%", (int)(pony.getMagicalReserves().getMana().getPercentFill() * 100))),
Text.literal(String.format("Corruption: %d%%", (int)(pony.getCorruption().getScaled(100)))),
Text.literal(String.format("Experience: %d", (int)(pony.getMagicalReserves().getXp().getPercentFill() * 100))), Text.literal(String.format("Experience: %d", (int)(pony.getMagicalReserves().getXp().getPercentFill() * 100))),
Text.literal(String.format("Next level in: %d experience points", 100 - (int)(pony.getMagicalReserves().getXp().getPercentFill() * 100))) Text.literal(String.format("Next level in: %dxp", 100 - (int)(pony.getMagicalReserves().getXp().getPercentFill() * 100)))
); );
}; };
} }

View file

@ -36,9 +36,15 @@ public class SpellbookProfilePageContent extends DrawableHelper implements Spell
screen.addDrawable(new SpellbookScreen.ImageButton(x, y, size, size)) screen.addDrawable(new SpellbookScreen.ImageButton(x, y, size, size))
.getStyle() .getStyle()
.setIcon(TribeButton.createSprite(pony.getSpecies(), 0, 0, size)) .setIcon(TribeButton.createSprite(pony.getActualSpecies(), 0, 0, size))
.setTooltip(ProfileTooltip.get(pony)); .setTooltip(ProfileTooltip.get(pony));
if (pony.getSpecies() != pony.getActualSpecies()) {
int halfSize = size / 2;
screen.addDrawable(new SpellbookScreen.ImageButton(x + halfSize, y + halfSize, halfSize, halfSize))
.getStyle()
.setIcon(TribeButton.createSprite(pony.getSpecies(), 0, 0, halfSize));
}
float mainAngle = 90 * MathHelper.RADIANS_PER_DEGREE; float mainAngle = 90 * MathHelper.RADIANS_PER_DEGREE;
float offAngle = 60 * MathHelper.RADIANS_PER_DEGREE; float offAngle = 60 * MathHelper.RADIANS_PER_DEGREE;

View file

@ -56,7 +56,7 @@ public class SunBlindnessStatusEffect extends StatusEffect {
public static boolean isSunImmune(LivingEntity entity) { public static boolean isSunImmune(LivingEntity entity) {
return entity.hasStatusEffect(StatusEffects.BLINDNESS) return entity.hasStatusEffect(StatusEffects.BLINDNESS)
|| entity.hasPortalCooldown() || entity.hasPortalCooldown()
|| Pony.of(entity).map(pony -> pony.isSunImmune()).orElse(false); || Pony.of(entity).filter(Pony::isSunImmune).isPresent();
} }
public static boolean hasSunExposure(LivingEntity entity) { public static boolean hasSunExposure(LivingEntity entity) {

View file

@ -218,7 +218,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if ((entity.isOnGround() && entity.isSneaking()) if ((entity.isOnGround() && entity.isSneaking())
|| entity.isTouchingWater() || entity.isTouchingWater()
|| entity.horizontalCollision || entity.horizontalCollision
|| (entity.verticalCollision && (pony.getSpecies() != Race.BAT || velocity.y < 0))) { || (entity.verticalCollision && (pony.getObservedSpecies() != Race.BAT || velocity.y < 0))) {
if (entity.getAbilities().flying && entity.horizontalCollision) { if (entity.getAbilities().flying && entity.horizontalCollision) {
handleWallCollission(velocity); handleWallCollission(velocity);
@ -251,7 +251,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if (type.canFly()) { if (type.canFly()) {
if (isFlying()) { if (isFlying()) {
if (pony.getSpecies() == Race.BAT && entity.verticalCollision && pony.canHangAt(pony.getOrigin().up(2))) { if (pony.getObservedSpecies() == Race.BAT && entity.verticalCollision && pony.canHangAt(pony.getOrigin().up(2))) {
EntityAttributeInstance attr = entity.getAttributeInstance(UEntityAttributes.ENTITY_GRAVTY_MODIFIER); EntityAttributeInstance attr = entity.getAttributeInstance(UEntityAttributes.ENTITY_GRAVTY_MODIFIER);
if (!attr.hasModifier(PlayerAttributes.BAT_HANGING)) { if (!attr.hasModifier(PlayerAttributes.BAT_HANGING)) {
@ -327,7 +327,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if (type.isAvian()) { if (type.isAvian()) {
applyThrust(velocity); applyThrust(velocity);
if (pony.getSpecies() != Race.BAT && entity.world.random.nextInt(9000) == 0) { if (pony.getObservedSpecies() != Race.BAT && entity.world.random.nextInt(9000) == 0) {
entity.dropItem(UItems.PEGASUS_FEATHER); entity.dropItem(UItems.PEGASUS_FEATHER);
entity.playSound(USounds.ENTITY_PLAYER_PEGASUS_MOLT, 0.3F, 1); entity.playSound(USounds.ENTITY_PLAYER_PEGASUS_MOLT, 0.3F, 1);
UCriteria.SHED_FEATHER.trigger(entity); UCriteria.SHED_FEATHER.trigger(entity);

View file

@ -181,6 +181,11 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
.orElse(getActualSpecies()); .orElse(getActualSpecies());
} }
public Race.Composite getCompositeRace() {
Race observed = getObservedSpecies();
return new Race.Composite(observed, AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN : observed);
}
public Race getActualSpecies() { public Race getActualSpecies() {
return Race.fromName(entity.getDataTracker().get(RACE), Race.HUMAN); return Race.fromName(entity.getDataTracker().get(RACE), Race.HUMAN);
} }
@ -287,7 +292,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
public void onSpawn() { public void onSpawn() {
if (entity.world instanceof ServerWorld sw if (entity.world instanceof ServerWorld sw
&& getSpecies() == Race.BAT && getObservedSpecies() == Race.BAT
&& sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE
&& SunBlindnessStatusEffect.isPositionExposedToSun(sw, getOrigin())) { && SunBlindnessStatusEffect.isPositionExposedToSun(sw, getOrigin())) {
SpawnLocator.selectSpawnPosition(sw, entity); SpawnLocator.selectSpawnPosition(sw, entity);
@ -371,14 +376,14 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
if (isHanging()) { if (isHanging()) {
((LivingEntityDuck)entity).setLeaningPitch(0); ((LivingEntityDuck)entity).setLeaningPitch(0);
if (!isClient() && (getSpecies() != Race.BAT || (ticksHanging++ > 2 && hangingPosition.filter(getOrigin().down()::equals).filter(this::canHangAt).isEmpty()))) { if (!isClient() && (getObservedSpecies() != Race.BAT || (ticksHanging++ > 2 && hangingPosition.filter(getOrigin().down()::equals).filter(this::canHangAt).isEmpty()))) {
stopHanging(); stopHanging();
} }
} else { } else {
ticksHanging = 0; ticksHanging = 0;
} }
if (getSpecies() == Race.BAT && !entity.hasPortalCooldown()) { if (getObservedSpecies() == Race.BAT && !entity.hasPortalCooldown()) {
if (SunBlindnessStatusEffect.hasSunExposure(entity)) { if (SunBlindnessStatusEffect.hasSunExposure(entity)) {
if (ticksInSun < 200) { if (ticksInSun < 200) {
ticksInSun++; ticksInSun++;

View file

@ -22,7 +22,7 @@ public class RacePredicatedAliasedBlockItem extends AliasedBlockItem {
@Override @Override
public ActionResult useOnBlock(ItemUsageContext context) { public ActionResult useOnBlock(ItemUsageContext context) {
Pony pony = Pony.of(context.getPlayer()); Pony pony = Pony.of(context.getPlayer());
if (pony == null || !predicate.test(pony.getSpecies())) { if (pony == null || !predicate.test(pony.getObservedSpecies())) {
return ActionResult.FAIL; return ActionResult.FAIL;
} }

View file

@ -37,7 +37,7 @@ public record Ailment (
} }
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) { public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
if (!Pony.of(player).getSpecies().hasIronGut()) { if (!Pony.of(player).getObservedSpecies().hasIronGut()) {
return TypedActionResult.fail(player.getStackInHand(hand)); return TypedActionResult.fail(player.getStackInHand(hand));
} }
return null; return null;
@ -52,7 +52,7 @@ public record Ailment (
if (map.isEmpty()) { if (map.isEmpty()) {
return of(def); return of(def);
} }
return entity -> Optional.of(entity instanceof PlayerEntity player ? map.getOrDefault(Pony.of(player).getSpecies(), def) : def); return entity -> Optional.of(entity instanceof PlayerEntity player ? map.getOrDefault(Pony.of(player).getObservedSpecies(), def) : def);
} }
static Ailment.Set of(Ailment ailment) { static Ailment.Set of(Ailment ailment) {