mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 19:34:09 +00:00
Simplify ability interrupt logic and reorganize GUI
- Remove interruptThreshold field entirely - interrupts now based on damage source type - Update canInterrupt() to only allow interrupts from direct physical hits (not magic/fire/explosions) - Interruption only occurs during WINDUP phase, not ACTIVE - Reorganize SubGuiAbilityConfig into 4 tabs: General, Type, Target, Effects - Increase GUI size to 420x216 for better layout
This commit is contained in:
3 files changed
+314
-286
No files matched your search
@@ -51,7 +51,6 @@ public abstract class Ability implements IAbility {
|
||||
|
||||
// Interruption
|
||||
protected boolean interruptible = true;
|
||||
protected float interruptThreshold = 10.0f;
|
||||
|
||||
// Feedback
|
||||
protected boolean lockMovement = true;
|
||||
@@ -451,9 +450,36 @@ public abstract class Ability implements IAbility {
|
||||
return phase != AbilityPhase.IDLE;
|
||||
}
|
||||
|
||||
/** Check if can be interrupted by this damage */
|
||||
public boolean canInterrupt(float damage) {
|
||||
return interruptible && damage >= interruptThreshold && phase == AbilityPhase.WINDUP;
|
||||
/**
|
||||
* Check if can be interrupted by this damage source.
|
||||
* Only direct physical hits (not magic, fire, or other indirect damage) can interrupt.
|
||||
* Interruption only occurs during WINDUP phase - if ability is already active, it completes.
|
||||
*
|
||||
* @param source The damage source
|
||||
* @return true if the ability should be interrupted
|
||||
*/
|
||||
public boolean canInterrupt(DamageSource source) {
|
||||
if (!interruptible || phase != AbilityPhase.WINDUP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only direct physical hits can interrupt, not magic, fire, or other indirect damage
|
||||
if (source == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reject indirect damage types
|
||||
if (source.isMagicDamage() || source.isFireDamage() || source.isExplosion()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reject damage without a direct attacker entity
|
||||
if (source.getEntity() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Direct hit from an entity - can interrupt
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Reset all execution state (called on combat end) */
|
||||
@@ -507,7 +533,6 @@ public abstract class Ability implements IAbility {
|
||||
nbt.setInteger("active", activeTicks);
|
||||
nbt.setInteger("recovery", recoveryTicks);
|
||||
nbt.setBoolean("interruptible", interruptible);
|
||||
nbt.setFloat("interruptThreshold", interruptThreshold);
|
||||
nbt.setBoolean("lockMovement", lockMovement);
|
||||
nbt.setInteger("windUpColor", windUpColor);
|
||||
nbt.setInteger("activeColor", activeColor);
|
||||
@@ -547,7 +572,6 @@ public abstract class Ability implements IAbility {
|
||||
activeTicks = nbt.getInteger("active");
|
||||
recoveryTicks = nbt.getInteger("recovery");
|
||||
interruptible = nbt.getBoolean("interruptible");
|
||||
interruptThreshold = nbt.getFloat("interruptThreshold");
|
||||
lockMovement = nbt.getBoolean("lockMovement");
|
||||
// Support old telegraphColor key for backwards compatibility
|
||||
if (nbt.hasKey("windUpColor")) {
|
||||
@@ -643,9 +667,6 @@ public abstract class Ability implements IAbility {
|
||||
public boolean isInterruptible() { return interruptible; }
|
||||
public void setInterruptible(boolean interruptible) { this.interruptible = interruptible; }
|
||||
|
||||
public float getInterruptThreshold() { return interruptThreshold; }
|
||||
public void setInterruptThreshold(float interruptThreshold) { this.interruptThreshold = interruptThreshold; }
|
||||
|
||||
public boolean isLockMovement() { return lockMovement; }
|
||||
public void setLockMovement(boolean lockMovement) { this.lockMovement = lockMovement; }
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ public class DataAbilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentAbility.canInterrupt(amount)) {
|
||||
if (currentAbility.canInterrupt(source)) {
|
||||
interruptCurrentAbility(source, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,18 +3,22 @@ package noppes.npcs.client.gui.advanced;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.telegraph.TelegraphType;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.client.gui.SubGuiColorSelector;
|
||||
import noppes.npcs.client.gui.select.GuiAnimationSelection;
|
||||
import noppes.npcs.client.gui.select.GuiSoundSelection;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.client.gui.util.*;
|
||||
|
||||
/**
|
||||
* Base SubGui for editing ability configuration with tabbed interface.
|
||||
* Uses GuiMenuTopButton for navigation tabs.
|
||||
*
|
||||
* Tabs: General, Type (if hasTypeSettings), Timing, Effects, Telegraph (if supported)
|
||||
* Tabs:
|
||||
* - General: Type ID, Name, Weight, Lock Movement, Interruptible, Timing (Windup/Active/Recovery/Cooldown)
|
||||
* - Type: Type-specific settings per Ability (override in subclasses)
|
||||
* - Target: Min/Max Range, Targeting Mode, Conditions
|
||||
* - Effects: Sounds, Animations, Telegraph settings, Colors
|
||||
*
|
||||
* Subclasses should override initTypeTab() and handleTypeButton()/handleTypeTextField()
|
||||
* to provide type-specific settings UI.
|
||||
@@ -24,9 +28,8 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
// Tab constants
|
||||
protected static final int TAB_GENERAL = 0;
|
||||
protected static final int TAB_TYPE = 1;
|
||||
protected static final int TAB_TIMING = 2;
|
||||
protected static final int TAB_TARGET = 2;
|
||||
protected static final int TAB_EFFECTS = 3;
|
||||
protected static final int TAB_TELEGRAPH = 4;
|
||||
|
||||
// Core references
|
||||
protected final Ability ability;
|
||||
@@ -34,36 +37,29 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
protected int activeTab = TAB_GENERAL;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CACHED VALUES - General
|
||||
// CACHED VALUES - General Tab
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
protected String name;
|
||||
protected boolean enabled;
|
||||
protected int weight;
|
||||
protected boolean lockMovement;
|
||||
protected boolean interruptible;
|
||||
|
||||
// Timing
|
||||
protected int cooldownTicks;
|
||||
protected int windUpTicks;
|
||||
protected int activeTicks;
|
||||
protected int recoveryTicks;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CACHED VALUES - Target Tab
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
protected float minRange;
|
||||
protected float maxRange;
|
||||
protected TargetingMode targetingMode;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CACHED VALUES - Timing
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
protected int cooldownTicks;
|
||||
protected int windUpTicks;
|
||||
protected int activeTicks;
|
||||
protected int recoveryTicks;
|
||||
protected boolean interruptible;
|
||||
protected float interruptThreshold;
|
||||
protected boolean lockMovement;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CACHED VALUES - Telegraph
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
protected boolean showTelegraph;
|
||||
protected int windUpColor;
|
||||
protected int activeColor;
|
||||
protected int editingColorId = 0;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CACHED VALUES - Effects
|
||||
// CACHED VALUES - Effects Tab
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
protected String windUpSound;
|
||||
protected String activeSound;
|
||||
@@ -71,6 +67,14 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
protected int activeAnimationId;
|
||||
protected String windUpAnimationName = null;
|
||||
protected String activeAnimationName = null;
|
||||
|
||||
// Telegraph
|
||||
protected boolean showTelegraph;
|
||||
protected int windUpColor;
|
||||
protected int activeColor;
|
||||
|
||||
// Editing state
|
||||
protected int editingColorId = 0;
|
||||
protected int editingSoundId = 0;
|
||||
protected int editingAnimationId = 0;
|
||||
|
||||
@@ -85,31 +89,35 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
this.ability = ability;
|
||||
this.callback = callback;
|
||||
|
||||
// Cache values from ability
|
||||
// Cache values from ability - General
|
||||
this.name = ability.getName() != null ? ability.getName() : "";
|
||||
this.enabled = ability.isEnabled();
|
||||
this.weight = ability.getWeight();
|
||||
this.minRange = ability.getMinRange();
|
||||
this.maxRange = ability.getMaxRange();
|
||||
this.targetingMode = ability.getTargetingMode();
|
||||
this.lockMovement = ability.isLockMovement();
|
||||
this.interruptible = ability.isInterruptible();
|
||||
|
||||
// Timing
|
||||
this.cooldownTicks = ability.getCooldownTicks();
|
||||
this.windUpTicks = ability.getWindUpTicks();
|
||||
this.activeTicks = ability.getActiveTicks();
|
||||
this.recoveryTicks = ability.getRecoveryTicks();
|
||||
this.interruptible = ability.isInterruptible();
|
||||
this.interruptThreshold = ability.getInterruptThreshold();
|
||||
this.lockMovement = ability.isLockMovement();
|
||||
|
||||
this.showTelegraph = ability.isShowTelegraph();
|
||||
this.windUpColor = ability.getWindUpColor();
|
||||
this.activeColor = ability.getActiveColor();
|
||||
// Target
|
||||
this.minRange = ability.getMinRange();
|
||||
this.maxRange = ability.getMaxRange();
|
||||
this.targetingMode = ability.getTargetingMode();
|
||||
|
||||
// Effects
|
||||
this.windUpSound = ability.getWindUpSound();
|
||||
this.activeSound = ability.getActiveSound();
|
||||
this.windUpAnimationId = ability.getWindUpAnimationId();
|
||||
this.activeAnimationId = ability.getActiveAnimationId();
|
||||
|
||||
// Telegraph
|
||||
this.showTelegraph = ability.isShowTelegraph();
|
||||
this.windUpColor = ability.getWindUpColor();
|
||||
this.activeColor = ability.getActiveColor();
|
||||
|
||||
// Determine ability type features
|
||||
TelegraphType defaultType = ability.getTelegraphType();
|
||||
this.supportsTelegraph = defaultType != null && defaultType != TelegraphType.NONE;
|
||||
@@ -117,7 +125,7 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
this.hasTypeSettings = ability.hasTypeSettings();
|
||||
|
||||
setBackground("menubg.png");
|
||||
xSize = 276;
|
||||
xSize = 420;
|
||||
ySize = 216;
|
||||
}
|
||||
|
||||
@@ -141,21 +149,14 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
lastTab = typeTab;
|
||||
}
|
||||
|
||||
GuiMenuTopButton timingTab = new GuiMenuTopButton(92, lastTab, "ability.tab.timing");
|
||||
timingTab.active = (activeTab == TAB_TIMING);
|
||||
addTopButton(timingTab);
|
||||
lastTab = timingTab;
|
||||
GuiMenuTopButton targetTab = new GuiMenuTopButton(92, lastTab, "ability.tab.target");
|
||||
targetTab.active = (activeTab == TAB_TARGET);
|
||||
addTopButton(targetTab);
|
||||
lastTab = targetTab;
|
||||
|
||||
GuiMenuTopButton effectsTab = new GuiMenuTopButton(94, lastTab, "ability.tab.effects");
|
||||
GuiMenuTopButton effectsTab = new GuiMenuTopButton(93, lastTab, "ability.tab.effects");
|
||||
effectsTab.active = (activeTab == TAB_EFFECTS);
|
||||
addTopButton(effectsTab);
|
||||
lastTab = effectsTab;
|
||||
|
||||
if (supportsTelegraph) {
|
||||
GuiMenuTopButton telegraphTab = new GuiMenuTopButton(93, lastTab, "ability.tab.telegraph");
|
||||
telegraphTab.active = (activeTab == TAB_TELEGRAPH);
|
||||
addTopButton(telegraphTab);
|
||||
}
|
||||
|
||||
// Close button on top right
|
||||
GuiMenuTopButton closeBtn = new GuiMenuTopButton(67, guiLeft + xSize - 22, guiTop - 17, "X");
|
||||
@@ -173,15 +174,12 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
case TAB_TYPE:
|
||||
initTypeTab(contentY);
|
||||
break;
|
||||
case TAB_TIMING:
|
||||
initTimingTab(contentY);
|
||||
case TAB_TARGET:
|
||||
initTargetTab(contentY);
|
||||
break;
|
||||
case TAB_EFFECTS:
|
||||
initEffectsTab(contentY);
|
||||
break;
|
||||
case TAB_TELEGRAPH:
|
||||
initTelegraphTab(contentY);
|
||||
break;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
@@ -197,35 +195,130 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// GENERAL TAB
|
||||
// Type ID (label), Name, Weight, Lock Movement, Interruptible,
|
||||
// Ticks: Windup, Active, Recovery, Cooldown
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void initGeneralTab(int startY) {
|
||||
int y = startY;
|
||||
int labelX = guiLeft + 8;
|
||||
int fieldX = guiLeft + 85;
|
||||
int col2LabelX = guiLeft + 145;
|
||||
int col2FieldX = guiLeft + 205;
|
||||
int col1LabelX = guiLeft + 8;
|
||||
int col1FieldX = guiLeft + 75;
|
||||
int col2LabelX = guiLeft + 215;
|
||||
int col2FieldX = guiLeft + 290;
|
||||
|
||||
// Row 1: Name + Weight
|
||||
addLabel(new GuiNpcLabel(1, "gui.name", labelX, y + 5));
|
||||
addTextField(new GuiNpcTextField(1, this, fontRendererObj, fieldX, y, 100, 20, name));
|
||||
// Row 1: Type ID (read-only) + Name
|
||||
addLabel(new GuiNpcLabel(1, "ability.typeId", col1LabelX, y + 5));
|
||||
addLabel(new GuiNpcLabel(2, ability.getTypeId(), col1FieldX, y + 5));
|
||||
|
||||
addLabel(new GuiNpcLabel(3, "ability.weight", col2LabelX, y + 5));
|
||||
GuiNpcTextField weightField = new GuiNpcTextField(3, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf(weight));
|
||||
addLabel(new GuiNpcLabel(3, "gui.name", col2LabelX, y + 5));
|
||||
GuiNpcTextField nameField = new GuiNpcTextField(1, this, fontRendererObj, col2FieldX, y, 120, 20, name);
|
||||
addTextField(nameField);
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 2: Weight + Lock Movement
|
||||
addLabel(new GuiNpcLabel(4, "ability.weight", col1LabelX, y + 5));
|
||||
GuiNpcTextField weightField = new GuiNpcTextField(3, this, fontRendererObj, col1FieldX, y, 50, 20, String.valueOf(weight));
|
||||
weightField.setIntegersOnly();
|
||||
weightField.setMinMaxDefault(1, 1000, 10);
|
||||
addTextField(weightField);
|
||||
|
||||
addLabel(new GuiNpcLabel(5, "ability.lockMove", col2LabelX, y + 5));
|
||||
addButton(new GuiNpcButton(16, col2FieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, lockMovement ? 1 : 0));
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 2: Min Range + Max Range
|
||||
addLabel(new GuiNpcLabel(5, "ability.minRange", labelX, y + 5));
|
||||
GuiNpcTextField minRangeField = new GuiNpcTextField(5, this, fontRendererObj, fieldX, y, 50, 20, String.valueOf((int) minRange));
|
||||
// Row 3: Interruptible (no threshold anymore - just a toggle)
|
||||
addLabel(new GuiNpcLabel(6, "ability.interruptible", col1LabelX, y + 5));
|
||||
addButton(new GuiNpcButton(14, col1FieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, interruptible ? 1 : 0));
|
||||
|
||||
y += 30;
|
||||
|
||||
// Separator - Timing section header
|
||||
addLabel(new GuiNpcLabel(7, "§n" + "ability.timing", col1LabelX, y));
|
||||
y += 14;
|
||||
|
||||
// Row 4: Windup Ticks + Active Ticks
|
||||
addLabel(new GuiNpcLabel(10, "ability.windup", col1LabelX, y + 5));
|
||||
GuiNpcTextField windupField = new GuiNpcTextField(11, this, fontRendererObj, col1FieldX, y, 50, 20, String.valueOf(windUpTicks));
|
||||
windupField.setIntegersOnly();
|
||||
windupField.setMinMaxDefault(0, 1000, 20);
|
||||
addTextField(windupField);
|
||||
|
||||
addLabel(new GuiNpcLabel(11, "ability.active", col2LabelX, y + 5));
|
||||
GuiNpcTextField activeField = new GuiNpcTextField(12, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf(activeTicks));
|
||||
activeField.setIntegersOnly();
|
||||
activeField.setMinMaxDefault(1, 1000, 10);
|
||||
addTextField(activeField);
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 5: Recovery Ticks + Cooldown Ticks
|
||||
addLabel(new GuiNpcLabel(12, "ability.recovery", col1LabelX, y + 5));
|
||||
GuiNpcTextField recoveryField = new GuiNpcTextField(13, this, fontRendererObj, col1FieldX, y, 50, 20, String.valueOf(recoveryTicks));
|
||||
recoveryField.setIntegersOnly();
|
||||
recoveryField.setMinMaxDefault(0, 1000, 20);
|
||||
addTextField(recoveryField);
|
||||
|
||||
addLabel(new GuiNpcLabel(13, "ability.cooldown", col2LabelX, y + 5));
|
||||
GuiNpcTextField cooldownField = new GuiNpcTextField(10, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf(cooldownTicks));
|
||||
cooldownField.setIntegersOnly();
|
||||
cooldownField.setMinMaxDefault(0, 10000, 100);
|
||||
addTextField(cooldownField);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// TYPE TAB - Override in subclasses for type-specific settings
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Override this method to render type-specific settings in the Type tab.
|
||||
* Use the helper methods createFloatField(), createIntField(), etc.
|
||||
* Button/TextField IDs should start at 100.
|
||||
*
|
||||
* @param startY The Y position to start rendering from
|
||||
*/
|
||||
protected void initTypeTab(int startY) {
|
||||
// Default: show message that no type settings are available
|
||||
addLabel(new GuiNpcLabel(100, "ability.noTypeSettings", guiLeft + 8, startY + 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to handle button clicks for type-specific buttons.
|
||||
* Button IDs 100+ are reserved for type-specific use.
|
||||
*/
|
||||
protected void handleTypeButton(int id, GuiNpcButton button) {
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to handle text field changes for type-specific fields.
|
||||
* Field IDs 100+ are reserved for type-specific use.
|
||||
*/
|
||||
protected void handleTypeTextField(int id, GuiNpcTextField field) {
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// TARGET TAB
|
||||
// Min/Max Range, Targeting Mode, Conditions
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void initTargetTab(int startY) {
|
||||
int y = startY;
|
||||
int col1LabelX = guiLeft + 8;
|
||||
int col1FieldX = guiLeft + 85;
|
||||
int col2LabelX = guiLeft + 215;
|
||||
int col2FieldX = guiLeft + 290;
|
||||
|
||||
// Row 1: Min Range + Max Range
|
||||
addLabel(new GuiNpcLabel(20, "ability.minRange", col1LabelX, y + 5));
|
||||
GuiNpcTextField minRangeField = new GuiNpcTextField(5, this, fontRendererObj, col1FieldX, y, 50, 20, String.valueOf((int) minRange));
|
||||
minRangeField.setIntegersOnly();
|
||||
minRangeField.setMinMaxDefault(0, 100, 0);
|
||||
addTextField(minRangeField);
|
||||
|
||||
addLabel(new GuiNpcLabel(6, "ability.maxRange", col2LabelX, y + 5));
|
||||
addLabel(new GuiNpcLabel(21, "ability.maxRange", col2LabelX, y + 5));
|
||||
GuiNpcTextField maxRangeField = new GuiNpcTextField(6, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf((int) maxRange));
|
||||
maxRangeField.setIntegersOnly();
|
||||
maxRangeField.setMinMaxDefault(1, 100, 20);
|
||||
@@ -233,13 +326,26 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 3: Targeting Mode (if not locked)
|
||||
// Row 2: Targeting Mode (if not locked)
|
||||
addLabel(new GuiNpcLabel(22, "ability.targeting", col1LabelX, y + 5));
|
||||
if (!targetingModeLocked) {
|
||||
addLabel(new GuiNpcLabel(4, "ability.targeting", labelX, y + 5));
|
||||
String[] targetingModes = getAvailableTargetingModes();
|
||||
int selectedIndex = getTargetingModeIndex(targetingMode);
|
||||
addButton(new GuiNpcButton(4, fieldX, y, 80, 20, targetingModes, selectedIndex));
|
||||
addButton(new GuiNpcButton(4, col1FieldX, y, 100, 20, targetingModes, selectedIndex));
|
||||
} else {
|
||||
// Show as label if locked
|
||||
String modeName = "ability.target." + targetingMode.name().toLowerCase();
|
||||
addLabel(new GuiNpcLabel(23, modeName, col1FieldX, y + 5));
|
||||
}
|
||||
|
||||
y += 30;
|
||||
|
||||
// Separator - Conditions section header
|
||||
addLabel(new GuiNpcLabel(24, "§n" + "ability.conditions", col1LabelX, y));
|
||||
y += 14;
|
||||
|
||||
// TODO: Add conditions UI here
|
||||
addLabel(new GuiNpcLabel(25, "ability.conditionsNote", col1LabelX, y + 5));
|
||||
}
|
||||
|
||||
private String[] getAvailableTargetingModes() {
|
||||
@@ -265,142 +371,91 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// TYPE TAB - Override in subclasses for type-specific settings
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Override this method to render type-specific settings in the Type tab.
|
||||
* Use the helper methods createFloatField(), createIntField(), etc.
|
||||
* Button/TextField IDs should start at 100.
|
||||
*
|
||||
* @param startY The Y position to start rendering from
|
||||
*/
|
||||
protected void initTypeTab(int startY) {
|
||||
// Default: show message that no type settings are available
|
||||
addLabel(new GuiNpcLabel(100, "ability.noTypeSettings", guiLeft + 8, startY + 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to handle button clicks for type-specific buttons.
|
||||
* Button IDs 100+ are reserved for type-specific use.
|
||||
*
|
||||
* @param id The button ID
|
||||
* @param button The button that was clicked
|
||||
*/
|
||||
protected void handleTypeButton(int id, GuiNpcButton button) {
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to handle text field changes for type-specific fields.
|
||||
* Field IDs 100+ are reserved for type-specific use.
|
||||
*
|
||||
* @param id The field ID
|
||||
* @param field The text field that changed
|
||||
*/
|
||||
protected void handleTypeTextField(int id, GuiNpcTextField field) {
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// TIMING TAB
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void initTimingTab(int startY) {
|
||||
int y = startY;
|
||||
int labelX = guiLeft + 8;
|
||||
int fieldX = guiLeft + 85;
|
||||
int col2LabelX = guiLeft + 145;
|
||||
int col2FieldX = guiLeft + 205;
|
||||
|
||||
// Row 1: Cooldown + Windup
|
||||
addLabel(new GuiNpcLabel(10, "ability.cooldown", labelX, y + 5));
|
||||
GuiNpcTextField cooldownField = new GuiNpcTextField(10, this, fontRendererObj, fieldX, y, 50, 20, String.valueOf(cooldownTicks));
|
||||
cooldownField.setIntegersOnly();
|
||||
cooldownField.setMinMaxDefault(0, 10000, 100);
|
||||
addTextField(cooldownField);
|
||||
|
||||
addLabel(new GuiNpcLabel(11, "ability.windup", col2LabelX, y + 5));
|
||||
GuiNpcTextField windupField = new GuiNpcTextField(11, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf(windUpTicks));
|
||||
windupField.setIntegersOnly();
|
||||
windupField.setMinMaxDefault(0, 1000, 20);
|
||||
addTextField(windupField);
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 2: Active + Recovery
|
||||
addLabel(new GuiNpcLabel(12, "ability.active", labelX, y + 5));
|
||||
GuiNpcTextField activeField = new GuiNpcTextField(12, this, fontRendererObj, fieldX, y, 50, 20, String.valueOf(activeTicks));
|
||||
activeField.setIntegersOnly();
|
||||
activeField.setMinMaxDefault(1, 1000, 10);
|
||||
addTextField(activeField);
|
||||
|
||||
addLabel(new GuiNpcLabel(13, "ability.recovery", col2LabelX, y + 5));
|
||||
GuiNpcTextField recoveryField = new GuiNpcTextField(13, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf(recoveryTicks));
|
||||
recoveryField.setIntegersOnly();
|
||||
recoveryField.setMinMaxDefault(0, 1000, 20);
|
||||
addTextField(recoveryField);
|
||||
|
||||
y += 28;
|
||||
|
||||
// Row 3: Interruptible + Threshold
|
||||
addLabel(new GuiNpcLabel(14, "ability.interruptible", labelX, y + 5));
|
||||
addButton(new GuiNpcButton(14, fieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, interruptible ? 1 : 0));
|
||||
|
||||
addLabel(new GuiNpcLabel(15, "ability.threshold", col2LabelX, y + 5));
|
||||
GuiNpcTextField thresholdField = new GuiNpcTextField(15, this, fontRendererObj, col2FieldX, y, 50, 20, String.valueOf((int) interruptThreshold));
|
||||
thresholdField.setIntegersOnly();
|
||||
thresholdField.setMinMaxDefault(0, 1000, 10);
|
||||
thresholdField.setEnabled(interruptible);
|
||||
addTextField(thresholdField);
|
||||
|
||||
y += 28;
|
||||
|
||||
// Row 4: Lock Movement
|
||||
addLabel(new GuiNpcLabel(16, "ability.lockMove", labelX, y + 5));
|
||||
addButton(new GuiNpcButton(16, fieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, lockMovement ? 1 : 0));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// EFFECTS TAB
|
||||
// Sounds, Animations, Telegraph, Colors
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void initEffectsTab(int startY) {
|
||||
int y = startY;
|
||||
int labelX = guiLeft + 8;
|
||||
int fieldX = guiLeft + 100;
|
||||
int btnX = guiLeft + 215;
|
||||
int col1LabelX = guiLeft + 8;
|
||||
int col1FieldX = guiLeft + 100;
|
||||
int col1ClearX = guiLeft + 185;
|
||||
int col2LabelX = guiLeft + 215;
|
||||
int col2FieldX = guiLeft + 290;
|
||||
|
||||
// Row 1: Wind Up Sound
|
||||
addLabel(new GuiNpcLabel(30, "ability.windUpSound", labelX, y + 5));
|
||||
String windUpSoundDisplay = windUpSound != null && !windUpSound.isEmpty() ? truncateString(windUpSound, 15) : "None";
|
||||
addButton(new GuiNpcButton(30, fieldX, y, 110, 20, windUpSoundDisplay));
|
||||
addButton(new GuiNpcButton(35, btnX, y, 20, 20, "X"));
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
// SOUNDS SECTION
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
addLabel(new GuiNpcLabel(30, "§n" + "ability.sounds", col1LabelX, y));
|
||||
y += 14;
|
||||
|
||||
y += 25;
|
||||
// Wind Up Sound
|
||||
addLabel(new GuiNpcLabel(31, "ability.windUpSound", col1LabelX, y + 5));
|
||||
String windUpSoundDisplay = windUpSound != null && !windUpSound.isEmpty() ? truncateString(windUpSound, 12) : "None";
|
||||
addButton(new GuiNpcButton(30, col1FieldX, y, 80, 20, windUpSoundDisplay));
|
||||
addButton(new GuiNpcButton(35, col1ClearX, y, 20, 20, "X"));
|
||||
|
||||
// Row 2: Active Sound
|
||||
addLabel(new GuiNpcLabel(31, "ability.activeSound", labelX, y + 5));
|
||||
String activeSoundDisplay = activeSound != null && !activeSound.isEmpty() ? truncateString(activeSound, 15) : "None";
|
||||
addButton(new GuiNpcButton(31, fieldX, y, 110, 20, activeSoundDisplay));
|
||||
addButton(new GuiNpcButton(36, btnX, y, 20, 20, "X"));
|
||||
// Active Sound
|
||||
addLabel(new GuiNpcLabel(32, "ability.activeSound", col2LabelX, y + 5));
|
||||
String activeSoundDisplay = activeSound != null && !activeSound.isEmpty() ? truncateString(activeSound, 12) : "None";
|
||||
addButton(new GuiNpcButton(31, col2FieldX, y, 80, 20, activeSoundDisplay));
|
||||
addButton(new GuiNpcButton(36, col2FieldX + 85, y, 20, 20, "X"));
|
||||
|
||||
y += 30;
|
||||
y += 28;
|
||||
|
||||
// Row 3: Wind Up Animation
|
||||
addLabel(new GuiNpcLabel(32, "ability.windUpAnim", labelX, y + 5));
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
// ANIMATIONS SECTION
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
addLabel(new GuiNpcLabel(33, "§n" + "ability.animations", col1LabelX, y));
|
||||
y += 14;
|
||||
|
||||
// Wind Up Animation
|
||||
addLabel(new GuiNpcLabel(34, "ability.windUpAnim", col1LabelX, y + 5));
|
||||
String windUpAnimName = getAnimationName(windUpAnimationId, true);
|
||||
addButton(new GuiNpcButton(32, fieldX, y, 110, 20, windUpAnimName));
|
||||
addButton(new GuiNpcButton(37, btnX, y, 20, 20, "X"));
|
||||
addButton(new GuiNpcButton(32, col1FieldX, y, 80, 20, windUpAnimName));
|
||||
addButton(new GuiNpcButton(37, col1ClearX, y, 20, 20, "X"));
|
||||
|
||||
y += 25;
|
||||
|
||||
// Row 4: Active Animation
|
||||
addLabel(new GuiNpcLabel(33, "ability.activeAnim", labelX, y + 5));
|
||||
// Active Animation
|
||||
addLabel(new GuiNpcLabel(35, "ability.activeAnim", col2LabelX, y + 5));
|
||||
String activeAnimName = getAnimationName(activeAnimationId, false);
|
||||
addButton(new GuiNpcButton(33, fieldX, y, 110, 20, activeAnimName));
|
||||
addButton(new GuiNpcButton(38, btnX, y, 20, 20, "X"));
|
||||
addButton(new GuiNpcButton(33, col2FieldX, y, 80, 20, activeAnimName));
|
||||
addButton(new GuiNpcButton(38, col2FieldX + 85, y, 20, 20, "X"));
|
||||
|
||||
y += 28;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
// TELEGRAPH SECTION (only if supported)
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
if (supportsTelegraph) {
|
||||
addLabel(new GuiNpcLabel(40, "§n" + "ability.telegraph", col1LabelX, y));
|
||||
y += 14;
|
||||
|
||||
// Show Telegraph + Type
|
||||
addLabel(new GuiNpcLabel(41, "ability.showTelegraph", col1LabelX, y + 5));
|
||||
addButton(new GuiNpcButton(20, col1FieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, showTelegraph ? 1 : 0));
|
||||
|
||||
String typeKey = ability.getTelegraphType().name().toLowerCase();
|
||||
addLabel(new GuiNpcLabel(42, "ability.telegraphType", col2LabelX, y + 5));
|
||||
addLabel(new GuiNpcLabel(43, "ability.telegraph." + typeKey, col2FieldX, y + 5));
|
||||
|
||||
y += 24;
|
||||
|
||||
// Colors
|
||||
addLabel(new GuiNpcLabel(44, "ability.windUpColor", col1LabelX, y + 5));
|
||||
String windUpHex = String.format("%06X", windUpColor & 0xFFFFFF);
|
||||
GuiNpcButton windUpColorBtn = new GuiNpcButton(22, col1FieldX, y, 60, 20, windUpHex);
|
||||
windUpColorBtn.setEnabled(showTelegraph);
|
||||
windUpColorBtn.setTextColor(windUpColor & 0xFFFFFF);
|
||||
addButton(windUpColorBtn);
|
||||
|
||||
addLabel(new GuiNpcLabel(45, "ability.activeColor", col2LabelX, y + 5));
|
||||
String activeHex = String.format("%06X", activeColor & 0xFFFFFF);
|
||||
GuiNpcButton activeColorBtn = new GuiNpcButton(24, col2FieldX, y, 60, 20, activeHex);
|
||||
activeColorBtn.setEnabled(showTelegraph);
|
||||
activeColorBtn.setTextColor(activeColor & 0xFFFFFF);
|
||||
addButton(activeColorBtn);
|
||||
}
|
||||
}
|
||||
|
||||
private String truncateString(String str, int maxLen) {
|
||||
@@ -417,57 +472,11 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
if (animId < 0) return "None";
|
||||
String cachedName = isWindUp ? windUpAnimationName : activeAnimationName;
|
||||
if (cachedName != null && !cachedName.isEmpty()) {
|
||||
return cachedName;
|
||||
return truncateString(cachedName, 10);
|
||||
}
|
||||
return "ID: " + animId;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// TELEGRAPH TAB
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void initTelegraphTab(int startY) {
|
||||
int y = startY;
|
||||
int labelX = guiLeft + 8;
|
||||
int fieldX = guiLeft + 110;
|
||||
|
||||
// Row 1: Show Telegraph
|
||||
addLabel(new GuiNpcLabel(20, "ability.showTelegraph", labelX, y + 5));
|
||||
addButton(new GuiNpcButton(20, fieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, showTelegraph ? 1 : 0));
|
||||
|
||||
y += 25;
|
||||
|
||||
// Row 2: Wind Up Color
|
||||
addLabel(new GuiNpcLabel(21, "ability.windUpColor", labelX, y + 5));
|
||||
String windUpHex = String.format("%06X", windUpColor & 0xFFFFFF);
|
||||
GuiNpcButton windUpBtn = new GuiNpcButton(22, fieldX, y, 70, 20, windUpHex);
|
||||
windUpBtn.setEnabled(showTelegraph);
|
||||
windUpBtn.setTextColor(windUpColor & 0xFFFFFF);
|
||||
addButton(windUpBtn);
|
||||
|
||||
y += 25;
|
||||
|
||||
// Row 3: Active Color
|
||||
addLabel(new GuiNpcLabel(23, "ability.activeColor", labelX, y + 5));
|
||||
String activeHex = String.format("%06X", activeColor & 0xFFFFFF);
|
||||
GuiNpcButton activeBtn = new GuiNpcButton(24, fieldX, y, 70, 20, activeHex);
|
||||
activeBtn.setEnabled(showTelegraph);
|
||||
activeBtn.setTextColor(activeColor & 0xFFFFFF);
|
||||
addButton(activeBtn);
|
||||
|
||||
y += 30;
|
||||
|
||||
// Info: Telegraph type
|
||||
String typeKey = "ability.telegraph." + ability.getTelegraphType().name().toLowerCase();
|
||||
addLabel(new GuiNpcLabel(25, "ability.telegraphType", labelX, y));
|
||||
addLabel(new GuiNpcLabel(26, typeKey, fieldX, y));
|
||||
|
||||
y += 18;
|
||||
|
||||
// Info note
|
||||
addLabel(new GuiNpcLabel(27, "ability.telegraphSizeNote", labelX, y));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// BUTTON EVENTS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
@@ -488,16 +497,11 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
return;
|
||||
}
|
||||
if (id == 92) {
|
||||
activeTab = TAB_TIMING;
|
||||
activeTab = TAB_TARGET;
|
||||
initGui();
|
||||
return;
|
||||
}
|
||||
if (id == 93 && supportsTelegraph) {
|
||||
activeTab = TAB_TELEGRAPH;
|
||||
initGui();
|
||||
return;
|
||||
}
|
||||
if (id == 94) {
|
||||
if (id == 93) {
|
||||
activeTab = TAB_EFFECTS;
|
||||
initGui();
|
||||
return;
|
||||
@@ -508,7 +512,16 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
enabled = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
initGui();
|
||||
}
|
||||
// Targeting mode
|
||||
|
||||
// General tab buttons
|
||||
else if (id == 14) {
|
||||
interruptible = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
}
|
||||
else if (id == 16) {
|
||||
lockMovement = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
}
|
||||
|
||||
// Target tab - Targeting mode
|
||||
else if (id == 4) {
|
||||
TargetingMode[] modes = ability.getAllowedTargetingModes();
|
||||
int idx = ((GuiNpcButton) guibutton).getValue();
|
||||
@@ -519,15 +532,7 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
}
|
||||
}
|
||||
|
||||
// Timing tab buttons
|
||||
else if (id == 14) {
|
||||
interruptible = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
initGui();
|
||||
} else if (id == 16) {
|
||||
lockMovement = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
}
|
||||
|
||||
// Telegraph tab buttons
|
||||
// Effects tab - Telegraph
|
||||
else if (id == 20) {
|
||||
showTelegraph = ((GuiNpcButton) guibutton).getValue() == 1;
|
||||
initGui();
|
||||
@@ -610,13 +615,16 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
name = textField.getText();
|
||||
} else if (id == 3) {
|
||||
weight = textField.getInteger();
|
||||
} else if (id == 5) {
|
||||
}
|
||||
|
||||
// Target tab
|
||||
else if (id == 5) {
|
||||
minRange = textField.getInteger();
|
||||
} else if (id == 6) {
|
||||
maxRange = textField.getInteger();
|
||||
}
|
||||
|
||||
// Timing tab
|
||||
// Timing (General tab)
|
||||
else if (id == 10) {
|
||||
cooldownTicks = textField.getInteger();
|
||||
} else if (id == 11) {
|
||||
@@ -625,8 +633,6 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
activeTicks = textField.getInteger();
|
||||
} else if (id == 13) {
|
||||
recoveryTicks = textField.getInteger();
|
||||
} else if (id == 15) {
|
||||
interruptThreshold = textField.getInteger();
|
||||
}
|
||||
|
||||
// Type-specific fields (100+) - delegate to subclass
|
||||
@@ -686,23 +692,19 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
ability.setName(name);
|
||||
ability.setEnabled(enabled);
|
||||
ability.setWeight(weight);
|
||||
ability.setMinRange(minRange);
|
||||
ability.setMaxRange(maxRange);
|
||||
ability.setTargetingMode(targetingMode);
|
||||
ability.setLockMovement(lockMovement);
|
||||
ability.setInterruptible(interruptible);
|
||||
|
||||
// Timing
|
||||
ability.setCooldownTicks(cooldownTicks);
|
||||
ability.setWindUpTicks(windUpTicks);
|
||||
ability.setActiveTicks(activeTicks);
|
||||
ability.setRecoveryTicks(recoveryTicks);
|
||||
ability.setInterruptible(interruptible);
|
||||
ability.setInterruptThreshold(interruptThreshold);
|
||||
ability.setLockMovement(lockMovement);
|
||||
|
||||
// Telegraph
|
||||
ability.setShowTelegraph(showTelegraph);
|
||||
ability.setWindUpColor(windUpColor);
|
||||
ability.setActiveColor(activeColor);
|
||||
// Target
|
||||
ability.setMinRange(minRange);
|
||||
ability.setMaxRange(maxRange);
|
||||
ability.setTargetingMode(targetingMode);
|
||||
|
||||
// Effects
|
||||
ability.setWindUpSound(windUpSound);
|
||||
@@ -710,6 +712,11 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
ability.setWindUpAnimationId(windUpAnimationId);
|
||||
ability.setActiveAnimationId(activeAnimationId);
|
||||
|
||||
// Telegraph
|
||||
ability.setShowTelegraph(showTelegraph);
|
||||
ability.setWindUpColor(windUpColor);
|
||||
ability.setActiveColor(activeColor);
|
||||
|
||||
// Type-specific values are applied directly in handleTypeTextField and handleTypeButton
|
||||
}
|
||||
|
||||
@@ -729,17 +736,17 @@ public class SubGuiAbilityConfig extends SubGuiInterface implements ITextfieldLi
|
||||
this.name = ability.getName() != null ? ability.getName() : "";
|
||||
this.enabled = ability.isEnabled();
|
||||
this.weight = ability.getWeight();
|
||||
this.minRange = ability.getMinRange();
|
||||
this.maxRange = ability.getMaxRange();
|
||||
this.targetingMode = ability.getTargetingMode();
|
||||
this.lockMovement = ability.isLockMovement();
|
||||
this.interruptible = ability.isInterruptible();
|
||||
|
||||
this.cooldownTicks = ability.getCooldownTicks();
|
||||
this.windUpTicks = ability.getWindUpTicks();
|
||||
this.activeTicks = ability.getActiveTicks();
|
||||
this.recoveryTicks = ability.getRecoveryTicks();
|
||||
this.interruptible = ability.isInterruptible();
|
||||
this.interruptThreshold = ability.getInterruptThreshold();
|
||||
this.lockMovement = ability.isLockMovement();
|
||||
|
||||
this.minRange = ability.getMinRange();
|
||||
this.maxRange = ability.getMaxRange();
|
||||
this.targetingMode = ability.getTargetingMode();
|
||||
|
||||
this.showTelegraph = ability.isShowTelegraph();
|
||||
this.windUpColor = ability.getWindUpColor();
|
||||
|
||||
Reference in new issue
Block a user