Implement attribute tooltips for all spells

This commit is contained in:
Sollace 2024-05-24 16:43:11 +01:00
parent d1b3e8702a
commit 79d97adf0b
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
26 changed files with 357 additions and 90 deletions

View file

@ -1,9 +1,21 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import net.minecraft.text.Text;
public abstract class AbstractAreaEffectSpell extends AbstractSpell {
public static void appendRangeTooltip(CustomisedSpellType<?> type, List<Text> tooltip) {
float addedRange = type.traits().get(Trait.POWER);
if (addedRange != 0) {
tooltip.add(SpellAttributes.ofRelative(SpellAttributes.RANGE, addedRange));
}
}
protected AbstractAreaEffectSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -12,4 +24,8 @@ public abstract class AbstractAreaEffectSpell extends AbstractSpell {
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
return toPlaceable();
}
protected final float getAdditionalRange() {
return (int)getTraits().get(Trait.POWER);
}
}

View file

@ -36,7 +36,7 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
@Override
public void onSuppressed(Caster<?> otherSource, float time) {
time /= getTraits().getOrDefault(Trait.STRENGTH, 1);
suppressionCounter = (int)(100 * time);
suppressionCounter = (int)time;
setDirty();
}

View file

@ -0,0 +1,64 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.StringHelper;
import net.minecraft.util.Util;
public interface SpellAttributes {
Text CAST_ON_LOCATION = of(Unicopia.id("cast_on_location"));
Text CAST_ON_PERSON = of(Unicopia.id("cast_on_person"));
Text TARGET_ENTITY = of(Unicopia.id("focused_entity"));
Text FOLLOWS_TARGET = of(Unicopia.id("follows_target"));
Text PERMIT_ITEMS = of(Unicopia.id("permit_items"));
Text PERMIT_PASSIVE = of(Unicopia.id("permit_passive"));
Text PERMIT_HOSTILE = of(Unicopia.id("permit_hostile"));
Text PERMIT_PLAYER = of(Unicopia.id("permit_player"));
Identifier RANGE = Unicopia.id("range");
Identifier DURATION = Unicopia.id("duration");
Identifier STRENGTH = Unicopia.id("strength");
Identifier VELOCITY = Unicopia.id("velocity");
Identifier VERTICAL_VELOCITY = Unicopia.id("vertical_velocity");
Identifier DAMAGE_TO_TARGET = Unicopia.id("damage_to_target");
Identifier SIMULTANIOUS_TARGETS = Unicopia.id("simultanious_targets");
Identifier COST_PER_INDIVIDUAL = Unicopia.id("cost_per_individual");
Identifier EXPLOSION_STRENGTH = Unicopia.id("explosion_strength");
Identifier PROJECTILE_COUNT = Unicopia.id("projectile_count");
Identifier ORB_COUNT = Unicopia.id("orb_count");
Identifier WAVE_SIZE = Unicopia.id("wave_size");
Identifier FOLLOW_RANGE = Unicopia.id("follow_range");
Identifier SOAPINESS = Unicopia.id("soapiness");
Identifier TARGET_PREFERENCE = Unicopia.id("target_preference");
Identifier CASTER_PREFERENCE = Unicopia.id("caster_preference");
static Text of(Identifier id) {
return Text.literal(" ").append(Text.translatable(Util.createTranslationKey("spell_attribute", id))).formatted(Formatting.LIGHT_PURPLE);
}
static Text of(Identifier id, float value) {
return Text.literal(" ").append(
Text.translatable("attribute.modifier.equals.0",
ItemStack.MODIFIER_FORMAT.format(value),
Text.translatable(Util.createTranslationKey("spell_attribute", id)))
).formatted(Formatting.LIGHT_PURPLE);
}
static Text ofRelative(Identifier id, float value) {
return EffectUtils.formatModifierChange(Util.createTranslationKey("spell_attribute", id), value, false);
}
static Text ofTime(Identifier id, long time) {
return Text.literal(" ").append(Text.translatable("attribute.modifier.equals.0",
StringHelper.formatTicks((int)Math.abs(time)),
Text.translatable(Util.createTranslationKey("spell_attribute", id))
).formatted(Formatting.LIGHT_PURPLE));
}
}

View file

