You can now trigger multiple abilities at the same time

This commit is contained in:
Sollace 2020-05-10 20:45:07 +02:00
parent 9006b2b295
commit 140ee68ae3
13 changed files with 105 additions and 142 deletions

View file

@ -14,8 +14,8 @@ public interface Abilities {
MutableRegistry<Ability<?>> REGISTRY = new SimpleRegistry<>();
// unicorn / alicorn
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.PRIMARY);
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.SECONDARY);
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.SECONDARY);
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY);
// earth / alicorn
Ability<?> STOMP = register(new EarthPonyStompAbility(), "stomp", AbilitySlot.PRIMARY);
@ -30,7 +30,7 @@ public interface Abilities {
// changeling
Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.PRIMARY);
Ability<?> FEED = register(new ChangelingFeedAbility(), "feed", AbilitySlot.SECONDARY);
Ability<?> TRAP = register(new ChangelingTrapAbility(), "trap", AbilitySlot.TERTIARY);
//Ability<?> TRAP = register(new ChangelingTrapAbility(), "trap", AbilitySlot.TERTIARY);
static <T extends Ability<?>> T register(T power, String name, AbilitySlot slot) {
Identifier id = new Identifier("unicopia", name);

View file

@ -60,11 +60,11 @@ public interface Ability<T extends Hit> {
* Called every tick until the warmup timer runs out.
* @param player The current player
*/
void preApply(Pony player);
void preApply(Pony player, AbilitySlot slot);
/**
* Called every tick until the cooldown timer runs out.
* @param player The current player
*/
void postApply(Pony player);
void postApply(Pony player, AbilitySlot slot);
}

View file

@ -4,8 +4,6 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -23,39 +21,22 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
private final Map<AbilitySlot, Stat> stats = new EnumMap<>(AbilitySlot.class);
/**
* True once the current ability has been triggered.
*/
private boolean triggered;
private Optional<Ability<?>> activeAbility = Optional.empty();
private AbilitySlot activeSlot = AbilitySlot.NONE;
public AbilityDispatcher(Pony player) {
this.player = player;
}
/**
* Returns true if the current ability can we swapped out.
*/
boolean canSwitchStates() {
return !activeAbility.isPresent() || getStat(getActiveSlot()).canSwitchStates();
}
public void clear(AbilitySlot slot) {
Stat stat = getStat(slot);
public AbilitySlot getActiveSlot() {
return activeSlot;
}
public void cancelAbility(AbilitySlot slot) {
if (getActiveSlot() == slot && canSwitchStates()) {
setActiveAbility(slot, null);
if (stat.canSwitchStates()) {
stat.setActiveAbility(null);
}
}
public void activate(AbilitySlot slot) {
if (canSwitchStates()) {
getAbility(slot).ifPresent(ability -> setActiveAbility(slot, ability));
Stat stat = getStat(slot);
if (stat.canSwitchStates()) {
stat.getAbility().ifPresent(stat::setActiveAbility);
}
}
@ -63,88 +44,13 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
return stats.computeIfAbsent(slot, Stat::new);
}
public Optional<Ability<?>> getAbility(AbilitySlot slot) {
Race race = player.getSpecies();
return Abilities.BY_SLOT.get(slot).stream().filter(a -> a.canUse(race)).findFirst();
}
protected synchronized void setActiveAbility(AbilitySlot slot, Ability<?> power) {
if (activeAbility.orElse(null) != power) {
activeSlot = slot;
triggered = false;
activeAbility = Optional.ofNullable(power);
Stat stat = getStat(slot);
stat.setWarmup(activeAbility.map(p -> p.getWarmupTime(player)).orElse(0));
stat.setCooldown(0);
}
}
@Nullable
protected synchronized Optional<Ability<?>> getActiveAbility() {
Stat stat = getStat(getActiveSlot());
return activeAbility.filter(ability -> {
return (!(ability == null || (triggered && stat.warmup == 0 && stat.cooldown == 0)) && ability.canUse(player.getSpecies()));
});
}
@Override
public void tick() {
getActiveAbility().ifPresent(this::activate);
}
private <T extends Hit> void activate(Ability<T> ability) {
Stat stat = getStat(getActiveSlot());
stats.values().forEach(s -> {
if (s != stat) {
s.idle();
}
});
if (stat.warmup > 0) {
stat.warmup--;
System.out.println("warming up");
ability.preApply(player);
return;
}
if (stat.tickInactive()) {
System.out.println("cooling down");
ability.postApply(player);
if (stat.cooldown <= 0) {
setActiveAbility(AbilitySlot.NONE, null);
}
return;
}
if (triggered) {
return;
}
if (ability.canActivate(player.getWorld(), player)) {
triggered = true;
stat.setCooldown(ability.getCooldownTime(player));
if (player.isClientPlayer()) {
T data = ability.tryActivate(player);
if (data != null) {
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data));
} else {
stat.setCooldown(0);
}
}
}
if (stat.cooldown <= 0) {
setActiveAbility(AbilitySlot.NONE, null);
}
stats.values().forEach(Stat::tick);
}
@Override
public void toNBT(CompoundTag compound) {
compound.putBoolean("triggered", triggered);
if (compound.contains("stats")) {
stats.clear();
CompoundTag li = compound.getCompound("stats");
@ -152,20 +58,13 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
getStat(AbilitySlot.valueOf(key)).fromNBT(li.getCompound(key));
});
}
compound.putInt("activeSlot", activeSlot.ordinal());
getActiveAbility().ifPresent(ability -> {
compound.putString("activeAbility", Abilities.REGISTRY.getId(ability).toString());
});
}
@Override
public void fromNBT(CompoundTag compound) {
triggered = compound.getBoolean("triggered");
CompoundTag li = new CompoundTag();
stats.forEach((key, value) -> li.put(key.name(), value.toNBT()));
compound.put("stats", li);
activeSlot = compound.contains("activeSlot") ? AbilitySlot.values()[compound.getInt("activeSlot")] : activeSlot;
activeAbility = Abilities.REGISTRY.getOrEmpty(new Identifier(compound.getString("activeAbility")));
}
public class Stat implements NbtSerialisable {
@ -184,6 +83,13 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
public final AbilitySlot slot;
/**
* True once the current ability has been triggered.
*/
private boolean triggered;
private Optional<Ability<?>> activeAbility = Optional.empty();
private Stat(AbilitySlot slot) {
this.slot = slot;
}
@ -192,7 +98,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
* Returns true if the current ability can we swapped out.
*/
boolean canSwitchStates() {
return (warmup != 0) || (triggered && cooldown == 0);
return !activeAbility.isPresent() || (warmup != 0) || (triggered && cooldown == 0);
}
public int getRemainingCooldown() {
@ -225,17 +131,68 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
warmup = value;
}
public void idle() {
public void tick() {
getActiveAbility().ifPresent(this::activate);
}
private <T extends Hit> void activate(Ability<T> ability) {
if (warmup > 0) {
warmup--;
ability.preApply(player, slot);
return;
}
if (cooldown > 0) {
cooldown--;
if (cooldown > 0 && cooldown-- > 0) {
ability.postApply(player, slot);
if (cooldown <= 0) {
setActiveAbility(null);
}
return;
}
if (triggered) {
return;
}
if (ability.canActivate(player.getWorld(), player)) {
triggered = true;
setCooldown(ability.getCooldownTime(player));
if (player.isClientPlayer()) {
T data = ability.tryActivate(player);
if (data != null) {
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data));
} else {
setCooldown(0);
}
}
}
if (cooldown <= 0) {
setActiveAbility(null);
}
}
public boolean tickInactive() {
return cooldown > 0 && cooldown-- > 0;
public Optional<Ability<?>> getAbility() {
Race race = player.getSpecies();
return Abilities.BY_SLOT.get(slot).stream().filter(a -> a.canUse(race)).findFirst();
}
protected synchronized void setActiveAbility(Ability<?> power) {
if (activeAbility.orElse(null) != power) {
triggered = false;
activeAbility = Optional.ofNullable(power);
setWarmup(activeAbility.map(p -> p.getWarmupTime(player)).orElse(0));
setCooldown(0);
}
}
protected synchronized Optional<Ability<?>> getActiveAbility() {
return activeAbility.filter(ability -> {
return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && ability.canUse(player.getSpecies()));
});
}
@Override
@ -244,6 +201,10 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
compound.putInt("cooldown", cooldown);
compound.putInt("maxWarmup", maxWarmup);
compound.putInt("maxCooldown", maxCooldown);
compound.putBoolean("triggered", triggered);
getActiveAbility().ifPresent(ability -> {
compound.putString("activeAbility", Abilities.REGISTRY.getId(ability).toString());
});
}
@Override
@ -252,6 +213,8 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
cooldown = compound.getInt("cooldown");
maxWarmup = compound.getInt("maxWarmup");
maxCooldown = compound.getInt("maxCooldown");
triggered = compound.getBoolean("triggered");
activeAbility = Abilities.REGISTRY.getOrEmpty(new Identifier(compound.getString("activeAbility")));
}
}
}

