Add a time change ability for the unicorn amulet

This commit is contained in:
Sollace 2023-08-27 14:56:18 +01:00
parent c6ad29eef3
commit 7787ca68b5
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
15 changed files with 161 additions and 7 deletions

View file

@ -20,13 +20,14 @@ public interface Abilities {
BiFunction<AbilitySlot, Race.Composite, List<Ability<?>>> BY_SLOT_AND_COMPOSITE_RACE = Util.memoize((slot, race) -> {
return BY_SLOT.computeIfAbsent(slot, s -> new LinkedHashSet<>())
.stream()
.filter(a -> race.any(a::canUse))
.filter(a -> a.canUse(race))
.toList();
});
// unicorn / alicorn
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY);
Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.PRIMARY);
Ability<?> TIME = register(new TimeChangeAbility(), "time_control", AbilitySlot.SECONDARY);
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.SECONDARY);
Ability<?> GROUP_TELEPORT = register(new UnicornGroupTeleportAbility(), "teleport_group", AbilitySlot.SECONDARY);
Ability<?> DISPELL = register(new UnicornDispellAbility(), "dispell", AbilitySlot.TERTIARY);

View file

@ -56,6 +56,14 @@ public interface Ability<T extends Hit> {
return false;
}
/**
* Checks if the given race is permitted to use this ability
* @param race The player's species
*/
default boolean canUse(Race.Composite race) {
return race.any(this::canUse);
}
/**
* Checks if the given race is permitted to use this ability
* @param playerSpecies The player's species

View file

@ -262,7 +262,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
public synchronized Optional<Ability<?>> getActiveAbility() {
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)) && ability.canUse(player.getCompositeRace()));
});
}

View file

@ -0,0 +1,69 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Hit.Serializer;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
public class TimeChangeAbility implements Ability<Hit> {
@Override
public boolean canUse(Race race) {
return race == Race.ALICORN;
}
@Override
public boolean canUse(Race.Composite race) {
return race.physical() != race.pseudo() && race.pseudo() == Race.UNICORN;
}
@Override
public int getCooldownTime(Pony player) {
return 2;
}
@Override
public int getWarmupTime(Pony player) {
return 20;
}
@Override
public double getCostEstimate(Pony player) {
return 400;
}
@Override
public Serializer<Hit> getSerializer() {
return Hit.SERIALIZER;
}
@Override
public Optional<Hit> prepare(Pony player) {
return Hit.INSTANCE;
}
@Override
public boolean apply(Pony player, Hit data) {
if (player.getSpellSlot().contains(SpellType.TIME_CONTROL)) {
player.getSpellSlot().removeWhere(SpellType.TIME_CONTROL, true);
} else {
SpellType.TIME_CONTROL.withTraits().apply(player, CastingMethod.INNATE);
}
return true;
}
@Override
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -0,0 +1,69 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.*;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
/**
* Internal.
* <p>
* Used by the Rainboom ability.
*/
public class TimeControlAbilitySpell extends AbstractSpell {
private Vec3d prevRotation = null;
public TimeControlAbilitySpell(CustomisedSpellType<?> type) {
super(type);
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (situation != Situation.BODY) {
return false;
}
if (!(source instanceof Pony pony) || !Abilities.TIME.canUse(pony.getCompositeRace())) {
return false;
}
if (source.asWorld() instanceof ServerWorld sw) {
float yaw = MathHelper.wrapDegrees(source.asEntity().getHeadYaw());
float pitch = MathHelper.wrapDegrees(source.asEntity().getPitch(1)) / 90F;
if (yaw > 0) {
pitch += 90;
}
Vec3d rotation = new Vec3d(pitch, 0, 1);
if (prevRotation != null) {
pitch = (float)MathHelper.lerp(0.05, pitch, rotation.x);
sw.setTimeOfDay((long)(pitch * 6000));
}
prevRotation = new Vec3d(pitch, 0, 1);
}
return source.subtractEnergyCost(2);
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
}
}

View file

@ -16,6 +16,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.TimeControlAbilitySpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.util.RegistryUtils;
@ -48,6 +49,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, SpellTraits.EMPTY, DispersableDisguiseSpell::new);
public static final SpellType<RainboomAbilitySpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RainboomAbilitySpell::new);
public static final SpellType<TimeControlAbilitySpell> TIME_CONTROL = register("time_control", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, TimeControlAbilitySpell::new);
public static final SpellType<IceSpell> FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, IceSpell.DEFAULT_TRAITS, IceSpell::new);
public static final SpellType<ChillingBreathSpell> CHILLING_BREATH = register("chilling_breath", Affinity.NEUTRAL, 0xFFEAFF, true, ChillingBreathSpell.DEFAULT_TRAITS, ChillingBreathSpell::new);

View file

@ -137,7 +137,9 @@ public class UHud {
slots.forEach(slot -> slot.renderBackground(context, abilities, swap, tickDelta));
if (pony.getObservedSpecies().canCast()) {
boolean canCast = Abilities.CAST.canUse(pony.getCompositeRace());
if (canCast) {
Ability<?> ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY)
.getAbility(Unicopia.getConfig().hudPage.get())
.orElse(null);
@ -168,7 +170,7 @@ public class UHud {
matrices.pop();
if (pony.getObservedSpecies().canCast()) {
if (canCast) {
renderSpell(context, pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 10 - xDirection * 13, hudY + 2);
renderSpell(context, pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 8 - xDirection * 2, hudY - 6);
}

View file

@ -312,7 +312,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
if (isBeingCarried()) {
Pony carrier = Pony.of(entity.getVehicle()).orElse(null);
if (!carrier.getCompositeRace().any(Abilities.CARRY::canUse)) {
if (!Abilities.CARRY.canUse(carrier.getCompositeRace())) {
entity.stopRiding();
entity.refreshPositionAfterTeleport(carrier.getOriginVector());
Living.transmitPassengers(carrier.asEntity());

View file

@ -336,7 +336,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
double horizontalSpeed = this.getHorizontalMotion();
double verticalSpeed = velocity.y;
if (Abilities.RAINBOOM.canUse(pony.getActualSpecies()) && horizontalSpeed != 0 && verticalSpeed < -0.3F && (verticalSpeed / horizontalSpeed) < -0.3F) {
if (Abilities.RAINBOOM.canUse(pony.getCompositeRace()) && horizontalSpeed != 0 && verticalSpeed < -0.3F && (verticalSpeed / horizontalSpeed) < -0.3F) {
ticksDiving++;
} else {
ticksDiving = 0;

View file

@ -199,6 +199,9 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
if (AmuletSelectors.ALICORN_AMULET.test(entity)) {
return Race.ALICORN;
}
if (AmuletSelectors.UNICORN_AMULET.test(entity)) {
return Race.UNICORN;
}
return getObservedSpecies();
}

View file

@ -146,7 +146,7 @@ public interface UItems {
AmuletItem UNICORN_AMULET = register("unicorn_amulet", new AmuletItem(new FabricItemSettings()
.maxCount(1)
.maxDamage(890)
.rarity(Rarity.UNCOMMON), 900), ItemGroups.TOOLS);
.rarity(Rarity.UNCOMMON), 0), ItemGroups.TOOLS);
GlassesItem SUNGLASSES = register("sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);
GlassesItem BROKEN_SUNGLASSES = register("broken_sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB