Rewrite controls

This commit is contained in:
Sollace 2020-05-06 15:55:25 +02:00
parent 019c9e17ed
commit ee7c67afe4
8 changed files with 234 additions and 111 deletions

View file

@ -1,41 +1,40 @@
package com.minelittlepony.unicopia.ability; package com.minelittlepony.unicopia.ability;
import java.util.HashMap; import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.lwjgl.glfw.GLFW;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.MutableRegistry; import net.minecraft.util.registry.MutableRegistry;
import net.minecraft.util.registry.SimpleRegistry; import net.minecraft.util.registry.SimpleRegistry;
public interface Abilities { public interface Abilities {
Map<Identifier, Integer> KEYS_CODES = new HashMap<>(); Map<AbilitySlot, Set<Ability<?>>> BY_SLOT = new EnumMap<>(AbilitySlot.class);
MutableRegistry<Ability<?>> REGISTRY = new SimpleRegistry<>(); MutableRegistry<Ability<?>> REGISTRY = new SimpleRegistry<>();
// unicorn / alicorn // unicorn / alicorn
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", GLFW.GLFW_KEY_O); Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.PRIMARY);
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", GLFW.GLFW_KEY_P); Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.SECONDARY);
// earth / alicorn // earth / alicorn
Ability<?> GROW = register(new EarthPonyGrowAbility(), "grow", GLFW.GLFW_KEY_N); Ability<?> STOMP = register(new EarthPonyStompAbility(), "stomp", AbilitySlot.PRIMARY);
Ability<?> STOMP = register(new EarthPonyStompAbility(), "stomp", GLFW.GLFW_KEY_M); Ability<?> GROW = register(new EarthPonyGrowAbility(), "grow", AbilitySlot.SECONDARY);
// pegasus / bat / alicorn / changeling // pegasus / bat / alicorn / changeling
Ability<?> CARRY = register(new CarryAbility(), "carry", GLFW.GLFW_KEY_K); Ability<?> CARRY = register(new CarryAbility(), "carry", AbilitySlot.PASSIVE);
// pegasus / alicorn // pegasus / alicorn
Ability<?> CLOUD = register(new PegasusCloudInteractionAbility(), "cloud", GLFW.GLFW_KEY_J); Ability<?> CLOUD = register(new PegasusCloudInteractionAbility(), "cloud", AbilitySlot.TERTIARY);
// changeling // changeling
Ability<?> FEED = register(new ChangelingFeedAbility(), "feed", GLFW.GLFW_KEY_O); Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.PRIMARY);
Ability<?> TRAP = register(new ChangelingTrapAbility(), "trap", GLFW.GLFW_KEY_L); Ability<?> FEED = register(new ChangelingFeedAbility(), "feed", AbilitySlot.SECONDARY);
Ability<?> TRAP = register(new ChangelingTrapAbility(), "trap", AbilitySlot.TERTIARY);
Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", GLFW.GLFW_KEY_P); static <T extends Ability<?>> T register(T power, String name, AbilitySlot slot) {
static <T extends Ability<?>> T register(T power, String name, int keyCode) {
Identifier id = new Identifier("unicopia", name); Identifier id = new Identifier("unicopia", name);
KEYS_CODES.put(id, keyCode); BY_SLOT.computeIfAbsent(slot, s -> new HashSet<>()).add(power);
return REGISTRY.add(id, power); return REGISTRY.add(id, power);
} }
} }

View file