@ -1,17 +1,33 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
/**
* A magic effect with a set duration capable of reporting how long it has until it runs out.
*/
public interface TimedSpell extends Spell {
int BASE_DURATION = 120 * 20;
Timer getTimer();
static void appendDurationTooltip(CustomisedSpellType<?> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.ofTime(SpellAttributes.DURATION, BASE_DURATION + getExtraDuration(type.traits())));
}
static int getExtraDuration(SpellTraits traits) {
return (int)(traits.get(Trait.FOCUS, 0, 160) * 19) * 20;
}
class Timer implements Tickable, NbtSerialisable {
private int maxDuration;
private int duration;

View file

@ -5,9 +5,9 @@ import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
@ -17,7 +17,6 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
@ -28,18 +27,20 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
.build();
static void appendTooltip(CustomisedSpellType<PortalSpell> type, List<Text> tooltip) {
float addedRange = type.traits().get(Trait.POWER);
if (addedRange != 0) {
tooltip.add(EffectUtils.formatModifierChange("spell.unicopia.shield.additional_range", addedRange, false));
}
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.caston.location")).formatted(Formatting.GRAY));
static void appendTooltip(CustomisedSpellType<AreaProtectionSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.CAST_ON_LOCATION);
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4 + type.traits().get(Trait.POWER)));
}
protected AreaProtectionSpell(CustomisedSpellType<?> type) {
super(type);
}
/*@Override
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
}*/
@Override
public boolean tick(Caster<?> source, Situation situation) {
@ -70,7 +71,7 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
private double getRange(Caster<?> source) {
float multiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2;
float min = 4 + getTraits().get(Trait.POWER);
float min = 4 + getAdditionalRange();
double range = (min + (source.getLevel().getScaled(4) * 2)) / multiplier;
if (source instanceof Pony && range > 2) {
range = Math.sqrt(range);

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
@ -13,9 +15,9 @@ import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.text.Text;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -26,9 +28,20 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
private final Timer timer;
public static void appendTooltip2(CustomisedSpellType<AttractiveSpell> type, List<Text> tooltip) {
TimedSpell.appendDurationTooltip(type, tooltip);
AbstractAreaEffectSpell.appendRangeTooltip(type, tooltip);
if (type.traits().get(Trait.ORDER) >= 20) {
tooltip.add(SpellAttributes.TARGET_ENTITY);
} else {
appendValidTargetsTooltip(type, tooltip);
}
appendCastLocationTooltip(type, tooltip);
}
protected AttractiveSpell(CustomisedSpellType<?> type) {
super(type);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
}
@Override
@ -73,17 +86,12 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
});
}
@Override
public double getDrawDropOffRange(Caster<?> caster) {
return 10 + (caster.getLevel().getScaled(8) * 2);
}
@Override
protected boolean isValidTarget(Caster<?> source, Entity entity) {
if (target.referenceEquals(entity)) {
return true;
}
return getTraits().get(Trait.KNOWLEDGE) > 10 ? entity instanceof ItemEntity : super.isValidTarget(source, entity);
return super.isValidTarget(source, entity);
}
@Override

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -22,6 +23,7 @@ import net.minecraft.entity.attribute.*;
import net.minecraft.entity.attribute.EntityAttributeModifier.Operation;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.text.Text;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -46,6 +48,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
.with(Trait.POWER, 1)
.build();
static void appendTooltip(CustomisedSpellType<? extends BubbleSpell> type, List<Text> tooltip) {
TimedSpell.appendDurationTooltip(type, tooltip);
tooltip.add(SpellAttributes.of(SpellAttributes.SOAPINESS, (int)(type.traits().get(Trait.POWER) * 2)));
}
private final Timer timer;
private int struggles;
@ -55,7 +62,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
protected BubbleSpell(CustomisedSpellType<?> type) {
super(type);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
struggles = (int)(getTraits().get(Trait.POWER) * 2);
}

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
@ -7,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity;
@ -17,6 +19,7 @@ import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.text.Text;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.BlockPos;
@ -37,6 +40,11 @@ public class CatapultSpell extends AbstractSpell implements ProjectileDelegate.B
private static final float HORIZONTAL_VARIANCE = 0.25F;
private static final float MAX_STRENGTH = 120;
static void appendTooltip(CustomisedSpellType<? extends CatapultSpell> type, List<Text> tooltip) {
float velocity = 0.1F + type.relativeTraits().get(Trait.STRENGTH, -MAX_STRENGTH, MAX_STRENGTH);
tooltip.add(SpellAttributes.of(SpellAttributes.VERTICAL_VELOCITY, velocity / 16F));
}
protected CatapultSpell(CustomisedSpellType<?> type) {
super(type);
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
@ -23,7 +24,8 @@ import net.minecraft.util.TypedActionResult;
public record CustomisedSpellType<T extends Spell> (
SpellType<T> type,
SpellTraits traits
SpellTraits traits,
Supplier<SpellTraits> traitsDifferenceSupplier
) implements SpellPredicate<T> {
public boolean isEmpty() {
@ -34,6 +36,10 @@ public record CustomisedSpellType<T extends Spell> (
return type().isStackable();
}
public SpellTraits relativeTraits() {
return traitsDifferenceSupplier.get();
}
public T create() {
try {
return type.getFactory().create(this);

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
@ -11,6 +13,7 @@ import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
public class DispellEvilSpell extends AbstractSpell implements ProjectileDelegate.HitListener {
@ -18,6 +21,10 @@ public class DispellEvilSpell extends AbstractSpell implements ProjectileDelegat
.with(Trait.POWER, 1)
.build();
static void appendTooltip(CustomisedSpellType<? extends DispellEvilSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, (1 + type.relativeTraits().get(Trait.POWER)) * 10));
}
protected DispellEvilSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -28,7 +35,7 @@ public class DispellEvilSpell extends AbstractSpell implements ProjectileDelegat
return !isDead();
}
source.findAllEntitiesInRange(getTraits().get(Trait.POWER) * 10, e -> e.getType() == EntityType.PHANTOM).forEach(entity -> {
source.findAllEntitiesInRange((1 + getTraits().get(Trait.POWER)) * 10, e -> e.getType() == EntityType.PHANTOM).forEach(entity -> {
entity.damage(entity.getDamageSources().magic(), 50);
if (entity instanceof LivingEntity l) {
double d = source.getOriginVector().getX() - entity.getX();

View file

@ -1,20 +1,29 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
/**
* An area-effect spell that disperses illusions.
*/
public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
public static void appendTooltip(CustomisedSpellType<? extends DisperseIllusionSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 15 + type.traits().get(Trait.POWER)));
tooltip.add(SpellAttributes.ofTime(SpellAttributes.DURATION, (1 + (long)type.traits().get(Trait.STRENGTH)) * 100));
}
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -22,7 +31,7 @@ public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
@Override
public boolean tick(Caster<?> source, Situation situation) {
float range = Math.max(0, 15 + getTraits().get(Trait.POWER));
float range = Math.max(0, 15 + getAdditionalRange());
if (range == 0) {
return false;
@ -41,7 +50,7 @@ public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
e.getSpellSlot().get(SpellPredicate.CAN_SUPPRESS)
.filter(spell -> spell.isVulnerable(source, this))
.ifPresent(spell -> {
spell.onSuppressed(source, 1 + getTraits().get(Trait.STRENGTH));
spell.onSuppressed(source, 100 * (1 + getTraits().get(Trait.STRENGTH)));
e.playSound(USounds.SPELL_ILLUSION_DISPERSE, 0.2F, 0.5F);
});
});

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.*;
@ -11,11 +13,19 @@ import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d;
public class DisplacementSpell extends AbstractSpell implements HomingSpell, ProjectileDelegate.EntityHitListener {
static void appendTooltip(CustomisedSpellType<? extends DisplacementSpell> type, List<Text> tooltip) {
float damage = type.traits().get(Trait.BLOOD);
if (damage > 0) {
tooltip.add(SpellAttributes.ofRelative(SpellAttributes.DAMAGE_TO_TARGET, damage));
}
}
private final EntityReference<Entity> target = new EntityReference<>();
private int ticks = 10;

View file

@ -5,6 +5,7 @@ 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.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
@ -15,6 +16,7 @@ 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.text.Text;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -36,6 +38,17 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
.with(Trait.ORDER, 15)
.build();
public static void appendTooltip(CustomisedSpellType<FeatherFallSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.ofTime(SpellAttributes.DURATION, 10 + (int)(type.traits().get(Trait.FOCUS, 0, 160))));
tooltip.add(SpellAttributes.of(SpellAttributes.STRENGTH, type.traits().get(Trait.STRENGTH, 2, 9)));
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, (float)getEffectRange(type.traits())));
tooltip.add(SpellAttributes.of(SpellAttributes.SIMULTANIOUS_TARGETS, getMaxTargets(type.traits())));
tooltip.add(SpellAttributes.of(SpellAttributes.COST_PER_INDIVIDUAL, (float)getCostPerEntity(type.traits())));
float generosity = type.traits().get(Trait.GENEROSITY, 1, MAX_GENEROSITY_FACTOR) / MAX_GENEROSITY_FACTOR;
tooltip.add(SpellAttributes.of(SpellAttributes.TARGET_PREFERENCE, (int)(generosity * 100)));
tooltip.add(SpellAttributes.of(SpellAttributes.CASTER_PREFERENCE, (int)((1 - generosity) * 100)));
}
private final Timer timer;
protected FeatherFallSpell(CustomisedSpellType<?> type) {
@ -91,35 +104,33 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), target, 7);
});
return caster.subtractEnergyCost(timer.getTicksRemaining() % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
return caster.subtractEnergyCost(timer.getTicksRemaining() % 50 == 0 ? getCostPerEntity(getTraits()) * 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;
protected static double getCostPerEntity(SpellTraits traits) {
float focus = Math.max(traits.get(Trait.FOCUS), 80) - 80;
float power = Math.max(traits.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 static double getEffectRange(SpellTraits traits) {
return MathHelper.clamp((traits.get(Trait.POWER) - 10) * 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;
protected static long getMaxTargets(SpellTraits traits) {
long generosity = (long)traits.get(Trait.GENEROSITY) * 2L;
long focus = (long)traits.get(Trait.FOCUS, MIN_TARGETS, MAX_TARGETS) * 2L;
return generosity + focus;
}
protected Stream<Entity> getTargets(Caster<?> caster) {
return Stream.concat(Stream.of(caster.asEntity()), caster.findAllEntitiesInRange(getEffectRange()).sorted((a, b) -> {
return Stream.concat(Stream.of(caster.asEntity()), caster.findAllEntitiesInRange(getEffectRange(getTraits())).sorted((a, b) -> {
return Integer.compare(
FriendshipBraceletItem.isComrade(caster, a) ? 1 : 0,
FriendshipBraceletItem.isComrade(caster, b) ? 1 : 0
);
}).distinct()).limit(getMaxTargets());
}).distinct()).limit(getMaxTargets(getTraits()));
}
@Override

View file

@ -1,9 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.HomingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference;
@ -14,6 +17,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.text.Text;
import net.minecraft.util.hit.EntityHitResult;
public class FireBoltSpell extends AbstractSpell implements HomingSpell,
@ -31,6 +35,19 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell,
.with(Trait.FIRE, 60)
.build();
public static void appendTooltip(CustomisedSpellType<? extends FireBoltSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.EXPLOSION_STRENGTH, type.traits().get(Trait.POWER, 0, type.traits().get(Trait.FOCUS) >= 50 ? 500 : 50) / 10F));
tooltip.add(SpellAttributes.of(SpellAttributes.VELOCITY, 1.3F + type.traits().get(Trait.STRENGTH) / 11F));
tooltip.add(SpellAttributes.of(SpellAttributes.PROJECTILE_COUNT, 1 + (int)type.traits().get(Trait.EARTH) * 3));
float homingRange = type.traits().get(Trait.FOCUS);
if (homingRange >= 50) {
tooltip.add(SpellAttributes.FOLLOWS_TARGET);
tooltip.add(SpellAttributes.of(SpellAttributes.FOLLOW_RANGE, homingRange - 50));
}
}
private final EntityReference<Entity> target = new EntityReference<>();
protected FireBoltSpell(CustomisedSpellType<?> type) {

View file

@ -1,9 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
@ -25,6 +28,7 @@ import net.minecraft.entity.damage.DamageTypes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.text.Text;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
@ -41,6 +45,10 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileDele
.with(Trait.FIRE, 15)
.build();
public static void appendTooltip(CustomisedSpellType<? extends FireSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4 + type.traits().get(Trait.POWER)));
}
protected FireSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -67,14 +75,14 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileDele
generateParticles(source);
}
return new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))).translate(source.getOrigin()).getBlockPositions().reduce(false,
return new Sphere(false, Math.max(0, 4 + getAdditionalRange())).translate(source.getOrigin()).getBlockPositions().reduce(false,
(r, i) -> source.canModifyAt(i) && applyBlocks(source.asWorld(), i),
(a, b) -> a || b)
|| applyEntities(source, source.getOriginVector());
}
protected void generateParticles(Caster<?> source) {
source.spawnParticles(new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))), (int)(1 + source.getLevel().getScaled(8)) * 6, pos -> {
source.spawnParticles(new Sphere(false, Math.max(0, 4 + getAdditionalRange())), (int)(1 + source.getLevel().getScaled(8)) * 6, pos -> {
source.addParticle(ParticleTypes.LARGE_SMOKE, pos, Vec3d.ZERO);
});
}
@ -122,7 +130,7 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileDele
}
protected boolean applyEntities(Caster<?> source, Vec3d pos) {
return source.findAllEntitiesInRange(Math.max(0, 3 + getTraits().get(Trait.POWER)), e -> {
return source.findAllEntitiesInRange(Math.max(0, 3 + getAdditionalRange()), e -> {
LivingEntity master = source.getMaster();
return (!(e.equals(source.asEntity()) || e.equals(master)) ||
(master instanceof PlayerEntity && !EquinePredicates.PLAYER_UNICORN.test(master))) && !(e instanceof ItemEntity)

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.minelittlepony.unicopia.USounds;
@ -8,6 +9,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.advancement.UCriteria;
@ -22,6 +24,7 @@ import net.minecraft.block.*;
import net.minecraft.fluid.*;
import net.minecraft.nbt.*;
import net.minecraft.state.property.Properties;
import net.minecraft.text.Text;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
@ -34,6 +37,10 @@ public class HydrophobicSpell extends AbstractSpell {
.with(Trait.FOCUS, 5)
.with(Trait.KNOWLEDGE, 1)
.build();
static void appendTooltip(CustomisedSpellType<? extends HydrophobicSpell> type, List<Text> tooltip) {
ShieldSpell.appendCastLocationTooltip(type, tooltip);
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4 + type.traits().get(Trait.POWER)));
}
private final TagKey<Fluid> affectedFluid;

View file

@ -6,6 +6,7 @@ import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
@ -20,6 +21,7 @@ import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.text.Text;
public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileDelegate.HitListener {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
@ -29,13 +31,18 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
.with(Trait.ORDER, 25)
.build();
public static void appendTooltip(CustomisedSpellType<? extends LightSpell> type, List<Text> tooltip) {
TimedSpell.appendDurationTooltip(type, tooltip);
tooltip.add(SpellAttributes.of(SpellAttributes.ORB_COUNT, 2 + (int)(type.relativeTraits().get(Trait.LIFE, 10, 20) / 10F)));
}
private final Timer timer;
private final List<EntityReference<FairyEntity>> lights = new ArrayList<>();
protected LightSpell(CustomisedSpellType<?> type) {
super(type);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
}
@Override

View file

@ -1,18 +1,25 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
public class MimicSpell extends AbstractDisguiseSpell implements HomingSpell, TimedSpell {
static final int BASE_DURATION = 120 * 20;
public static void appendTooltip(CustomisedSpellType<? extends MimicSpell> type, List<Text> tooltip) {
TimedSpell.appendDurationTooltip(type, tooltip);
}
private final Timer timer;
protected MimicSpell(CustomisedSpellType<?> type) {
super(type);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
}
@Override

View file

@ -8,6 +8,7 @@ import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.EntityReference;
@ -31,6 +32,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.text.Text;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.BlockPos;
@ -79,6 +81,11 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
return e -> e.getType() == type;
}
static void appendTooltip(CustomisedSpellType<? extends NecromancySpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4 + type.traits().get(Trait.POWER)));
tooltip.add(SpellAttributes.of(SpellAttributes.WAVE_SIZE, 10 + (int)type.traits().get(Trait.CHAOS, 0, 10)));
}
private final List<EntityReference<LivingEntity>> summonedEntities = new ArrayList<>();
private int spawnCountdown;
@ -90,7 +97,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
@Override
public boolean tick(Caster<?> source, Situation situation) {
float radius = 4 + source.getLevel().getScaled(4) * 4 + getTraits().get(Trait.POWER);
float radius = 4 + source.getLevel().getScaled(4) * 4 + getAdditionalRange();
if (radius <= 0) {
return false;

View file

@ -36,7 +36,7 @@ public class ScorchSpell extends FireSpell implements ProjectileDelegate.Configu
BlockPos pos = PosHelper.findSolidGroundAt(source.asWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());
if (source.canModifyAt(pos) && StateMaps.FIRE_AFFECTED.convert(source.asWorld(), pos)) {
source.spawnParticles(new Sphere(false, Math.max(1, getTraits().get(Trait.POWER))), 5, p -> {
source.spawnParticles(new Sphere(false, Math.max(1, getAdditionalRange())), 5, p -> {
source.addParticle(ParticleTypes.SMOKE, PosHelper.offset(p, pos), Vec3d.ZERO);
});
}

View file

@ -7,13 +7,14 @@ import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -27,6 +28,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EyeOfEnderEntity;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.TntEntity;
import net.minecraft.entity.Entity.RemovalReason;
@ -37,7 +39,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -49,27 +50,32 @@ public class ShieldSpell extends AbstractSpell {
.with(Trait.AIR, 9)
.build();
static void appendTooltip(CustomisedSpellType<ShieldSpell> type, List<Text> tooltip) {
float addedRange = type.traits().get(Trait.POWER);
if (addedRange != 0) {
tooltip.add(EffectUtils.formatModifierChange("spell.unicopia.shield.additional_range", addedRange, false));
}
if (type.traits().get(Trait.LIFE) > 0) {
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.permit.passive")).formatted(Formatting.GRAY));
}
if (type.traits().get(Trait.BLOOD) > 0) {
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.permit.hostile")).formatted(Formatting.GRAY));
}
if (type.traits().get(Trait.ICE) > 0) {
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.permit.player")).formatted(Formatting.GRAY));
}
if (type.traits().get(Trait.GENEROSITY) > 0) {
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.caston.location")).formatted(Formatting.GRAY));
static void appendTooltip(CustomisedSpellType<? extends ShieldSpell> type, List<Text> tooltip) {
AbstractAreaEffectSpell.appendRangeTooltip(type, tooltip);
appendValidTargetsTooltip(type, tooltip);
appendCastLocationTooltip(type, tooltip);
}
static void appendValidTargetsTooltip(CustomisedSpellType<? extends ShieldSpell> type, List<Text> tooltip) {
if (type.traits().get(Trait.KNOWLEDGE) > 10) {
tooltip.add(SpellAttributes.PERMIT_ITEMS);
} else {
tooltip.add(Text.literal(" ").append(Text.translatable("spell.unicopia.shield.caston.person")).formatted(Formatting.GRAY));
if (type.traits().get(Trait.LIFE) > 0) {
tooltip.add(SpellAttributes.PERMIT_PASSIVE);
}
if (type.traits().get(Trait.BLOOD) > 0) {
tooltip.add(SpellAttributes.PERMIT_HOSTILE);
}
if (type.traits().get(Trait.ICE) > 0) {
tooltip.add(SpellAttributes.PERMIT_PLAYER);
}
}
}
static void appendCastLocationTooltip(CustomisedSpellType<?> type, List<Text> tooltip) {
tooltip.add(type.traits().get(Trait.GENEROSITY) > 0 ? SpellAttributes.CAST_ON_LOCATION : SpellAttributes.CAST_ON_PERSON);
}
protected final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget);
private final Lerp radius = new Lerp(0);
@ -177,6 +183,11 @@ public class ShieldSpell extends AbstractSpell {
}
protected boolean isValidTarget(Caster<?> source, Entity entity) {
if (getTraits().get(Trait.KNOWLEDGE) > 10) {
return entity instanceof ItemEntity;
}
boolean valid = (entity instanceof LivingEntity
|| entity instanceof TntEntity
|| entity instanceof FallingBlockEntity

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -28,6 +29,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
@ -37,6 +39,10 @@ import net.minecraft.util.math.Vec3d;
public class SiphoningSpell extends AbstractAreaEffectSpell {
static final Predicate<Entity> TARGET_PREDICATE = EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(EntityPredicates.VALID_LIVING_ENTITY);
static void appendTooltip(CustomisedSpellType<? extends SiphoningSpell> type, List<Text> tooltip) {
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4));
}
private int ticksUpset;
protected SiphoningSpell(CustomisedSpellType<?> type) {
@ -56,7 +62,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell {
}
if (source.isClient()) {
float radius = 4 + source.getLevel().getScaled(5);
float radius = 4 + source.getLevel().getScaled(5) + getAdditionalRange();
int direction = isFriendlyTogether(source) ? 1 : -1;
source.spawnParticles(new Sphere(true, radius, 1, 0, 1), 1, pos -> {

View file

@ -4,6 +4,7 @@ import java.util.List;
import java.util.function.BiConsumer;
import org.jetbrains.annotations.Nullable;
import com.google.common.base.Suppliers;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Affine;
@ -54,29 +55,29 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<IceSpell> FROST = register("frost", builder(IceSpell::new).color(0xEABBFF).shape(GemstoneItem.Shape.TRIANGLE).traits(IceSpell.DEFAULT_TRAITS));
public static final SpellType<ChillingBreathSpell> CHILLING_BREATH = register("chilling_breath", builder(ChillingBreathSpell::new).affinity(Affinity.NEUTRAL).color(0xFFEAFF).shape(GemstoneItem.Shape.TRIANGLE).traits(ChillingBreathSpell.DEFAULT_TRAITS));
public static final SpellType<ScorchSpell> SCORCH = register("scorch", builder(ScorchSpell::new).affinity(Affinity.BAD).color(0xF8EC1F).stackable().shape(GemstoneItem.Shape.FLAME).traits(ScorchSpell.DEFAULT_TRAITS));
public static final SpellType<FireSpell> FLAME = register("flame", builder(FireSpell::new).color(0xFFBB99).shape(GemstoneItem.Shape.FLAME).traits(FireSpell.DEFAULT_TRAITS));
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", builder(InfernoSpell::new).affinity(Affinity.BAD).color(0xFFAA00).shape(GemstoneItem.Shape.FLAME).traits(InfernoSpell.DEFAULT_TRAITS));
public static final SpellType<ScorchSpell> SCORCH = register("scorch", builder(ScorchSpell::new).affinity(Affinity.BAD).color(0xF8EC1F).stackable().shape(GemstoneItem.Shape.FLAME).traits(ScorchSpell.DEFAULT_TRAITS).tooltip(FireSpell::appendTooltip));
public static final SpellType<FireSpell> FLAME = register("flame", builder(FireSpell::new).color(0xFFBB99).shape(GemstoneItem.Shape.FLAME).traits(FireSpell.DEFAULT_TRAITS).tooltip(FireSpell::appendTooltip));
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", builder(InfernoSpell::new).affinity(Affinity.BAD).color(0xFFAA00).shape(GemstoneItem.Shape.FLAME).traits(InfernoSpell.DEFAULT_TRAITS).tooltip(FireSpell::appendTooltip));
public static final SpellType<ShieldSpell> SHIELD = register("shield", builder(ShieldSpell::new).affinity(Affinity.NEUTRAL).color(0x66CDAA).shape(GemstoneItem.Shape.SHIELD).traits(ShieldSpell.DEFAULT_TRAITS).tooltip(ShieldSpell::appendTooltip));
public static final SpellType<AreaProtectionSpell> ARCANE_PROTECTION = register("arcane_protection", builder(AreaProtectionSpell::new).affinity(Affinity.BAD).color(0x99CDAA).shape(GemstoneItem.Shape.SHIELD).traits(AreaProtectionSpell.DEFAULT_TRAITS));
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", builder(AttractiveSpell::new).affinity(Affinity.NEUTRAL).color(0xFFEA88).shape(GemstoneItem.Shape.VORTEX).traits(AttractiveSpell.DEFAULT_TRAITS));
public static final SpellType<AreaProtectionSpell> ARCANE_PROTECTION = register("arcane_protection", builder(AreaProtectionSpell::new).affinity(Affinity.BAD).color(0x99CDAA).shape(GemstoneItem.Shape.SHIELD).traits(AreaProtectionSpell.DEFAULT_TRAITS).tooltip(AreaProtectionSpell::appendTooltip));
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", builder(AttractiveSpell::new).affinity(Affinity.NEUTRAL).color(0xFFEA88).shape(GemstoneItem.Shape.VORTEX).traits(AttractiveSpell.DEFAULT_TRAITS).tooltip(AttractiveSpell::appendTooltip2));
public static final SpellType<DarkVortexSpell> DARK_VORTEX = register("dark_vortex", builder(DarkVortexSpell::new).affinity(Affinity.BAD).color(0xA33333).stackable().shape(GemstoneItem.Shape.VORTEX).traits(DarkVortexSpell.DEFAULT_TRAITS));
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", builder(NecromancySpell::new).affinity(Affinity.BAD).color(0xFA3A3A).shape(GemstoneItem.Shape.SKULL));
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", builder(SiphoningSpell::new).affinity(Affinity.NEUTRAL).color(0xFFA3AA).shape(GemstoneItem.Shape.LAMBDA));
public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", builder(DisperseIllusionSpell::new).color(0xFFFFAF).shape(GemstoneItem.Shape.CROSS));
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", builder(NecromancySpell::new).affinity(Affinity.BAD).color(0xFA3A3A).shape(GemstoneItem.Shape.SKULL).tooltip(NecromancySpell::appendTooltip));
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", builder(SiphoningSpell::new).affinity(Affinity.NEUTRAL).color(0xFFA3AA).shape(GemstoneItem.Shape.LAMBDA).tooltip(SiphoningSpell::appendTooltip));
public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", builder(DisperseIllusionSpell::new).color(0xFFFFAF).shape(GemstoneItem.Shape.CROSS).tooltip(DisperseIllusionSpell::appendTooltip));
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", builder(AwkwardSpell::new).affinity(Affinity.NEUTRAL).color(0x3A59FF).shape(GemstoneItem.Shape.ICE));
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", builder(TransformationSpell::new).color(0x19E48E).shape(GemstoneItem.Shape.BRUSH));
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", builder(FeatherFallSpell::new).color(0x00EEFF).shape(GemstoneItem.Shape.LAMBDA).traits(FeatherFallSpell.DEFAULT_TRAITS));
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", builder(CatapultSpell::new).color(0x22FF00).shape(GemstoneItem.Shape.ROCKET).traits(CatapultSpell.DEFAULT_TRAITS));
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", builder(FireBoltSpell::new).color(0xFF8811).shape(GemstoneItem.Shape.FLAME).traits(FireBoltSpell.DEFAULT_TRAITS));
public static final SpellType<LightSpell> LIGHT = register("light", builder(LightSpell::new).color(0xEEFFAA).shape(GemstoneItem.Shape.STAR).traits(LightSpell.DEFAULT_TRAITS));
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", builder(DisplacementSpell::new).affinity(Affinity.NEUTRAL).color(0x9900FF).stackable().shape(GemstoneItem.Shape.BRUSH).traits(PortalSpell.DEFAULT_TRAITS));
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", builder(FeatherFallSpell::new).color(0x00EEFF).shape(GemstoneItem.Shape.LAMBDA).traits(FeatherFallSpell.DEFAULT_TRAITS).tooltip(FeatherFallSpell::appendTooltip));
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", builder(CatapultSpell::new).color(0x22FF00).shape(GemstoneItem.Shape.ROCKET).traits(CatapultSpell.DEFAULT_TRAITS).tooltip(CatapultSpell::appendTooltip));
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", builder(FireBoltSpell::new).color(0xFF8811).shape(GemstoneItem.Shape.FLAME).traits(FireBoltSpell.DEFAULT_TRAITS).tooltip(FireBoltSpell::appendTooltip));
public static final SpellType<LightSpell> LIGHT = register("light", builder(LightSpell::new).color(0xEEFFAA).shape(GemstoneItem.Shape.STAR).traits(LightSpell.DEFAULT_TRAITS).tooltip(LightSpell::appendTooltip));
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", builder(DisplacementSpell::new).affinity(Affinity.NEUTRAL).color(0x9900FF).stackable().shape(GemstoneItem.Shape.BRUSH).traits(PortalSpell.DEFAULT_TRAITS).tooltip(DisplacementSpell::appendTooltip));
public static final SpellType<PortalSpell> PORTAL = register("portal", builder(PortalSpell::new).color(0x99FFFF).shape(GemstoneItem.Shape.RING).traits(PortalSpell.DEFAULT_TRAITS));
public static final SpellType<MimicSpell> MIMIC = register("mimic", builder(MimicSpell::new).color(0xFFFF00).shape(GemstoneItem.Shape.ARROW));
public static final SpellType<MindSwapSpell> MIND_SWAP = register("mind_swap", builder(MindSwapSpell::new).affinity(Affinity.BAD).color(0xF9FF99).shape(GemstoneItem.Shape.WAVE));
public static final SpellType<HydrophobicSpell> HYDROPHOBIC = register("hydrophobic", SpellType.<HydrophobicSpell>builder(s -> new HydrophobicSpell(s, FluidTags.WATER)).affinity(Affinity.NEUTRAL).color(0xF999FF).stackable().shape(GemstoneItem.Shape.ROCKET));
public static final SpellType<BubbleSpell> BUBBLE = register("bubble", builder(BubbleSpell::new).affinity(Affinity.NEUTRAL).color(0xF999FF).shape(GemstoneItem.Shape.DONUT).traits(BubbleSpell.DEFAULT_TRAITS));
public static final SpellType<DispellEvilSpell> DISPEL_EVIL = register("dispel_evil", builder(DispellEvilSpell::new).color(0x00FF00).shape(GemstoneItem.Shape.CROSS).traits(DispellEvilSpell.DEFAULT_TRAITS));
public static final SpellType<MimicSpell> MIMIC = register("mimic", builder(MimicSpell::new).color(0xFFFF00).shape(GemstoneItem.Shape.ARROW).tooltip(MimicSpell::appendTooltip));
public static final SpellType<MindSwapSpell> MIND_SWAP = register("mind_swap", builder(MindSwapSpell::new).affinity(Affinity.BAD).color(0xF9FF99).shape(GemstoneItem.Shape.WAVE).tooltip(MimicSpell::appendTooltip));
public static final SpellType<HydrophobicSpell> HYDROPHOBIC = register("hydrophobic", SpellType.<HydrophobicSpell>builder(s -> new HydrophobicSpell(s, FluidTags.WATER)).affinity(Affinity.NEUTRAL).color(0xF999FF).stackable().shape(GemstoneItem.Shape.ROCKET).tooltip(HydrophobicSpell::appendTooltip));
public static final SpellType<BubbleSpell> BUBBLE = register("bubble", builder(BubbleSpell::new).affinity(Affinity.NEUTRAL).color(0xF999FF).shape(GemstoneItem.Shape.DONUT).traits(BubbleSpell.DEFAULT_TRAITS).tooltip(BubbleSpell::appendTooltip));
public static final SpellType<DispellEvilSpell> DISPEL_EVIL = register("dispel_evil", builder(DispellEvilSpell::new).color(0x00FF00).shape(GemstoneItem.Shape.CROSS).traits(DispellEvilSpell.DEFAULT_TRAITS).tooltip(DispellEvilSpell::appendTooltip));
public static void bootstrap() {}
@ -109,7 +110,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
this.factory = factory;
this.traits = traits;
this.stackable = stackable;
traited = new CustomisedSpellType<>(this, traits);
traited = new CustomisedSpellType<>(this, traits, SpellTraits::empty);
defaultStack = UItems.GEMSTONE.getDefaultStack(this);
}
@ -165,7 +166,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
}
public CustomisedSpellType<T> withTraits(SpellTraits traits) {
return traits.isEmpty() ? withTraits() : new CustomisedSpellType<>(this, traits);
return traits.isEmpty() ? withTraits() : new CustomisedSpellType<>(this, traits, Suppliers.memoize(() -> traits.map((trait, value) -> value - getTraits().get(trait))));
}
public Factory<T> getFactory() {

View file

@ -60,6 +60,10 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
});
}
public static SpellTraits empty() {
return EMPTY;
}
public static Map<Identifier, SpellTraits> all() {
return new HashMap<>(REGISTRY);
}

View file

@ -6,6 +6,7 @@ import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.StringHelper;
public interface EffectUtils {
static boolean isPoisoned(LivingEntity entity) {
@ -50,6 +51,13 @@ public interface EffectUtils {
return false;
}
static Text formatModifierChange(String modifierName, int time, boolean isDetrimental) {
return Text.literal(" ").append(Text.translatable("attribute.modifier.equals.0",
StringHelper.formatTicks(Math.abs(time)),
Text.translatable(modifierName)
).formatted((isDetrimental ? time : -time) < 0 ? Formatting.DARK_GREEN : Formatting.RED));
}
static Text formatModifierChange(String modifierName, float change, boolean isDetrimental) {
return Text.literal(" ").append(Text.translatable("attribute.modifier." + (change > 0 ? "plus" : "take") + ".0",
ItemStack.MODIFIER_FORMAT.format(Math.abs(change)),

View file

@ -548,12 +548,6 @@
"spell.unicopia.fire_bolt": "Fire Bolt",
"spell.unicopia.fire_bolt.lore": "Produces several burning projectiles",
"spell.unicopia.shield": "Protection",
"spell.unicopia.shield.additional_range": "Additional Range",
"spell.unicopia.shield.permit.passive": "Permits Passive Mobs",
"spell.unicopia.shield.permit.hostile": "Permits Hostile Mobs",
"spell.unicopia.shield.permit.player": " Permits Other Players",
"spell.unicopia.shield.caston.location": "Applies to location",
"spell.unicopia.shield.caston.person": "Applies to self",
"spell.unicopia.shield.lore": "Casts a protective shield around the user",
"spell.unicopia.bubble": "Bubble",
"spell.unicopia.bubble.lore": "Traps any creature it hits in a soap bubble",
@ -590,6 +584,33 @@
"spell.unicopia.dispel_evil": "Dispel Evil",
"spell.unicopia.dispel_evil.lore": "Casts away any nearby unearthly forces",
"spell_attribute.unicopia.cast_on_location": "Applies to location",
"spell_attribute.unicopia.cast_on_person": "Applies to self",
"spell_attribute.unicopia.focused_entity": "Applies to focused entity",
"spell_attribute.unicopia.follows_target": "Follows target",
"spell_attribute.unicopia.permit_items": " Permits Items",
"spell_attribute.unicopia.permit_passive": "Permits Passive Mobs",
"spell_attribute.unicopia.permit_hostile": "Permits Hostile Mobs",
"spell_attribute.unicopia.permit_player": " Permits Other Players",
"spell_attribute.unicopia.range": "Effect Range",
"spell_attribute.unicopia.duration": "Effect Duration",
"spell_attribute.unicopia.strength": "Effect Strength",
"spell_attribute.unicopia.soapiness": "Soapiness",
"spell_attribute.unicopia.velocity": "Velocity",
"spell_attribute.unicopia.vertical_velocity": "Launch Speed",
"spell_attribute.unicopia.damage_to_target": "Damage to Target",
"spell_attribute.unicopia.simultanious_targets": "Simultanious Targets",
"spell_attribute.unicopia.cost_per_individual": "Mana cost per individual",
"spell_attribute.unicopia.explosion_strength": "Blast Strength",
"spell_attribute.unicopia.projectile_count": "Projectile Count",
"spell_attribute.unicopia.follow_range": "Following Range",
"spell_attribute.unicopia.orb_count": "Orb Count",
"spell_attribute.unicopia.wave_size": "Wave Size",
"spell_attribute.unicopia.target_preference": "Target Preference",
"spell_attribute.unicopia.caster_preference": "Caster Preference",
"trait.unicopia.strength.name": "Strength",
"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",