Added a feather fall spell

This commit is contained in:
Sollace 2021-11-21 18:50:02 +02:00
parent cbc8c6a6de
commit 7d830a3474
10 changed files with 198 additions and 26 deletions

View file

@ -0,0 +1,129 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class FeatherFallSpell extends AbstractSpell {
private static final int MIN_RANGE = 1;
private static final int MAX_RANGE = 20;
private static final int MIN_TARGETS = 1;
private static final int MAX_TARGETS = 19;
private static final float FOCUS_RANGE_WEIGHT = 0.1F;
private static final float POWERS_RANGE_WEIGHT = 0.3F;
private static final float MAX_GENEROSITY_FACTOR = 19F;
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.FOCUS, 80)
.with(Trait.POWER, 10)
.with(Trait.AIR, 0.1F)
.with(Trait.KINDNESS, 90)
.with(Trait.ORDER, 15)
.build();
private int duration;
protected FeatherFallSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
duration = (int)(traits.get(Trait.FOCUS, 0, 160) * 19);
}
@Override
public boolean tick(Caster<?> caster, Situation situation) {
if (duration-- <= 0) {
return false;
}
List<Entity> targets = getTargets(caster).toList();
if (targets.isEmpty()) {
return true;
}
final float strength = 1F / (getTraits().get(Trait.STRENGTH, 2, 9) / targets.size());
final float generosity = getTraits().get(Trait.GENEROSITY, 1, MAX_GENEROSITY_FACTOR) / MAX_GENEROSITY_FACTOR;
Entity master = caster.getMaster();
Entity entity = caster.getEntity();
Vec3d masterVelocity = caster.getEntity().getVelocity().multiply(0.1);
targets.forEach(target -> {
if (target.getVelocity().y < 0) {
boolean isSelf = target == master || target == entity;
float delta = strength * (isSelf ? (1F - generosity) : generosity);
if (!isSelf || generosity < 0.5F) {
target.verticalCollision = true;
target.setOnGround(true);
target.fallDistance = 0;
}
if (target instanceof PlayerEntity) {
((PlayerEntity)target).getAbilities().flying = false;
}
target.setVelocity(target.getVelocity().multiply(1, delta, 1));
if (situation == Situation.PROJECTILE && target != caster.getEntity()) {
target.addVelocity(masterVelocity.x, 0, masterVelocity.z);
}
}
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), target, 7);
});
return caster.subtractEnergyCost(duration % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
}
protected double getCostPerEntity() {
float focus = Math.max(getTraits().get(Trait.FOCUS), 80) - 80;
float power = Math.max(getTraits().get(Trait.POWER), 10) - 10;
return MathHelper.clamp((power * POWERS_RANGE_WEIGHT) - (focus * FOCUS_RANGE_WEIGHT), 1, 7);
}
protected double getEffectRange() {
float power = getTraits().get(Trait.POWER) - 10;
return MathHelper.clamp(power * POWERS_RANGE_WEIGHT, MIN_RANGE, MAX_RANGE);
}
protected long getMaxTargets() {
long generosity = (long)getTraits().get(Trait.GENEROSITY) * 2L;
long focus = (long)getTraits().get(Trait.FOCUS, MIN_TARGETS, MAX_TARGETS) * 2L;
return generosity + focus;
}
protected Stream<Entity> getTargets(Caster<?> caster) {
Entity owner = caster.getEntity();// , EquinePredicates.IS_PLAYER
return Stream.concat(Stream.of(owner), caster.findAllEntitiesInRange(getEffectRange()).sorted((a, b) -> {
return Integer.compare(
FriendshipBraceletItem.isComrade(caster, a) ? 1 : 0,
FriendshipBraceletItem.isComrade(caster, b) ? 1 : 0
);
}).distinct()).limit(getMaxTargets());
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("duration", duration);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
duration = compound.getInt("duration");
}
}

View file

@ -31,7 +31,7 @@ import net.minecraft.util.registry.Registry;
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
public static final Identifier EMPTY_ID = new Identifier("unicopia", "null");
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, (t, c) -> null);
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, SpellTraits.EMPTY, (t, c) -> null);
private static final Registry<SpellType<?>> REGISTRY = Registries.createSimple(new Identifier("unicopia", "spells"));
private static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
@ -57,6 +57,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<RevealingSpell> REVEALING = register("reveal", Affinity.GOOD, 0x5CE81F, true, RevealingSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0xE1239C, true, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.NEUTRAL, 0x3A59AA, true, TransformationSpell::new);
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new);
private final Identifier id;
private final Affinity affinity;
@ -69,14 +70,16 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
private String translationKey;
private final CustomisedSpellType<T> traited;
private final SpellTraits traits;
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
this.id = id;
this.affinity = affinity;
this.color = color;
this.obtainable = obtainable;
this.factory = factory;
traited = new CustomisedSpellType<>(this, SpellTraits.EMPTY);
this.traits = traits;
traited = new CustomisedSpellType<>(this, traits);
}
public boolean isObtainable() {
@ -99,6 +102,10 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return affinity;
}
public SpellTraits getTraits() {
return traits;
}
public String getTranslationKey() {
if (translationKey == null) {
translationKey = Util.createTranslationKey(getAffinity().getTranslationKey(), getId());
@ -153,15 +160,19 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return this == EMPTY_KEY;
}
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
SpellType<T> type = new SpellType<>(id, affinity, color, obtainable, factory);
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
SpellType<T> type = new SpellType<>(id, affinity, color, obtainable, traits, factory);
byAffinity(affinity).add(type);
Registry.register(REGISTRY, id, type);
return type;
}
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
return register(new Identifier("unicopia", name), affinity, color, obtainable, factory);
return register(name, affinity, color, obtainable, SpellTraits.EMPTY, factory);
}
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
return register(new Identifier("unicopia", name), affinity, color, obtainable, traits, factory);
}
@SuppressWarnings("unchecked")