@ -1,9 +1,12 @@
package com.minelittlepony.unicopia.ability; package com.minelittlepony.unicopia.ability;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.Updatable; import com.minelittlepony.unicopia.entity.Updatable;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
@ -18,15 +21,7 @@ public class AbilityDispatcher implements Updatable, NbtSerialisable {
private final Pony player; private final Pony player;
/** private final Map<AbilitySlot, Stat> stats = new EnumMap<>(AbilitySlot.class);
* Ticks of warmup before an ability is triggered.
*/
private int warmup;
/**
* Ticks of cooldown after an ability has been triggered.
*/
private int cooldown;
/** /**
* True once the current ability has been triggered. * True once the current ability has been triggered.
@ -35,69 +30,90 @@ public class AbilityDispatcher implements Updatable, NbtSerialisable {
private Optional<Ability<?>> activeAbility = Optional.empty(); private Optional<Ability<?>> activeAbility = Optional.empty();
private AbilitySlot activeSlot = AbilitySlot.NONE;
public AbilityDispatcher(Pony player) { public AbilityDispatcher(Pony player) {
this.player = player; this.player = player;
} }
/** /**
* Returns true if the currrent ability can we swapped out. * Returns true if the current ability can we swapped out.
*/ */
boolean canSwitchStates() { boolean canSwitchStates() {
return !activeAbility.isPresent() || (warmup != 0) || (triggered && cooldown == 0); return !activeAbility.isPresent() || getStat(getActiveSlot()).canSwitchStates();
} }
public void tryUseAbility(Ability<?> power) { public AbilitySlot getActiveSlot() {
if (canSwitchStates()) { return activeSlot;
setAbility(power); }
public void cancelAbility(AbilitySlot slot) {
if (getActiveSlot() == slot && canSwitchStates()) {
setActiveAbility(slot, null);
} }
} }
public void tryClearAbility() { public void activate(AbilitySlot slot) {
if (canSwitchStates()) { if (canSwitchStates()) {
setAbility(null); getAbility(slot).ifPresent(ability -> setActiveAbility(slot, ability));
} }
} }
protected synchronized void setAbility(Ability<?> power) { 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) { if (activeAbility.orElse(null) != power) {
activeSlot = slot;
triggered = false; triggered = false;
activeAbility = Optional.ofNullable(power); activeAbility = Optional.ofNullable(power);
warmup = activeAbility.map(p -> p.getWarmupTime(player)).orElse(0); Stat stat = getStat(slot);
cooldown = 0; stat.setWarmup(activeAbility.map(p -> p.getWarmupTime(player)).orElse(0));
stat.setCooldown(0);
} }
} }
@Nullable @Nullable
protected synchronized Optional<Ability<?>> getUsableAbility() { protected synchronized Optional<Ability<?>> getActiveAbility() {
Stat stat = getStat(getActiveSlot());
return activeAbility.filter(ability -> { return activeAbility.filter(ability -> {
return (!(ability == null || (triggered && warmup == 0 && cooldown == 0)) && ability.canUse(player.getSpecies())); return (!(ability == null || (triggered && stat.warmup == 0 && stat.cooldown == 0)) && ability.canUse(player.getSpecies()));
}); });
} }
public int getRemainingCooldown() {
return cooldown;
}
@Override @Override
public void onUpdate() { public void onUpdate() {
getUsableAbility().ifPresent(this::activate); getActiveAbility().ifPresent(this::activate);
} }
private <T extends Hit> void activate(Ability<T> ability) { private <T extends Hit> void activate(Ability<T> ability) {
if (warmup > 0) { Stat stat = getStat(getActiveSlot());
warmup--;
stats.values().forEach(s -> {
if (s != stat) {
s.idle();
}
});
if (stat.warmup > 0) {
stat.warmup--;
System.out.println("warming up"); System.out.println("warming up");
ability.preApply(player); ability.preApply(player);
return; return;
} }
if (cooldown > 0) { if (stat.tickInactive()) {
cooldown--;
System.out.println("cooling down"); System.out.println("cooling down");
ability.postApply(player); ability.postApply(player);
if (cooldown <= 0) { if (stat.cooldown <= 0) {
setAbility(null); setActiveAbility(AbilitySlot.NONE, null);
} }
return; return;
} }
@ -108,7 +124,7 @@ public class AbilityDispatcher implements Updatable, NbtSerialisable {
if (ability.canActivate(player.getWorld(), player)) { if (ability.canActivate(player.getWorld(), player)) {
triggered = true; triggered = true;
cooldown = ability.getCooldownTime(player); stat.setCooldown(ability.getCooldownTime(player));
if (player.isClientPlayer()) { if (player.isClientPlayer()) {
T data = ability.tryActivate(player); T data = ability.tryActivate(player);
@ -116,22 +132,28 @@ public class AbilityDispatcher implements Updatable, NbtSerialisable {
if (data != null) { if (data != null) {
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data)); Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data));
} else { } else {
cooldown = 0; stat.setCooldown(0);
} }
} }
} }
if (cooldown <= 0) { if (stat.cooldown <= 0) {
setAbility(null); setActiveAbility(AbilitySlot.NONE, null);
} }
} }
@Override @Override
public void toNBT(CompoundTag compound) { public void toNBT(CompoundTag compound) {
compound.putBoolean("triggered", triggered); compound.putBoolean("triggered", triggered);
compound.putInt("warmup", warmup); if (compound.contains("stats")) {
compound.putInt("cooldown", cooldown); stats.clear();
getUsableAbility().ifPresent(ability -> { 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()); compound.putString("activeAbility", Abilities.REGISTRY.getId(ability).toString());
}); });
} }
@ -139,8 +161,97 @@ public class AbilityDispatcher implements Updatable, NbtSerialisable {
@Override @Override
public void fromNBT(CompoundTag compound) { public void fromNBT(CompoundTag compound) {
triggered = compound.getBoolean("triggered"); triggered = compound.getBoolean("triggered");
warmup = compound.getInt("warmup"); CompoundTag li = new CompoundTag();
cooldown = compound.getInt("cooldown"); 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"))); activeAbility = Abilities.REGISTRY.getOrEmpty(new Identifier(compound.getString("activeAbility")));
} }
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");
}
}
} }

