Unicopia/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java

258 lines
7.5 KiB
Java
Raw Normal View History

package com.minelittlepony.unicopia.ability;
2020-05-06 15:55:25 +02:00
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
2018-09-12 01:29:49 +02:00
import javax.annotation.Nullable;
2020-05-06 15:55:25 +02:00
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.Updatable;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.MsgPlayerAbility;
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;
import net.minecraft.util.Identifier;
2018-09-12 01:29:49 +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
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;
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) {
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) {
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;
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());
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()));
});
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);
}
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--;
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()) {
System.out.println("cooling down");
2019-02-03 10:45:45 +01:00
ability.postApply(player);
2020-05-06 15:55:25 +02:00
if (stat.cooldown <= 0) {
setActiveAbility(AbilitySlot.NONE, null);
}
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()) {
T data = ability.tryActivate(player);
if (data != null) {
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data));
} else {
2020-05-06 15:55:25 +02:00
stat.setCooldown(0);
}
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 -> {
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;
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
}