View file

@ -157,6 +157,10 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
public ItemStack applyTo(ItemStack stack) {
stack = stack.copy();
if (isEmpty()) {
stack.removeSubTag("spell_traits");
return stack;
}
stack.getOrCreateTag().put("spell_traits", toNbt());
return stack;
}
@ -235,4 +239,17 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
.filter(e -> e.getValue() != 0)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a + b, () -> new EnumMap<>(Trait.class)));
}
public static final class Builder {
private final Map<Trait, Float> traits = new EnumMap<>(Trait.class);
public Builder with(Trait trait, float amount) {
traits.put(trait, amount);
return this;
}
public SpellTraits build() {
return fromEntries(traits.entrySet().stream()).orElse(SpellTraits.EMPTY);
}
}
}

View file

@ -10,7 +10,15 @@ import java.util.stream.Collectors;
import net.minecraft.util.Identifier;
public enum Trait {
/**
* Imparts physical strength or enhances endurance.
* Spells with more of the strength trait hit harder and last longer.
*/
STRENGTH(TraitGroup.NATURAL),
/**
* Narrows a spell to focus its energy more effectively.
* Adding the focus trait to spells will decrease the cost of its effects whilst extending its range to more targets in cases of multi-target spells.
*/
FOCUS(TraitGroup.NATURAL),
KNOWLEDGE(TraitGroup.NATURAL),
LIFE(TraitGroup.NATURAL),
@ -27,6 +35,13 @@ public enum Trait {
KINDNESS(TraitGroup.MAGICAL),
HAPPINESS(TraitGroup.MAGICAL),
/**
* Causes a spell to favor others over the caster.
* Can be used to increase range and power, but to the detriment to the caster.
*
* Complemented by the Element of Harmony and the Element of Kindness.
* Spells with this trait are better suited to lending aid to those in need.
*/
GENEROSITY(TraitGroup.MAGICAL),
DARKNESS(TraitGroup.DARKNESS),

View file

@ -72,6 +72,10 @@ public class ModifierTooltipRenderer implements ItemTooltipCallback {
}
});
}
if (MinecraftClient.getInstance().player != null) {
Pony.of(MinecraftClient.getInstance().player).getDiscoveries().appendTooltip(stack, MinecraftClient.getInstance().world, lines);
}
}
private int getInsertPosition(ItemStack stack, Text category, int flags, List<Text> lines, boolean advanced) {

View file

@ -28,16 +28,16 @@ public class ItemTraitsTooltipRenderer extends BaseText implements OrderedText,
@Override
public int getHeight() {
return getRows() * 8 + 2 + 4;
return getRows() * 8 + 4;
}
@Override
public int getWidth(TextRenderer textRenderer) {
return getColumns() * 8 + 2;
return getColumns() * 17 + 2;
}
private int getColumns() {
return Math.max(2, (int)Math.ceil(Math.sqrt(traits.entries().size() + 1)));
return Math.max(4, (int)Math.ceil(Math.sqrt(traits.entries().size() + 1)));
}
private int getRows() {
@ -52,20 +52,17 @@ public class ItemTraitsTooltipRenderer extends BaseText implements OrderedText,
var entries = traits.stream().toList();
for (int i = 0; i < entries.size(); i++) {
int xx = x + (i % columns) * 10;
int xx = x + (i % columns) * 17;
int yy = y + (i / columns) * 10;
Entry<Trait, Float> entry = entries.get(i);
RenderSystem.setShaderTexture(0, entry.getKey().getSprite());
DrawableHelper.drawTexture(matrices, xx, yy, 1, 0, 0, 8, 8, 8, 8);
String string = String.format("%.2f", entry.getValue());
String string = entry.getValue() > 99 ? "99+" : Math.round(entry.getValue()) + "";
matrices.push();
xx += 19 - 2 - textRenderer.getWidth(string);
yy += 6 + 3;
matrices.translate(xx, yy, itemRenderer.zOffset + 200.0F);
matrices.translate(xx + 9, yy + 3, itemRenderer.zOffset + 200.0F);
matrices.scale(0.5F, 0.5F, 1);
VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
textRenderer.draw(string, 0, 0, 16777215, true, matrices.peek().getModel(), immediate, false, 0, 15728880);

View file

@ -36,6 +36,7 @@ public class GemstoneItem extends Item {
@Override
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> lines, TooltipContext tooltipContext) {
super.appendTooltip(stack, world, lines, tooltipContext);
if (isEnchanted(stack)) {
SpellType<?> key = getSpellKey(stack);
@ -94,7 +95,7 @@ public class GemstoneItem extends Item {
return TypedActionResult.fail(null);
}
if (key.isEmpty() || (test == null || !test.test(key))) {
if (key.isEmpty() || (test != null && !test.test(key))) {
return TypedActionResult.fail(null);
}
@ -121,7 +122,7 @@ public class GemstoneItem extends Item {
public static ItemStack enchanted(ItemStack stack, SpellType<?> type, Affinity affinity) {
stack.getOrCreateTag().putString("spell", type.getId().toString());
return stack;
return type.getTraits().applyTo(stack);
}
public static ItemStack unenchanted(ItemStack stack) {

View file

@ -8,7 +8,6 @@ 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.entity.player.Pony;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import net.minecraft.client.MinecraftClient;
@ -23,8 +22,5 @@ abstract class MixinItem implements ToxicHolder {
@Inject(method = "appendTooltip", at = @At("RETURN"))
private void onAppendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext context, CallbackInfo into) {
getToxic().ifPresent(t -> t.getAilmentFor(MinecraftClient.getInstance().player).appendTooltip(tooltip, context));
if (MinecraftClient.getInstance().player != null) {
Pony.of(MinecraftClient.getInstance().player).getDiscoveries().appendTooltip(stack, world, tooltip);
}
}
}

View file

@ -39,7 +39,6 @@ public interface ParticleSource extends ParticleSpawner {
.forEach(particleSpawner);
}
@Override
default void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity) {
getWorld().addParticle(effect, position.x, position.y, position.z, velocity.x, velocity.y, velocity.z);

View file

@ -171,20 +171,23 @@
"spell.unicopia.transformation": "Transformation",
"spell.unicopia.transformation.lore": "Chaos II",
"spell.unicopia.feather_fall": "Feather Fall",
"spell.unicopia.feather_fall.lore": "Air I",
"gui.unicopia.trait.label": "Element of %s",
"gui.unicopia.trait.group": "\n %s",
"gui.unicopia.trait.corruption": "\n %s corruption",
"trait.unicopia.strength.name": "Strength",
"trait.unicopia.strength.description": "An ebued trait of the Earth\nMay be used to impose force or enhance the endurance of some spells.",
"trait.unicopia.strength.description": "Imparts physical strength or enhances endurance.\nSpells with more of the strength trait hit harder and last longer.",
"trait.unicopia.focus.name": "Focus",
"trait.unicopia.focus.description": "Narrows a spell to focus its energy more effectively.\nAdding the focus trait to spells will decrease the cost of its effects whilst extending its range to more targets in cases of multi-target spells.",
"trait.unicopia.knowledge.name": "Knowledge",
"trait.unicopia.knowledge.description": "A mechanical harvest born of machinery and technology.\nSome spells require a little...ingenuity.",
"trait.unicopia.power.name": "Power",
"trait.unicopia.power.description": "Force for force's sake.\nExtends or ehanced a spell's natural duration.",
"trait.unicopia.blood.name": "Blood",
"trait.unicopia.blood.description": "Blood for the blood god",
"trait.unicopia.focus.name": "Focus",
"trait.unicopia.focus.description": "Blood for the blood god",
"trait.unicopia.water.name": "Water",
"trait.unicopia.water.description": "Embodies the first natural element. Counter to the Element of Fire.",
"trait.unicopia.earth.name": "Earth",
@ -208,7 +211,7 @@
"trait.unicopia.kindness.name": "Kindness",
"trait.unicopia.kindness.description": "Complemented by the Element of Harmony and the Element of Laughter, wants nothing more than to bring happiness into this world.",
"trait.unicopia.generosity.name": "Generosity",
"trait.unicopia.generosity.description": "Complemented by the Element of Harmony and the Element of Kindness. Spells with this trait are better ruited to lending aid to those in need.",
"trait.unicopia.generosity.description": "Causes a spell to favor others over the caster.\nCan be used to increase range and power, but to the detriment to the caster.\n\nComplemented by the Element of Harmony and the Element of Kindness.\nSpells with this trait are better suited to lending aid to those in need.",
"trait.unicopia.rot.name": "Rot",
"trait.unicopia.rot.description": "Death and destruction enter this world. All will die, all must die. It has been written and so shall it be.",
"trait.unicopia.life.name": "Life",