mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Merge remote-tracking branch 'origin/dev' into new-abilities
This commit is contained in:
commit
fb3a392f2e
16 files changed
+261
-51
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ plugins {
|
||||
id "dts.typescript-generator"
|
||||
}
|
||||
|
||||
version = "1.11"
|
||||
version = "1.11.1"
|
||||
group= "kamkeel.customnpc-plus"
|
||||
archivesBaseName = "CustomNPC-Plus"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,9 +10,11 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.api.ability.type.IAbilityDefend;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.controllers.data.Animation;
|
||||
import noppes.npcs.scripted.event.AbilityEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -147,8 +149,14 @@ public abstract class AbilityDefend extends Ability implements IAbilityDefend {
|
||||
}
|
||||
}
|
||||
|
||||
//inject defend event
|
||||
AbilityEvent.DefendEvent event = new AbilityEvent.DefendEvent(caster, this, attacker, lastAttacker, amount);
|
||||
if (EventHooks.onAbilityDefend(this, event)) {
|
||||
return event.getDamage();
|
||||
}
|
||||
|
||||
// Always calculate the correct reduction for the caller's damage amount
|
||||
return performDefend(attacker, amount);
|
||||
return performDefend(attacker, event.getDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ public final class JobSpawnerAddPacket extends AbstractPacket {
|
||||
|
||||
private String selected;
|
||||
private int activeTab;
|
||||
private String folderName;
|
||||
private int slot;
|
||||
|
||||
private NBTTagCompound compound;
|
||||
@@ -39,6 +40,15 @@ public final class JobSpawnerAddPacket extends AbstractPacket {
|
||||
this.isServer = isServer;
|
||||
this.selected = selected;
|
||||
this.activeTab = activeTab;
|
||||
this.folderName = null;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public JobSpawnerAddPacket(boolean isServer, String selected, String folderName, int slot) {
|
||||
this.isServer = isServer;
|
||||
this.selected = selected;
|
||||
this.activeTab = -1;
|
||||
this.folderName = folderName;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@@ -75,6 +85,9 @@ public final class JobSpawnerAddPacket extends AbstractPacket {
|
||||
if (this.isServer) {
|
||||
ByteBufUtils.writeString(out, selected);
|
||||
out.writeInt(this.activeTab);
|
||||
if (this.activeTab == -1) {
|
||||
ByteBufUtils.writeString(out, folderName != null ? folderName : "");
|
||||
}
|
||||
out.writeInt(this.slot);
|
||||
} else {
|
||||
out.writeInt(this.slot);
|
||||
@@ -96,7 +109,15 @@ public final class JobSpawnerAddPacket extends AbstractPacket {
|
||||
JobSpawner job = (JobSpawner) npc.jobInterface;
|
||||
boolean useServerClone = in.readBoolean();
|
||||
if (useServerClone) {
|
||||
NBTTagCompound compound = ServerCloneController.Instance.getCloneData(null, ByteBufUtils.readString(in), in.readInt());
|
||||
String selected = ByteBufUtils.readString(in);
|
||||
int tab = in.readInt();
|
||||
NBTTagCompound compound;
|
||||
if (tab == -1) {
|
||||
String folder = ByteBufUtils.readString(in);
|
||||
compound = ServerCloneController.Instance.getCloneData(null, selected, folder);
|
||||
} else {
|
||||
compound = ServerCloneController.Instance.getCloneData(null, selected, tab);
|
||||
}
|
||||
job.setJobCompound(in.readInt(), compound);
|
||||
} else {
|
||||
job.setJobCompound(in.readInt(), ByteBufUtils.readNBT(in));
|
||||
|
||||
@@ -115,7 +115,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11")
|
||||
@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11.1")
|
||||
public class CustomNpcs {
|
||||
|
||||
@SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy")
|
||||
|
||||
@@ -199,11 +199,19 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
|
||||
@Override
|
||||
protected void rollChainCooldown(ChainedAbility chain) {
|
||||
int baseCooldown = minCooldown;
|
||||
if (maxCooldown > minCooldown) {
|
||||
baseCooldown = minCooldown + random.nextInt(maxCooldown - minCooldown + 1);
|
||||
if (!chain.isIgnoreCooldown()) {
|
||||
int baseCooldown = minCooldown;
|
||||
if (maxCooldown > minCooldown) {
|
||||
baseCooldown = minCooldown + random.nextInt(maxCooldown - minCooldown + 1);
|
||||
}
|
||||
cooldownEndTime = npc.worldObj.getTotalWorldTime() + baseCooldown + chain.getCooldownTicks();
|
||||
}
|
||||
|
||||
// Additionally set per-ability cooldown if enabled
|
||||
if (chain.isPerAbilityCooldown() && chain.getCooldownTicks() > 0) {
|
||||
long endTime = npc.worldObj.getTotalWorldTime() + chain.getCooldownTicks();
|
||||
setPerAbilityCooldown(chain.getName(), endTime, chain.getCooldownTicks());
|
||||
}
|
||||
cooldownEndTime = npc.worldObj.getTotalWorldTime() + baseCooldown + chain.getCooldownTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -471,12 +479,17 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Per-ability cooldown filter: ability may have its own independent cooldown
|
||||
// Per-ability cooldown filter: ability/chain may have its own independent cooldown
|
||||
if (!action.isChain()) {
|
||||
Ability ab = (Ability) action;
|
||||
if (ab.isPerAbilityCooldown() && isOnPerAbilityCooldown(ab.getName())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ChainedAbility ch = (ChainedAbility) action;
|
||||
if (ch.isPerAbilityCooldown() && isOnPerAbilityCooldown(ch.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -975,6 +975,11 @@ public class EventHooks {
|
||||
return postAbilityEvent(ability, event);
|
||||
}
|
||||
|
||||
public static boolean onAbilityDefend(Ability ability, AbilityEvent.DefendEvent event) {
|
||||
if (ability == null || event == null) return false;
|
||||
return postAbilityEvent(ability, event);
|
||||
}
|
||||
|
||||
public static boolean onAbilityToggle(Ability ability, EntityLivingBase entity, int oldState, int newState) {
|
||||
if (ability == null || entity == null) return false;
|
||||
return postAbilityEvent(ability, new AbilityEvent.ToggleEvent(entity, ability, oldState, newState));
|
||||
|
||||
@@ -8,22 +8,26 @@ import net.minecraft.nbt.NBTTagDouble;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.client.controllers.ClientCloneController;
|
||||
import noppes.npcs.client.gui.util.GuiCustomScroll;
|
||||
import noppes.npcs.client.gui.util.GuiMenuSideButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcTextField;
|
||||
import noppes.npcs.client.gui.util.ICustomScrollListener;
|
||||
import noppes.npcs.client.gui.util.IGuiData;
|
||||
import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
import noppes.npcs.controllers.data.CloneFolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiData {
|
||||
public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiData, ICustomScrollListener {
|
||||
|
||||
private GuiCustomScroll scroll;
|
||||
private GuiCustomScroll navScroll;
|
||||
private List<String> list;
|
||||
private List<String> navList = new ArrayList<String>();
|
||||
|
||||
private static String search = "";
|
||||
public int activeTab = 1;
|
||||
public String activeFolder = null;
|
||||
public boolean isServer = false;
|
||||
|
||||
public GuiNpcMobSpawnerSelector() {
|
||||
@@ -50,40 +54,62 @@ public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiDat
|
||||
addButton(new GuiNpcButton(0, guiLeft + 171, guiTop + 80, 80, 20, "gui.done"));
|
||||
addButton(new GuiNpcButton(1, guiLeft + 171, guiTop + 103, 80, 20, "gui.cancel"));
|
||||
|
||||
addSideButton(new GuiMenuSideButton(21, guiLeft - 90, this.guiTop + 2, 90, 22, "1"));
|
||||
addSideButton(new GuiMenuSideButton(22, guiLeft - 90, this.guiTop + 23, 90, 22, "2"));
|
||||
addSideButton(new GuiMenuSideButton(23, guiLeft - 90, this.guiTop + 44, 90, 22, "3"));
|
||||
addSideButton(new GuiMenuSideButton(24, guiLeft - 90, this.guiTop + 65, 90, 22, "4"));
|
||||
addSideButton(new GuiMenuSideButton(25, guiLeft - 90, this.guiTop + 86, 90, 22, "5"));
|
||||
addSideButton(new GuiMenuSideButton(26, guiLeft - 45, this.guiTop + 107, 45, 22, "6"));
|
||||
addSideButton(new GuiMenuSideButton(27, guiLeft - 90, this.guiTop + 107, 45, 22, "7"));
|
||||
addSideButton(new GuiMenuSideButton(28, guiLeft - 45, this.guiTop + 128, 45, 22, "8"));
|
||||
addSideButton(new GuiMenuSideButton(29, guiLeft - 90, this.guiTop + 128, 45, 22, "9"));
|
||||
addSideButton(new GuiMenuSideButton(30, guiLeft - 45, this.guiTop + 149, 45, 22, "10"));
|
||||
addSideButton(new GuiMenuSideButton(31, guiLeft - 90, this.guiTop + 149, 45, 22, "11"));
|
||||
addSideButton(new GuiMenuSideButton(32, guiLeft - 45, this.guiTop + 170, 45, 22, "12"));
|
||||
addSideButton(new GuiMenuSideButton(33, guiLeft - 90, this.guiTop + 170, 45, 22, "13"));
|
||||
addSideButton(new GuiMenuSideButton(34, guiLeft - 45, this.guiTop + 191, 45, 22, "14"));
|
||||
addSideButton(new GuiMenuSideButton(35, guiLeft - 90, this.guiTop + 191, 45, 22, "15"));
|
||||
// Navigation scroll (tabs + folders) replacing hardcoded side buttons
|
||||
buildNavList();
|
||||
if (navScroll == null) {
|
||||
navScroll = new GuiCustomScroll(this, 1);
|
||||
} else {
|
||||
navScroll.clear();
|
||||
}
|
||||
navScroll.setSize(90, 210);
|
||||
navScroll.guiLeft = guiLeft - 94;
|
||||
navScroll.guiTop = guiTop + 2;
|
||||
navScroll.setList(navList);
|
||||
navScroll.setSelected(getNavSelection());
|
||||
addScroll(navScroll);
|
||||
|
||||
getSideButton(20 + activeTab).active = true;
|
||||
showClones();
|
||||
}
|
||||
|
||||
private void buildNavList() {
|
||||
navList.clear();
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
navList.add("Tab " + i);
|
||||
}
|
||||
if (ClientCloneController.Instance != null) {
|
||||
for (CloneFolder folder : ClientCloneController.Instance.getFolderList()) {
|
||||
navList.add(folder.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getNavSelection() {
|
||||
if (activeFolder != null) return activeFolder;
|
||||
if (activeTab >= 1 && activeTab <= 15) return "Tab " + activeTab;
|
||||
return "Tab 1";
|
||||
}
|
||||
|
||||
public String getSelected() {
|
||||
return scroll.getSelected();
|
||||
}
|
||||
|
||||
private void showClones() {
|
||||
|
||||
if (isServer) {
|
||||
PacketClient.sendClient(new CloneListPacket(activeTab));
|
||||
if (activeFolder != null) {
|
||||
PacketClient.sendClient(new CloneListPacket(activeFolder));
|
||||
} else {
|
||||
PacketClient.sendClient(new CloneListPacket(activeTab));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
this.list = new ArrayList<String>(ClientCloneController.Instance.getClones(activeTab));
|
||||
if (activeFolder != null && ClientCloneController.Instance != null) {
|
||||
this.list = new ArrayList<String>(ClientCloneController.Instance.getClones(activeFolder));
|
||||
} else if (ClientCloneController.Instance != null) {
|
||||
this.list = new ArrayList<String>(ClientCloneController.Instance.getClones(activeTab));
|
||||
} else {
|
||||
this.list = new ArrayList<String>();
|
||||
}
|
||||
scroll.setList(getSearchList());
|
||||
}
|
||||
|
||||
@@ -97,6 +123,7 @@ public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiDat
|
||||
}
|
||||
|
||||
private List<String> getSearchList() {
|
||||
if (list == null) list = new ArrayList<String>();
|
||||
if (search.isEmpty())
|
||||
return new ArrayList<String>(list);
|
||||
List<String> list = new ArrayList<String>();
|
||||
@@ -112,8 +139,15 @@ public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiDat
|
||||
if (sel == null)
|
||||
return null;
|
||||
|
||||
NBTTagCompound compound = ClientCloneController.Instance.getCloneData(player, sel, activeTab);
|
||||
compound.setString("ClonedName", sel);
|
||||
NBTTagCompound compound;
|
||||
if (activeFolder != null && ClientCloneController.Instance != null) {
|
||||
compound = ClientCloneController.Instance.getCloneData(player, sel, activeFolder);
|
||||
} else {
|
||||
compound = ClientCloneController.Instance.getCloneData(player, sel, activeTab);
|
||||
}
|
||||
if (compound != null) {
|
||||
compound.setString("ClonedName", sel);
|
||||
}
|
||||
return compound;
|
||||
}
|
||||
|
||||
@@ -126,9 +160,28 @@ public class GuiNpcMobSpawnerSelector extends SubGuiInterface implements IGuiDat
|
||||
scroll.clear();
|
||||
close();
|
||||
}
|
||||
if (id > 20) {
|
||||
activeTab = id - 20;
|
||||
initGui();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) {
|
||||
if (guiCustomScroll.id == 1) {
|
||||
String selected = navScroll.getSelected();
|
||||
if (selected == null) return;
|
||||
|
||||
if (selected.startsWith("Tab ")) {
|
||||
try {
|
||||
int tabNum = Integer.parseInt(selected.substring(4));
|
||||
if (tabNum >= 1 && tabNum <= 15) {
|
||||
activeTab = tabNum;
|
||||
activeFolder = null;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
} else {
|
||||
activeFolder = selected;
|
||||
activeTab = -1;
|
||||
}
|
||||
showClones();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -184,8 +184,13 @@ public class GuiNpcSpawner extends GuiNPCInterface2 implements ITextfieldListene
|
||||
GuiNpcMobSpawnerSelector selector = (GuiNpcMobSpawnerSelector) gui;
|
||||
if (selector.isServer) {
|
||||
String selected = selector.getSelected();
|
||||
if (selected != null)
|
||||
PacketClient.sendClient(new JobSpawnerAddPacket(selector.isServer, selected, selector.activeTab, slot));
|
||||
if (selected != null) {
|
||||
if (selector.activeFolder != null) {
|
||||
PacketClient.sendClient(new JobSpawnerAddPacket(selector.isServer, selected, selector.activeFolder, slot));
|
||||
} else {
|
||||
PacketClient.sendClient(new JobSpawnerAddPacket(selector.isServer, selected, selector.activeTab, slot));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NBTTagCompound compound = selector.getCompound();
|
||||
if (compound != null) {
|
||||
|
||||
@@ -125,6 +125,7 @@ public enum EnumScriptType {
|
||||
ABILITY_INTERRUPT("abilityInterrupt"),
|
||||
ABILITY_COMPLETE("abilityComplete"),
|
||||
ABILITY_HIT("abilityHit"),
|
||||
ABILITY_DEFEND("abilityDefend"),
|
||||
ABILITY_TICK("abilityTick"),
|
||||
ABILITY_TOGGLE("abilityToggle"),
|
||||
ABILITY_TOGGLE_TICK("abilityToggleTick"),
|
||||
|
||||
@@ -124,6 +124,7 @@ public class ScriptHookController implements IScriptHookHandler {
|
||||
hook(NPC, ABILITY_START, IAbilityEvent.StartEvent.class);
|
||||
hook(NPC, ABILITY_EXECUTE, IAbilityEvent.ExecuteEvent.class);
|
||||
hook(NPC, ABILITY_HIT, IAbilityEvent.HitEvent.class);
|
||||
hook(NPC, ABILITY_DEFEND, IAbilityEvent.DefendEvent.class);
|
||||
hook(NPC, ABILITY_TICK, IAbilityEvent.TickEvent.class);
|
||||
hook(NPC, ABILITY_INTERRUPT, IAbilityEvent.InterruptEvent.class);
|
||||
hook(NPC, ABILITY_COMPLETE, IAbilityEvent.CompleteEvent.class);
|
||||
@@ -259,6 +260,7 @@ public class ScriptHookController implements IScriptHookHandler {
|
||||
hook(PLAYER, ABILITY_START, IAbilityEvent.StartEvent.class);
|
||||
hook(PLAYER, ABILITY_EXECUTE, IAbilityEvent.ExecuteEvent.class);
|
||||
hook(PLAYER, ABILITY_HIT, IAbilityEvent.HitEvent.class);
|
||||
hook(PLAYER, ABILITY_DEFEND, IAbilityEvent.DefendEvent.class);
|
||||
hook(PLAYER, ABILITY_TICK, IAbilityEvent.TickEvent.class);
|
||||
hook(PLAYER, ABILITY_INTERRUPT, IAbilityEvent.InterruptEvent.class);
|
||||
hook(PLAYER, ABILITY_COMPLETE, IAbilityEvent.CompleteEvent.class);
|
||||
@@ -357,6 +359,7 @@ public class ScriptHookController implements IScriptHookHandler {
|
||||
hook(ABILITY, ABILITY_START, IAbilityEvent.StartEvent.class);
|
||||
hook(ABILITY, ABILITY_EXECUTE, IAbilityEvent.ExecuteEvent.class);
|
||||
hook(ABILITY, ABILITY_HIT, IAbilityEvent.HitEvent.class);
|
||||
hook(ABILITY, ABILITY_DEFEND, IAbilityEvent.DefendEvent.class);
|
||||
hook(ABILITY, ABILITY_TICK, IAbilityEvent.TickEvent.class);
|
||||
hook(ABILITY, ABILITY_INTERRUPT, IAbilityEvent.InterruptEvent.class);
|
||||
hook(ABILITY, ABILITY_COMPLETE, IAbilityEvent.CompleteEvent.class);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -214,6 +214,46 @@ public class AbilityEvent extends CustomNPCsEvent implements IAbilityEvent {
|
||||
}
|
||||
}
|
||||
|
||||
@Cancelable
|
||||
public static class DefendEvent extends AbilityEvent implements IAbilityEvent.DefendEvent {
|
||||
private final IEntityLivingBase attacker;
|
||||
private final IEntityLivingBase lastAttacker;
|
||||
private float damage;
|
||||
|
||||
public DefendEvent(EntityLivingBase entity, Ability ability, EntityLivingBase attacker,
|
||||
EntityLivingBase lastAttacker, float damage) {
|
||||
super(entity, ability, null);
|
||||
this.attacker = attacker != null ? (IEntityLivingBase) NpcAPI.Instance().getIEntity(attacker) : null;
|
||||
this.lastAttacker = lastAttacker != null ? (IEntityLivingBase) NpcAPI.Instance().getIEntity(lastAttacker) : null;
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IEntityLivingBase getAttacker() {
|
||||
return attacker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IEntityLivingBase getLastAttacker() {
|
||||
return lastAttacker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDamage() {
|
||||
return damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDamage(float damage) {
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHookName() {
|
||||
return EnumScriptType.ABILITY_DEFEND.function;
|
||||
}
|
||||
}
|
||||
|
||||
@Cancelable
|
||||
public static class ToggleEvent extends AbilityEvent implements IAbilityEvent.ToggleEvent {
|
||||
private final int oldState;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user