2020-04-25 15:37:17 +02:00
|
|
|
package com.minelittlepony.unicopia.ability;
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
import java.util.EnumMap;
|
|
|
|
import java.util.Map;
|
2020-04-25 15:37:17 +02:00
|
|
|
import java.util.Optional;
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2019-02-02 17:50:15 +01:00
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
import com.minelittlepony.unicopia.Race;
|
2020-04-25 15:37:17 +02:00
|
|
|
import com.minelittlepony.unicopia.ability.data.Hit;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.Updatable;
|
2020-04-25 15:37:17 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.network.MsgPlayerAbility;
|
2020-04-23 23:44:31 +02:00
|
|
|
import com.minelittlepony.unicopia.network.Channel;
|
2020-04-15 18:12:00 +02:00
|
|
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-01-16 12:35:46 +01:00
|
|
|
import net.minecraft.nbt.CompoundTag;
|
2020-04-25 15:37:17 +02:00
|
|
|
import net.minecraft.util.Identifier;
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-04-25 15:37:17 +02:00
|
|
|
public class AbilityDispatcher implements Updatable, NbtSerialisable {
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-04-15 18:12:00 +02:00
|
|
|
private final Pony player;
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
private final Map<AbilitySlot, Stat> stats = new EnumMap<>(AbilitySlot.class);
|
2019-02-03 10:45:45 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* True once the current ability has been triggered.
|
|
|
|
*/
|
|
|
|
private boolean triggered;
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-04-25 15:37:17 +02:00
|
|
|
private Optional<Ability<?>> activeAbility = Optional.empty();
|
2018-09-12 01:29:49 +02:00
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
private AbilitySlot activeSlot = AbilitySlot.NONE;
|
|
|
|
|
2020-04-25 15:37:17 +02:00
|
|
|
public AbilityDispatcher(Pony player) {
|
2018-09-12 01:29:49 +02:00
|
|
|
this.player = player;
|
|
|
|
}
|
|
|
|
|
2019-02-03 10:45:45 +01:00
|
|
|
/**
|
2020-05-06 15:55:25 +02:00
|
|
|
* Returns true if the current ability can we swapped out.
|
2019-02-03 10:45:45 +01:00
|
|
|
*/
|
2018-09-12 01:29:49 +02:00
|
|
|
boolean canSwitchStates() {
|
2020-05-06 15:55:25 +02:00
|
|
|
return !activeAbility.isPresent() || getStat(getActiveSlot()).canSwitchStates();
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
public AbilitySlot getActiveSlot() {
|
|
|
|
return activeSlot;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void cancelAbility(AbilitySlot slot) {
|
|
|
|
if (getActiveSlot() == slot && canSwitchStates()) {
|
|
|
|
setActiveAbility(slot, null);
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
public void activate(AbilitySlot slot) {
|
2019-02-02 17:50:15 +01:00
|
|
|
if (canSwitchStates()) {
|
2020-05-06 15:55:25 +02:00
|
|
|
getAbility(slot).ifPresent(ability -> setActiveAbility(slot, ability));
|
2019-02-03 10:45:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
public Stat getStat(AbilitySlot slot) {
|
|
|
|
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) {
|
2020-04-25 15:37:17 +02:00
|
|
|
if (activeAbility.orElse(null) != power) {
|
2020-05-06 15:55:25 +02:00
|
|
|
activeSlot = slot;
|
2019-02-03 16:43:37 +01:00
|
|
|
triggered = false;
|
2020-04-25 15:37:17 +02:00
|
|
|
activeAbility = Optional.ofNullable(power);
|
2020-05-06 15:55:25 +02:00
|
|
|
Stat stat = getStat(slot);
|
|
|
|
stat.setWarmup(activeAbility.map(p -> p.getWarmupTime(player)).orElse(0));
|
|
|
|
stat.setCooldown(0);
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 10:45:45 +01:00
|
|
|
@Nullable
|
2020-05-06 15:55:25 +02:00
|
|
|
protected synchronized Optional<Ability<?>> getActiveAbility() {
|
|
|
|
Stat stat = getStat(getActiveSlot());
|
2020-04-25 15:37:17 +02:00
|
|
|
return activeAbility.filter(ability -> {
|
2020-05-06 15:55:25 +02:00
|
|
|
return (!(ability == null || (triggered && stat.warmup == 0 && stat.cooldown == 0)) && ability.canUse(player.getSpecies()));
|
2020-04-25 15:37:17 +02:00
|
|
|
});
|
2019-02-03 10:45:45 +01:00
|
|
|
}
|
|
|
|
|
2018-09-12 01:29:49 +02:00
|
|
|
@Override
|
2019-02-17 18:22:11 +01:00
|
|
|
public void onUpdate() {
|
2020-05-06 15:55:25 +02:00
|
|
|
getActiveAbility().ifPresent(this::activate);
|
2020-04-25 15:37:17 +02:00
|
|
|
}
|
2019-02-02 17:50:15 +01:00
|
|
|
|
2020-04-25 15:37:17 +02:00
|
|
|
private <T extends Hit> void activate(Ability<T> ability) {
|
2020-05-06 15:55:25 +02:00
|
|
|
Stat stat = getStat(getActiveSlot());
|
|
|
|
|
|
|
|
stats.values().forEach(s -> {
|
|
|
|
if (s != stat) {
|
|
|
|
s.idle();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (stat.warmup > 0) {
|
|
|
|
stat.warmup--;
|
2020-04-27 00:18:11 +02:00
|
|
|
System.out.println("warming up");
|
2019-02-03 10:45:45 +01:00
|
|
|
ability.preApply(player);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
if (stat.tickInactive()) {
|
2020-04-27 00:18:11 +02:00
|
|
|
System.out.println("cooling down");
|
2019-02-03 10:45:45 +01:00
|
|
|
ability.postApply(player);
|
2020-04-27 00:18:11 +02:00
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
if (stat.cooldown <= 0) {
|
|
|
|
setActiveAbility(AbilitySlot.NONE, null);
|
2020-04-27 00:18:11 +02:00
|
|
|
}
|
2019-02-03 10:45:45 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (triggered) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ability.canActivate(player.getWorld(), player)) {
|
|
|
|
triggered = true;
|
2020-05-06 15:55:25 +02:00
|
|
|
stat.setCooldown(ability.getCooldownTime(player));
|
2019-02-03 10:45:45 +01:00
|
|
|
|
|
|
|
if (player.isClientPlayer()) {
|
2020-04-25 15:37:17 +02:00
|
|
|
T data = ability.tryActivate(player);
|
2020-04-23 23:44:31 +02:00
|
|
|
|
|
|
|
if (data != null) {
|
2020-04-25 15:37:17 +02:00
|
|
|
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data));
|
2020-04-23 23:44:31 +02:00
|
|
|
} else {
|
2020-05-06 15:55:25 +02:00
|
|
|
stat.setCooldown(0);
|
2019-02-07 14:47:06 +01:00
|
|
|
}
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
}
|
2019-02-03 10:45:45 +01:00
|
|
|
|
2020-05-06 15:55:25 +02:00
|
|
|
if (stat.cooldown <= 0) {
|
|
|
|
setActiveAbility(AbilitySlot.NONE, null);
|
2019-02-03 10:45:45 +01:00
|
|
|
}
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-01-16 12:35:46 +01:00
|
|
|
public void toNBT(CompoundTag compound) {
|
|
|
|
compound.putBoolean("triggered", triggered);
|
2020-05-06 15:55:25 +02:00
|
|
|
if (compound.contains("stats")) {
|
|
|
|
stats.clear();
|
|
|
|
CompoundTag li = compound.getCompound("stats");
|
|
|
|
li.getKeys().forEach(key -> {
|
|
|
|
getStat(AbilitySlot.valueOf(key)).fromNBT(li.getCompound(key));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
compound.putInt("activeSlot", activeSlot.ordinal());
|
|
|
|
getActiveAbility().ifPresent(ability -> {
|
2020-04-25 15:37:17 +02:00
|
|
|
compound.putString("activeAbility", Abilities.REGISTRY.getId(ability).toString());
|
|
|
|
});
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-01-16 12:35:46 +01:00
|
|
|
public void fromNBT(CompoundTag compound) {
|
2019-02-03 10:45:45 +01:00
|
|
|
triggered = compound.getBoolean("triggered");
|
2020-05-06 15:55:25 +02:00
|
|
|
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;
|
2020-04-25 15:37:17 +02:00
|
|
|
activeAbility = Abilities.REGISTRY.getOrEmpty(new Identifier(compound.getString("activeAbility")));
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|
2020-05-06 15:55:25 +02:00
|
|
|
|
|
|
|
public class Stat implements NbtSerialisable {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ticks of warmup before an ability is triggered.
|
|
|
|
*/
|
|
|
|
private int warmup;
|
|
|
|
private int maxWarmup;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ticks of cooldown after an ability has been triggered.
|
|
|
|
*/
|
|
|
|
private int cooldown;
|
|
|
|
private int maxCooldown;
|
|
|
|
|
|
|
|
public final AbilitySlot slot;
|
|
|
|
|
|
|
|
private Stat(AbilitySlot slot) {
|
|
|
|
this.slot = slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the current ability can we swapped out.
|
|
|
|
*/
|
|
|
|
boolean canSwitchStates() {
|
|
|
|
return (warmup != 0) || (triggered && cooldown == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getRemainingCooldown() {
|
|
|
|
return cooldown;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getFillProgress() {
|
|
|
|
float cooldown = getWarmup();
|
|
|
|
if (cooldown <= 0 || cooldown >= 1) {
|
|
|
|
return getCooldown();
|
|
|
|
}
|
|
|
|
return 1 - cooldown;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getCooldown() {
|
|
|
|
return maxCooldown <= 0 ? 0 : ((float)cooldown / (float)maxCooldown);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setCooldown(int value) {
|
|
|
|
cooldown = value;
|
|
|
|
maxCooldown = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getWarmup() {
|
|
|
|
return maxWarmup <= 0 ? 0 : ((float)warmup / (float)maxWarmup);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setWarmup(int value) {
|
|
|
|
maxWarmup = value;
|
|
|
|
warmup = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void idle() {
|
|
|
|
if (warmup > 0) {
|
|
|
|
warmup--;
|
|
|
|
}
|
|
|
|
if (cooldown > 0) {
|
|
|
|
cooldown--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean tickInactive() {
|
|
|
|
return cooldown > 0 && cooldown-- > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void toNBT(CompoundTag compound) {
|
|
|
|
compound.putInt("warmup", warmup);
|
|
|
|
compound.putInt("cooldown", cooldown);
|
|
|
|
compound.putInt("maxWarmup", maxWarmup);
|
|
|
|
compound.putInt("maxCooldown", maxCooldown);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void fromNBT(CompoundTag compound) {
|
|
|
|
warmup = compound.getInt("warmup");
|
|
|
|
cooldown = compound.getInt("cooldown");
|
|
|
|
maxWarmup = compound.getInt("maxWarmup");
|
|
|
|
maxCooldown = compound.getInt("maxCooldown");
|
|
|
|
}
|
|
|
|
}
|
2018-09-12 01:29:49 +02:00
|
|
|
}
|