package noppes.npcs.scripted; import noppes.npcs.api.handler.IActionManager; import noppes.npcs.api.handler.data.IAction; import noppes.npcs.api.handler.data.IActionChain; import java.util.Deque; import java.util.LinkedList; import java.util.Queue; import java.util.function.Consumer; import java.util.function.Supplier; /** * Full-featured ActionManager: * - delayed start, max-duration, tick-interval * - chaining (getNext, getPrevious, scheduleAfter/Before) * - per-action data * - conditional actions * - cancellation & clearing */ public class ScriptedActionManager implements IActionManager { private boolean isWorking = false; private final Deque actionQueue = new LinkedList<>(); @Override public IAction create(String name, int maxDuration, int startAfterTicks, Consumer task) { return new Action(name, maxDuration, startAfterTicks, task); } @Override public void start() { isWorking = true; } @Override public void stop() { isWorking = false; } @Override public void scheduleAction(IAction action) { actionQueue.addLast(action); } @Override public void scheduleAction(String name, int maxDuration, int startAfterTicks, Consumer task) { scheduleAction(create(name, maxDuration, startAfterTicks, task)); } @Override public void scheduleActionAt(int index, IAction action) { if (index < 0 || index > actionQueue.size()) { actionQueue.addLast(action); } else { ((LinkedList) actionQueue).add(index, action); } } @Override public int getIndex(IAction action) { if (action == null) return -1; return ((LinkedList) actionQueue).indexOf(action); } @Override public IAction getCurrentAction() { return actionQueue.peekFirst(); } @Override public Queue getActionQueue() { return actionQueue; } @Override public void clear() { actionQueue.clear(); } @Override public boolean cancelAction(String name) { for (IAction act : actionQueue) { if (act.getName().equals(name)) { ((LinkedList) actionQueue).remove(act); return true; } } return false; } @Override public void scheduleConditionalAction(String name, int checkIntervalTicks, Supplier predicate, Consumer task) { scheduleConditionalAction(name, checkIntervalTicks, predicate, task, -1); } @Override public void scheduleConditionalAction(String name, int checkIntervalTicks, Supplier predicate, Consumer task, int maxChecks) { scheduleAction(new ConditionalAction(name, checkIntervalTicks, predicate, task, maxChecks)); } @Override public void scheduleConditionalAction(String name, int checkIntervalTicks, Supplier predicate, Supplier terminateWhen, Consumer task, int maxChecks) { scheduleAction(new ConditionalAction(name, checkIntervalTicks, predicate, terminateWhen, task, maxChecks)); } /** * Call once per tick from your main loop. * * @param ticksExisted the global tick count, used for modulo checks. */ public void tick(int ticksExisted) { if (!isWorking) return; IAction current = getCurrentAction(); if (current instanceof ActionBase) { ActionBase cab = (ActionBase) current; cab.tick(ticksExisted); if (cab.isDone() || cab.getDuration() >= cab.getMaxDuration()) { actionQueue.pollFirst(); } } } @Override public IActionChain chain() { return new ActionChain(this); } /** helper to build a back‐to‐back chain of one‐shot actions */ public static class ActionChain implements IActionChain { private final IActionManager mgr; private int offset = 0, index = 0; private ActionChain(IActionManager mgr) { this.mgr = mgr; } /** schedule the next task ‘delay’ ticks after the previous one */ @Override public IActionChain after(int delay, Consumer task) { offset += delay; IAction a = mgr.create("chain#" + (index++), 1, offset, task); mgr.scheduleAction(a); return this; } } // ──── base class for all custom actions ────────────────────────────────── private abstract class ActionBase implements IAction { protected final String name; protected int startAfterTicks; protected int count = 0; protected int duration = 0; protected final int maxDuration; protected int updateEveryXTick = 5; protected final Consumer task; private boolean done = false; protected ActionBase(String name, int maxDuration, int startAfterTicks, Consumer task) { this.name = name; this.maxDuration = maxDuration; this.startAfterTicks = startAfterTicks; this.task = task; } /** * Called once per global tick; respects delay, interval, duration & done‐flag. */ public void tick(int ticksExisted) { if (done) return; if (startAfterTicks > 0) { startAfterTicks--; return; } // only run on our update tick if (ticksExisted % updateEveryXTick == 0) { task.accept(this); count++; } duration++; } @Override public int getCount() { return count; } @Override public int getDuration() { return duration; } @Override public String getName() { return name; } @Override public int getMaxDuration() { return maxDuration; } @Override public void markDone() { done = true; } @Override public boolean isDone() { return done; } @Override public Object getData(String key) { return dataStore.get(key); } @Override public void addData(String key, Object v) { dataStore.put(key, v); } @Override public int getUpdateEveryXTick() { return updateEveryXTick; } @Override public void setUpdateEveryXTick(int x) { updateEveryXTick = x; } @Override public int getStartAfterTicks() { return startAfterTicks; } @Override public IAction create(String n, int md, int sat, Consumer t) { return ScriptedActionManager.this.create(n, md, sat, t); } @Override public IAction getNext() { int idx = getIndex(this); return (idx >= 0 && idx + 1 < actionQueue.size()) ? ((LinkedList) actionQueue).get(idx + 1) : null; } @Override public IAction getPrevious() { int idx = getIndex(this); return (idx > 0) ? ((LinkedList) actionQueue).get(idx - 1) : null; } @Override public void scheduleAfter(IAction after) { int idx = getIndex(this); if (idx >= 0) scheduleActionAt(idx + 1, after); } @Override public void scheduleBefore(IAction before) { int idx = getIndex(this); if (idx >= 0) scheduleActionAt(Math.max(0, idx), before); } private final java.util.Map dataStore = new java.util.HashMap<>(); } private class Action extends ActionBase { public Action(String name, int maxDuration, int startAfterTicks, Consumer task) { super(name, maxDuration, startAfterTicks, task); } @Override public int getCheckCount() { return 0; } @Override public int getMaxChecks() { return 0; } } private class ConditionalAction extends ActionBase { private final Supplier predicate; private Supplier terminate; private final int maxChecks; private int checkCount = 0; public ConditionalAction(String name, int checkIntervalTicks, Supplier predicate, Consumer task, int maxChecks) { super(name, Integer.MAX_VALUE, 0, task); this.predicate = predicate; this.updateEveryXTick = checkIntervalTicks; this.maxChecks = maxChecks; } public ConditionalAction(String name, int checkIntervalTicks, Supplier predicate, Supplier terminate, Consumer task, int maxChecks) { this(name, checkIntervalTicks, predicate, task, maxChecks); this.terminate = terminate; } @Override public void tick(int ticksExisted) { if (isDone()) return; if (ticksExisted % updateEveryXTick == 0) { checkCount++; if ((maxChecks >= 0 && checkCount > maxChecks) || terminate != null && terminate.get()) { markDone(); return; } if (predicate.get()) task.accept(this); } } @Override public int getCheckCount() { return checkCount; } @Override public int getMaxChecks() { return maxChecks; } } }