View file

@ -71,11 +71,11 @@ public class CarryAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
}
public interface IPickupImmuned {

View file

@ -78,13 +78,13 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
}
@Override
public void preApply(Pony player) {
player.getMagicalReserves().addEnergy(2);
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().addEnergy(20);
player.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().setEnergy(0);
player.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
}

View file

@ -148,12 +148,12 @@ public class ChangelingFeedAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().addExertion(6);
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
player.spawnParticles(ParticleTypes.HEART, 1);
}
}

View file

@ -43,12 +43,12 @@ public class ChangelingTrapAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
}
}

View file

@ -81,8 +81,8 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public void preApply(Pony player) {
player.getMagicalReserves().addExertion(3);
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().addExertion(30);
if (player.getWorld().isClient()) {
player.spawnParticles(MagicParticleEffect.UNICORN, 1);
@ -90,7 +90,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
}
}

View file

@ -186,14 +186,14 @@ public class EarthPonyStompAbility implements Ability<Multi> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().addExertion(40);
player.getOwner().attemptSprintingParticles();
}
@Override
public void postApply(Pony player) {
int timeDiff = getCooldownTime(player) - player.getAbilities().getStat(player.getAbilities().getActiveSlot()).getRemainingCooldown();
public void postApply(Pony player, AbilitySlot slot) {
int timeDiff = getCooldownTime(player) - player.getAbilities().getStat(slot).getRemainingCooldown();
if (player.getOwner().getEntityWorld().getTime() % 1 == 0 || timeDiff == 0) {
spawnParticleRing(player.getOwner(), timeDiff, 1);

View file

@ -66,12 +66,12 @@ public class PegasusCloudInteractionAbility implements Ability<Numeric> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 10);
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
player.spawnParticles(UParticles.RAIN_DROPS, 5);
}

View file

@ -48,12 +48,12 @@ public class UnicornCastingAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -151,13 +151,13 @@ public class UnicornTeleportAbility implements Ability<Pos> {
}
@Override
public void preApply(Pony player) {
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().addExertion(30);
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
@Override
public void postApply(Pony player) {
public void postApply(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -61,7 +61,7 @@ class KeyBindingsHandler {
}
} else if (pressed.remove(i)) {
System.out.println("Key up " + slot);
iplayer.getAbilities().cancelAbility(slot);
iplayer.getAbilities().clear(slot);
}
}
}