View file

@ -0,0 +1,13 @@
package com.minelittlepony.unicopia.ability;
public enum AbilitySlot {
NONE,
PRIMARY,
SECONDARY,
TERTIARY,
PASSIVE;
public boolean isPassive() {
return this == PASSIVE;
}
}

View file

@ -193,7 +193,7 @@ public class EarthPonyStompAbility implements Ability<Multi> {
@Override @Override
public void postApply(Pony player) { public void postApply(Pony player) {
int timeDiff = getCooldownTime(player) - player.getAbilities().getRemainingCooldown(); int timeDiff = getCooldownTime(player) - player.getAbilities().getStat(player.getAbilities().getActiveSlot()).getRemainingCooldown();
if (player.getOwner().getEntityWorld().getTime() % 1 == 0 || timeDiff == 0) { if (player.getOwner().getEntityWorld().getTime() % 1 == 0 || timeDiff == 0) {
spawnParticleRing(player.getOwner(), timeDiff, 1); spawnParticleRing(player.getOwner(), timeDiff, 1);

View file

@ -1,17 +1,13 @@
package com.minelittlepony.unicopia.client; package com.minelittlepony.unicopia.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.minelittlepony.unicopia.Race; import org.lwjgl.glfw.GLFW;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.Ability; import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
@ -24,27 +20,25 @@ import net.minecraft.util.Identifier;
class KeyBindingsHandler { class KeyBindingsHandler {
private final String KEY_CATEGORY = "unicopia.category.name"; private final String KEY_CATEGORY = "unicopia.category.name";
private final Map<InputUtil.KeyCode, List<Ability<? extends Hit>>> keyPools = new HashMap<>(); private final Map<KeyBinding, AbilitySlot> keys = new HashMap<>();
private final Set<KeyBinding> bindings = new HashSet<>();
private final Set<KeyBinding> pressed = new HashSet<>(); private final Set<KeyBinding> pressed = new HashSet<>();
private Collection<Ability<?>> getKeyCodePool(KeyBinding keyCode) { public KeyBindingsHandler() {
return keyPools.computeIfAbsent(keyCode.getDefaultKeyCode(), i -> new ArrayList<>());
}
public void addKeybind(Ability<?> p) {
KeyBindingRegistry.INSTANCE.addCategory(KEY_CATEGORY); KeyBindingRegistry.INSTANCE.addCategory(KEY_CATEGORY);
Identifier id = Abilities.REGISTRY.getId(p); addKeybind(GLFW.GLFW_KEY_O, AbilitySlot.PRIMARY);
int code = Abilities.KEYS_CODES.get(id); addKeybind(GLFW.GLFW_KEY_P, AbilitySlot.SECONDARY);
addKeybind(GLFW.GLFW_KEY_L, AbilitySlot.TERTIARY);
}
FabricKeyBinding b = FabricKeyBinding.Builder.create(id, InputUtil.Type.KEYSYM, code, KEY_CATEGORY).build(); public void addKeybind(int code, AbilitySlot slot) {
KeyBindingRegistry.INSTANCE.addCategory(KEY_CATEGORY);
FabricKeyBinding b = FabricKeyBinding.Builder.create(new Identifier("unicopia", slot.name().toLowerCase()), InputUtil.Type.KEYSYM, code, KEY_CATEGORY).build();
KeyBindingRegistry.INSTANCE.register(b); KeyBindingRegistry.INSTANCE.register(b);
getKeyCodePool(b).add(p); keys.put(b, slot);
bindings.add(b);
} }
public void tick(MinecraftClient client) { public void tick(MinecraftClient client) {
@ -54,19 +48,20 @@ class KeyBindingsHandler {
} }
Pony iplayer = Pony.of(client.player); Pony iplayer = Pony.of(client.player);
for (KeyBinding i : bindings) { for (KeyBinding i : keys.keySet()) {
AbilitySlot slot = keys.get(i);
if (slot == AbilitySlot.PRIMARY && client.options.keySneak.isPressed()) {
slot = AbilitySlot.PASSIVE;
}
if (i.isPressed()) { if (i.isPressed()) {
if (pressed.add(i)) { if (pressed.add(i)) {
System.out.println("key press " + i); System.out.println("Key down " + slot);
Race race = iplayer.getSpecies(); iplayer.getAbilities().activate(slot);
getKeyCodePool(i).stream()
.filter(power -> power.canUse(race))
.findFirst()
.ifPresent(iplayer.getAbilities()::tryUseAbility);
} }
} else if (pressed.remove(i)) { } else if (pressed.remove(i)) {
System.out.println("key release " + i); System.out.println("Key up " + slot);
iplayer.getAbilities().tryClearAbility(); iplayer.getAbilities().cancelAbility(slot);
} }
} }
} }

View file

@ -4,10 +4,8 @@ import static com.minelittlepony.unicopia.EquinePredicates.PLAYER_UNICORN;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.common.event.ClientReadyCallback;
import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.container.SpellbookResultSlot; import com.minelittlepony.unicopia.container.SpellbookResultSlot;
import com.minelittlepony.unicopia.ducks.Colourful; import com.minelittlepony.unicopia.ducks.Colourful;
@ -45,8 +43,6 @@ public class UnicopiaClient implements ClientModInitializer {
URenderers.bootstrap(); URenderers.bootstrap();
ClientTickCallback.EVENT.register(this::tick); ClientTickCallback.EVENT.register(this::tick);
ClientReadyCallback.EVENT.register(client -> Abilities.REGISTRY.stream().forEach(keyboard::addKeybind));
DefaultTexturesRegistry.getDefaultTextures().add(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, SpellbookResultSlot.EMPTY_GEM_SLOT)); DefaultTexturesRegistry.getDefaultTextures().add(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, SpellbookResultSlot.EMPTY_GEM_SLOT));
ColorProviderRegistry.ITEM.register((stack, tint) -> { ColorProviderRegistry.ITEM.register((stack, tint) -> {

View file

@ -1,5 +1,8 @@
package com.minelittlepony.unicopia.client.gui; package com.minelittlepony.unicopia.client.gui;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -14,8 +17,8 @@ public class UHud extends DrawableHelper {
public static final Identifier HUD_TEXTURE = new Identifier("unicopia", "textures/gui/hud.png"); public static final Identifier HUD_TEXTURE = new Identifier("unicopia", "textures/gui/hud.png");
private Slot secondarySlot = new Slot(26, 0); private Slot secondarySlot = new Slot(AbilitySlot.SECONDARY, 26, 0);
private Slot tertiarySlot = new Slot(36, 24); private Slot tertiarySlot = new Slot(AbilitySlot.TERTIARY, 36, 24);
public void render(InGameHud hud, float tickDelta) { public void render(InGameHud hud, float tickDelta) {
MinecraftClient client = MinecraftClient.getInstance(); MinecraftClient client = MinecraftClient.getInstance();
@ -34,55 +37,60 @@ public class UHud extends DrawableHelper {
int frameHeight = 54; int frameHeight = 54;
int frameWidth = 54; int frameWidth = 54;
AbilityDispatcher abilities = Pony.of(client.player).getAbilities();
blit(x, y, 0, 0, frameWidth, frameHeight, 128, 128); // background blit(x, y, 0, 0, frameWidth, frameHeight, 128, 128); // background
float progressPercent = 0.25F; float progressPercent = abilities.getStat(AbilitySlot.PRIMARY).getFillProgress();
int progressHeight = (int)(frameHeight * progressPercent);
blit(x, y + (frameHeight - progressHeight), if (progressPercent > 0 && progressPercent < 1) {
61, frameHeight - progressHeight, int progressHeight = (int)(frameHeight * progressPercent);
frameWidth, progressHeight, 128, 128); // progress
blit(x, y + (frameHeight - progressHeight),
61, frameHeight - progressHeight,
frameWidth, progressHeight, 128, 128); // progress
}
blit(x, y, 0, 54, frameWidth, frameHeight, 128, 128); // frame blit(x, y, 0, 54, frameWidth, frameHeight, 128, 128); // frame
secondarySlot.render(abilities, x, y, tickDelta);
tertiarySlot.render(abilities, x, y, tickDelta);
secondarySlot.render(x, y, 50, 100, tickDelta);
tertiarySlot.render(x, y, 5, 10, tickDelta);
RenderSystem.disableBlend(); RenderSystem.disableBlend();
RenderSystem.disableAlphaTest(); RenderSystem.disableAlphaTest();
} }
static class Slot { static class Slot {
private final AbilitySlot slot;
private int x; private int x;
private int y; private int y;
private float lastCooldown; private float lastCooldown;
public Slot(int x, int y) { public Slot(AbilitySlot slot, int x, int y) {
this.slot = slot;
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
void render(int x, int y, float cooldown, float maxCooldown, float tickDelta) { void render(AbilityDispatcher abilities, int x, int y, float tickDelta) {
x += this.x; x += this.x;
y += this.y; y += this.y;
if (cooldown > 0 && maxCooldown > 0 && cooldown < maxCooldown) { float cooldown = abilities.getStat(slot).getFillProgress();
if (cooldown > 0 && cooldown < 1) {
float lerpCooldown = MathHelper.lerp(tickDelta, cooldown, lastCooldown); float lerpCooldown = MathHelper.lerp(tickDelta, cooldown, lastCooldown);
lastCooldown = lerpCooldown; lastCooldown = lerpCooldown;
float cooldownPercent = 1 - lerpCooldown / maxCooldown;
int slotPadding = 4; int slotPadding = 4;
int slotSize = 15; int slotSize = 15;
int progressBottom = y + slotPadding + slotSize; int progressBottom = y + slotPadding + slotSize;
int progressTop = progressBottom - (int)(15F * cooldownPercent); int progressTop = progressBottom - (int)(15F * cooldown);
fill(x + slotPadding, progressTop, x + slotPadding + slotSize, progressBottom, 0xAAFFFFFF); fill(x + slotPadding, progressTop, x + slotPadding + slotSize, progressBottom, 0xAAFFFFFF);
} }

View file

@ -4,9 +4,9 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.ability.AbilityDispatcher; import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ducks.PonyContainer;
import com.minelittlepony.unicopia.enchanting.PageOwner; import com.minelittlepony.unicopia.enchanting.PageOwner;
import com.minelittlepony.unicopia.entity.FlightControl; import com.minelittlepony.unicopia.entity.FlightControl;
import com.minelittlepony.unicopia.entity.Ponylike;
import com.minelittlepony.unicopia.entity.RaceContainer; import com.minelittlepony.unicopia.entity.RaceContainer;
import com.minelittlepony.unicopia.magic.Caster; import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.network.Transmittable; import com.minelittlepony.unicopia.network.Transmittable;
@ -97,9 +97,10 @@ public interface Pony extends Caster<PlayerEntity>, RaceContainer<PlayerEntity>,
return InteractionManager.instance().isClientPlayer(getOwner()); return InteractionManager.instance().isClientPlayer(getOwner());
} }
@SuppressWarnings("unchecked")
@Nullable @Nullable
static Pony of(@Nullable PlayerEntity player) { static Pony of(@Nullable PlayerEntity player) {
return Ponylike.<Pony>of(player); return player == null ? null : ((PonyContainer<Pony>)player).get();
} }
static boolean equal(GameProfile one, GameProfile two) { static boolean equal(GameProfile one, GameProfile two) {