Fix Chained Ability Icons + Chained Ability Per Cooldown Config Options

This commit is contained in:
Kam committed 2026-03-13 18:52:17 -04:00
1 parent ac953685c8
commit ab60fdbd12
5 files changed
+73 -12

No files matched your search

@@ -51,6 +51,16 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
*/
private int cooldownTicks = 100;
/**
* If true, only this chain goes on cooldown (not the global cooldown).
*/
private boolean perAbilityCooldown = false;
/**
* If true, skip cooldown entirely after chain completes.
*/
private boolean ignoreCooldown = false;
/**
* Minimum range for NPC eligibility check.
*/
@@ -159,6 +169,22 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
this.cooldownTicks = Math.max(0, cooldownTicks);
}
public boolean isPerAbilityCooldown() {
return perAbilityCooldown;
}
public void setPerAbilityCooldown(boolean perAbilityCooldown) {
this.perAbilityCooldown = perAbilityCooldown;
}
public boolean isIgnoreCooldown() {
return ignoreCooldown;
}
public void setIgnoreCooldown(boolean ignoreCooldown) {
this.ignoreCooldown = ignoreCooldown;
}
public float getMinRange() {
return minRange;
}
@@ -402,6 +428,8 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
nbt.setInteger("Weight", weight);
nbt.setBoolean("WindUpAll", windUpAll);
nbt.setInteger("CooldownTicks", cooldownTicks);
nbt.setBoolean("PerAbilityCooldown", perAbilityCooldown);
nbt.setBoolean("IgnoreCooldown", ignoreCooldown);
nbt.setFloat("MinRange", minRange);
nbt.setFloat("MaxRange", maxRange);
@@ -443,6 +471,8 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
weight = nbt.hasKey("Weight") ? nbt.getInteger("Weight") : 10;
windUpAll = !nbt.hasKey("WindUpAll") || nbt.getBoolean("WindUpAll");
cooldownTicks = nbt.hasKey("CooldownTicks") ? nbt.getInteger("CooldownTicks") : 100;
perAbilityCooldown = nbt.getBoolean("PerAbilityCooldown");
ignoreCooldown = nbt.getBoolean("IgnoreCooldown");
minRange = nbt.getFloat("MinRange");
maxRange = nbt.hasKey("MaxRange") ? nbt.getFloat("MaxRange") : 20;
// AllowedBy is computed from child abilities
@@ -33,6 +33,7 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;
import static kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition.MAX_CONDITIONS;
@@ -153,7 +154,11 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
fieldDefs.add(FieldDef.boolField("gui.enabled", chain::isEnabled, chain::setEnabled).tab("General"));
fieldDefs.add(FieldDef.intField("ability.weight", chain::getWeight, chain::setWeight).range(1, 100).tab("General"));
fieldDefs.add(FieldDef.boolField("ability.windUpAll", chain::isWindUpAll, chain::setWindUpAll).tab("General"));
fieldDefs.add(FieldDef.intField("ability.cooldown", chain::getCooldownTicks, chain::setCooldownTicks).range(0, 12000).tab("General"));
fieldDefs.add(FieldDef.row(
FieldDef.intField("ability.cooldown", chain::getCooldownTicks, chain::setCooldownTicks).range(0, 12000),
FieldDef.boolField("ability.perAbilityCooldown", chain::isPerAbilityCooldown, chain::setPerAbilityCooldown)
).tab("General"));
fieldDefs.add(FieldDef.boolField("ability.ignoreCooldown", chain::isIgnoreCooldown, chain::setIgnoreCooldown).tab("General"));
// ── Target tab ───────────────────────────────────────────────
fieldDefs.add(FieldDef.row(
@@ -163,16 +168,20 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
// ── Icon tab ──────────────────────────────────────────────
AbilityIconData chainIcon = AbilityIconData.fromChainedAbility(chain);
BooleanSupplier isCustomIcon = chainIcon::isEnabled;
fieldDefs.add(FieldDef.section("ability.section.icon").tab("Icon"));
fieldDefs.add(FieldDef.boolField("ability.hasIcon", chainIcon::isEnabled, chainIcon::setEnabled).tab("Icon"));
fieldDefs.add(FieldDef.row(
FieldDef.intField("gui.width", chainIcon::getWidth, chainIcon::setWidth).range(1, 256),
FieldDef.intField("gui.height", chainIcon::getHeight, chainIcon::setHeight).range(1, 256)
).tab("Icon"));
fieldDefs.add(FieldDef.floatField("gui.scale", chainIcon::getScale, chainIcon::setScale).tab("Icon").range(0.1f, 10.0f));
).tab("Icon").visibleWhen(isCustomIcon));
fieldDefs.add(FieldDef.floatField("gui.scale", chainIcon::getScale, chainIcon::setScale).tab("Icon").range(0.1f, 10.0f).visibleWhen(isCustomIcon));
fieldDefs.add(FieldDef.intField("ability.icon.layers", chainIcon::getLayerCount, chainIcon::setLayerCount)
.tab("Icon").range(1, AbilityIconData.MAX_LAYERS));
.tab("Icon").range(1, AbilityIconData.MAX_LAYERS).visibleWhen(isCustomIcon));
for (int i = 0; i < AbilityIconData.MAX_LAYERS; i++) {
final int idx = i;
java.util.function.BooleanSupplier layerVisible = () -> chainIcon.getLayerCount() > idx;
java.util.function.BooleanSupplier layerVisible = () -> isCustomIcon.getAsBoolean() && chainIcon.getLayerCount() > idx;
String sectionLabel = net.minecraft.util.StatCollector.translateToLocal("ability.icon.layer") + " " + (idx + 1);
fieldDefs.add(FieldDef.section(sectionLabel).tab("Icon").visibleWhen(layerVisible));
fieldDefs.add(FieldDef.textureSubGui("gui.texture",
@@ -194,12 +203,12 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
}
// Animation
fieldDefs.add(FieldDef.section("Animation").tab("Icon"));
fieldDefs.add(FieldDef.boolField("gui.animated", chainIcon::isAnimated, chainIcon::setAnimated).tab("Icon").hover("gui.animated.hover"));
fieldDefs.add(FieldDef.section("Animation").tab("Icon").visibleWhen(isCustomIcon));
fieldDefs.add(FieldDef.boolField("gui.animated", chainIcon::isAnimated, chainIcon::setAnimated).tab("Icon").hover("gui.animated.hover").visibleWhen(isCustomIcon));
fieldDefs.add(FieldDef.intField("gui.frameCount", chainIcon::getFrameCount, chainIcon::setFrameCount)
.tab("Icon").range(1, 256).visibleWhen(chainIcon::isAnimated));
.tab("Icon").range(1, 256).visibleWhen(() -> isCustomIcon.getAsBoolean() && chainIcon.isAnimated()));
fieldDefs.add(FieldDef.intField("gui.frameTime", chainIcon::getFrameTime, chainIcon::setFrameTime)
.tab("Icon").range(1, 100).visibleWhen(chainIcon::isAnimated));
.tab("Icon").range(1, 100).visibleWhen(() -> isCustomIcon.getAsBoolean() && chainIcon.isAnimated()));
// External field providers (e.g., DBC Addon injecting a "DBC" tab)
if (AbilityController.Instance != null) {
@@ -4,6 +4,7 @@ import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.AbilityController;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.packets.player.ability.AbilityHotbarSelectPacket;
@@ -638,9 +639,12 @@ public class AbilityHotbarComponent extends HudComponent {
String key = slots[slotIndex].abilityKey;
Ability ability = slots[slotIndex].ability;
IAbilityAction action = slots[slotIndex].action;
if (ability != null && ability.isPerAbilityCooldown()) {
return playerData.abilityData.getPerAbilityCooldownProgress(key);
} else if (action instanceof ChainedAbility && ((ChainedAbility) action).isPerAbilityCooldown()) {
return playerData.abilityData.getPerAbilityCooldownProgress(key);
} else {
return playerData.abilityData.getGlobalCooldownProgress();
}
@@ -237,9 +237,19 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
@Override
protected void rollChainCooldown(ChainedAbility chain) {
if (chain.isIgnoreCooldown()) return;
int duration = chain.getCooldownTicks();
cooldownEndTime = getWorldTime() + duration;
globalCooldownDuration = duration;
long endTime = getWorldTime() + duration;
if (chain.isPerAbilityCooldown()) {
if (currentAbilityKey != null) {
setPerAbilityCooldown(currentAbilityKey, endTime, duration);
}
} else {
cooldownEndTime = endTime;
globalCooldownDuration = duration;
}
}
@Override
@@ -403,7 +413,14 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
// Dispatch: chain vs ability
if (action.isChain()) {
if (isOnCooldown()) return false;
ChainedAbility chain = (ChainedAbility) action;
if (!chain.isIgnoreCooldown()) {
if (chain.isPerAbilityCooldown()) {
if (isOnPerAbilityCooldown(key)) return false;
} else {
if (isOnCooldown()) return false;
}
}
currentAbilityKey = key;
currentTarget = null;
@@ -1736,6 +1736,7 @@ ability.weight=Weight
ability.windUpTicks=Wind Up
ability.cooldownTicks=Cooldown
ability.perAbilityCooldown=Per-Ability
ability.ignoreCooldown=Ignore Cooldown
ability.freeOnCast=Free on Cast
ability.hover.freeOnCast=Ability enters cooldown immediately after entities are spawned, allowing the caster to use other abilities while summoned entities remain active
ability.freeAim=Free Aim