Files
CustomNPC-Plus/src/main/java/noppes/npcs/scripted/ScriptedActionManager.java
T

635 lines
19 KiB
Java
Raw Normal View History

package noppes.npcs.scripted;
2025-03-03 20:05:19 -05:00
import noppes.npcs.api.handler.IActionManager;
import noppes.npcs.api.handler.data.IAction;
2025-04-27 23:10:55 -04:00
import noppes.npcs.api.handler.data.IActionChain;
2025-04-28 07:41:29 +03:00
import noppes.npcs.api.handler.data.actions.IConditionalAction;
2025-04-28 07:41:29 +03:00
import java.util.*;
import java.util.function.Consumer;
2025-04-26 20:52:08 -04:00
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 {
2025-04-26 20:52:08 -04:00
private boolean isWorking = false;
private final Deque<IAction> actionQueue = new LinkedList<>();
2025-04-28 07:41:29 +03:00
private final List<IConditionalAction> conditionalActions = new LinkedList<>();
2025-04-28 10:09:50 +03:00
@Override
public IAction create(String name) {
return new Action(name);
}
@Override
public IAction create(Consumer<IAction> t) {
return new Action(t);
}
@Override
2025-04-26 20:52:08 -04:00
public IAction create(String name, int maxDuration, int startAfterTicks, Consumer<IAction> task) {
return new Action(name, maxDuration, startAfterTicks, task);
}
2025-04-28 07:26:39 +03:00
@Override
public IAction create(String name, int delay, Consumer<IAction> t) {
return new Action(name, delay, t);
}
@Override
public IAction create(int delay, Consumer<IAction> t) {
return new Action(delay, t);
}
@Override
public IAction create(String name, Consumer<IAction> t) {
return new Action(name, t);
}
2025-04-28 08:17:57 +03:00
@Override
public IConditionalAction create(Supplier<Boolean> predicate, Consumer<IAction> task) {
return new ConditionalAction(predicate, task);
}
@Override
public IConditionalAction create(String name, Supplier<Boolean> predicate, Consumer<IAction> task) {
return new ConditionalAction(name, predicate, task);
}
@Override
public IConditionalAction create(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
return new ConditionalAction(predicate, terminateWhen, task);
2025-04-28 08:17:57 +03:00
}
@Override
public IConditionalAction create(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
return new ConditionalAction(name, predicate, terminateWhen, task);
}
@Override
public IConditionalAction create(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTermination, Consumer<IAction> task) {
return new ConditionalAction(predicate, terminateWhen, onTermination, task);
}
@Override
public IConditionalAction create(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTermination, Consumer<IAction> task) {
return new ConditionalAction(name, predicate, terminateWhen, onTermination, task);
2025-04-28 08:17:57 +03:00
}
@Override
public void start() {
isWorking = true;
}
@Override
public void stop() {
isWorking = false;
}
@Override
2025-04-28 06:47:34 +03:00
public IAction scheduleAction(IAction action) {
2025-04-26 20:52:08 -04:00
actionQueue.addLast(action);
2025-04-28 06:47:34 +03:00
return action;
}
@Override
2025-04-28 06:47:34 +03:00
public IAction scheduleAction(String name, int maxDuration, int startAfterTicks, Consumer<IAction> task) {
return scheduleAction(create(name, maxDuration, startAfterTicks, task));
}
@Override
2025-04-28 06:47:34 +03:00
public IAction scheduleActionAt(int index, IAction action) {
2025-04-26 20:52:08 -04:00
if (index < 0 || index > actionQueue.size()) {
actionQueue.addLast(action);
} else {
((LinkedList<IAction>) actionQueue).add(index, action);
}
2025-04-28 06:47:34 +03:00
return action;
}
@Override
2025-03-13 16:52:08 -04:00
public int getIndex(IAction action) {
2025-04-26 20:52:08 -04:00
if (action == null) return -1;
return ((LinkedList<IAction>) actionQueue).indexOf(action);
}
@Override
public IAction getCurrentAction() {
2025-04-26 20:52:08 -04:00
return actionQueue.peekFirst();
}
@Override
public Queue<IAction> getActionQueue() {
return actionQueue;
}
2025-04-26 20:52:08 -04:00
@Override
2025-03-13 16:52:08 -04:00
public void clear() {
actionQueue.clear();
2025-04-28 08:38:30 +03:00
conditionalActions.clear();
}
2025-03-13 16:52:08 -04:00
2025-04-26 20:52:08 -04:00
@Override
public boolean cancelAction(String name) {
2025-04-28 08:36:26 +03:00
Iterator<IAction> acts = actionQueue.iterator();
while (acts.hasNext()) {
IAction act = acts.next();
2025-04-26 20:52:08 -04:00
if (act.getName().equals(name)) {
2025-04-28 08:36:26 +03:00
acts.remove();
return true;
}
}
Iterator<IConditionalAction> cons = conditionalActions.iterator();
while (cons.hasNext()) {
IConditionalAction con = cons.next();
if (con.getName().equals(name)) {
cons.remove();
2025-04-26 20:52:08 -04:00
return true;
}
}
2025-04-26 20:52:08 -04:00
return false;
}
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Conditionals
2025-04-26 20:52:08 -04:00
@Override
public IConditionalAction scheduleAction(String name, Supplier<Boolean> predicate, Consumer<IAction> task) {
return scheduleAction(new ConditionalAction(name, predicate, task));
2025-04-26 20:52:08 -04:00
}
@Override
public IConditionalAction scheduleAction(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
return scheduleAction(new ConditionalAction(name, predicate, terminateWhen, task));
2025-04-28 07:41:29 +03:00
}
@Override
public IConditionalAction scheduleAction(IConditionalAction action) {
2025-04-28 07:41:29 +03:00
conditionalActions.add(action);
return action;
}
@Override
public List<IConditionalAction> getConditionalActions() {
return conditionalActions;
}
2025-04-26 20:52:08 -04:00
/**
* 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);
2025-04-28 07:41:29 +03:00
if (cab.isDone())
2025-04-26 20:52:08 -04:00
actionQueue.pollFirst();
}
2025-04-28 07:41:29 +03:00
Iterator<IConditionalAction> it = conditionalActions.iterator();
while (it.hasNext()) {
ConditionalAction con = (ConditionalAction) it.next();
con.tick(ticksExisted);
if (con.isDone())
it.remove();
}
2025-04-26 20:52:08 -04:00
}
2025-04-27 23:10:55 -04:00
@Override
public IActionChain chain() {
return new ActionChain();
2025-04-27 23:10:55 -04:00
}
/** helper to build a back‐to‐back chain of one‐shot actions */
public class ActionChain implements IActionChain {
2025-04-27 23:10:55 -04:00
private int offset = 0, index = 0;
/** schedule the next task ‘delay’ ticks after the previous one */
@Override
public IActionChain after(int delay, Consumer<IAction> task) {
offset += delay;
2025-04-27 23:23:21 -04:00
Consumer<IAction> wrapper = act -> {
task.accept(act);
act.markDone();
};
IAction a = create("chain#" + (index++), Integer.MAX_VALUE, offset, wrapper);
2025-04-27 23:23:21 -04:00
a.setUpdateEveryXTick(1);
scheduleAction(a);
2025-04-27 23:10:55 -04:00
return this;
}
}
2025-04-26 20:52:08 -04:00
// ──── base class for all custom actions ──────────────────────────────────
private abstract class ActionBase implements IAction {
protected final String name;
protected int startAfterTicks;
protected int count = 0;
2025-04-28 07:53:21 +03:00
protected int duration;
2025-04-28 07:19:59 +03:00
protected int maxDuration = -1;
2025-04-26 20:52:08 -04:00
protected int updateEveryXTick = 5;
2025-04-28 10:09:50 +03:00
protected Consumer<IAction> task;
2025-04-26 20:52:08 -04:00
private boolean done = false;
2025-04-28 10:09:50 +03:00
protected ActionBase(String name) {
this.name = name;
}
protected ActionBase(Consumer<IAction> task) {
this(task.toString());
this.task = task;
}
2025-04-26 20:52:08 -04:00
protected ActionBase(String name, int maxDuration, int startAfterTicks, Consumer<IAction> task) {
this.name = name;
this.maxDuration = maxDuration;
this.startAfterTicks = startAfterTicks;
2025-04-26 20:52:08 -04:00
this.task = task;
}
2025-04-28 07:29:42 +03:00
protected ActionBase(String name, int startAfterTicks, Consumer<IAction> task) {
2025-04-28 07:19:59 +03:00
this.name = name;
this.startAfterTicks = startAfterTicks;
this.task = task;
}
2025-04-28 07:29:42 +03:00
protected ActionBase(String name, Consumer<IAction> task) {
2025-04-28 07:19:59 +03:00
this.name = name;
this.task = task;
}
2025-04-28 07:29:42 +03:00
protected ActionBase(int startAfterTicks, Consumer<IAction> task) {
2025-04-28 07:19:59 +03:00
this(task);
this.startAfterTicks = startAfterTicks;
}
2025-04-28 10:09:50 +03:00
@Override
public IAction setTask(Consumer<IAction> task) {
2025-04-28 07:19:59 +03:00
this.task = task;
2025-04-28 10:09:50 +03:00
return this;
2025-04-28 07:19:59 +03:00
}
2025-04-28 10:09:50 +03:00
2025-04-26 20:52:08 -04:00
/**
* Called once per global tick; respects delay, interval, duration & done‐flag.
*/
public void tick(int ticksExisted) {
if (done) return;
2025-04-26 20:52:08 -04:00
if (startAfterTicks > 0) {
startAfterTicks--;
return;
}
2025-04-28 07:41:29 +03:00
2025-04-28 07:53:21 +03:00
if (maxDuration != -1 && duration >= maxDuration)
2025-04-28 07:41:29 +03:00
markDone();
2025-04-26 20:52:08 -04:00
// 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;
}
2025-04-28 10:09:50 +03:00
@Override
public IAction setMaxDuration(int x) {
maxDuration = x;
return this;
}
@Override
2025-03-13 16:52:08 -04:00
public void markDone() {
2025-04-26 20:52:08 -04:00
done = true;
}
@Override
public boolean isDone() {
2025-04-26 20:52:08 -04:00
return done;
}
2025-04-28 07:19:59 +03:00
private final java.util.Map<String, Object> dataStore = new java.util.HashMap<>();
@Override
public Object getData(String key) {
2025-04-26 20:52:08 -04:00
return dataStore.get(key);
}
@Override
public IAction addData(String key, Object v) {
2025-04-26 20:52:08 -04:00
dataStore.put(key, v);
return this;
}
2025-04-28 10:09:50 +03:00
@Override
public IAction removeData(String key) {
dataStore.remove(key);
return this;
}
@Override
public int getUpdateEveryXTick() {
return updateEveryXTick;
}
@Override
public IAction setUpdateEveryXTick(int x) {
2025-04-26 20:52:08 -04:00
updateEveryXTick = x;
return this;
}
@Override
public int getStartAfterTicks() {
return startAfterTicks;
}
2025-04-28 10:09:50 +03:00
@Override
public IAction pauseFor(int ticks) {
startAfterTicks = ticks;
return this;
}
2025-03-13 16:52:08 -04:00
@Override
public IAction getNext() {
2025-04-26 20:52:08 -04:00
int idx = getIndex(this);
return (idx >= 0 && idx + 1 < actionQueue.size())
? ((LinkedList<IAction>) actionQueue).get(idx + 1)
: null;
}
@Override
2025-03-13 16:52:08 -04:00
public IAction getPrevious() {
2025-04-26 20:52:08 -04:00
int idx = getIndex(this);
return (idx > 0)
? ((LinkedList<IAction>) actionQueue).get(idx - 1)
: null;
2025-03-13 16:52:08 -04:00
}
2025-04-28 07:19:59 +03:00
/////////////////////////////////////////////////
/////////////////////////////////////////////////
//After chains
2025-03-13 16:52:08 -04:00
@Override
public IAction after(IAction after) {
2025-04-26 20:52:08 -04:00
int idx = getIndex(this);
if (idx >= 0) scheduleActionAt(idx + 1, after);
return after;
2025-03-13 16:52:08 -04:00
}
2025-04-28 07:19:59 +03:00
@Override
public IAction after(String name, int maxDuration, int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return after(create(name, maxDuration, delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction after(String name, int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return after(create(name, delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction after(int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return after(create(delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction after(String name, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return after(create(name, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction after(Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return after(create(t));
2025-04-28 07:19:59 +03:00
}
2025-04-28 07:19:59 +03:00
/////////////////////////////////////////////////
/////////////////////////////////////////////////
//Before chains
2025-03-13 16:52:08 -04:00
@Override
public IAction before(IAction before) {
2025-04-26 20:52:08 -04:00
int idx = getIndex(this);
if (idx >= 0) scheduleActionAt(Math.max(0, idx), before);
return before;
}
2025-04-26 20:52:08 -04:00
2025-04-28 07:19:59 +03:00
@Override
public IAction before(String name, int maxDuration, int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return before(create(name, maxDuration, delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction before(String name, int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return before(create(name, delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction before(int delay, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return before(create(delay, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction before(String name, Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return before(create(name, t));
2025-04-28 07:19:59 +03:00
}
@Override
public IAction before(Consumer<IAction> t) {
2025-04-28 07:26:39 +03:00
return before(create(t));
2025-04-28 07:19:59 +03:00
}
}
2025-04-26 20:52:08 -04:00
private class Action extends ActionBase {
2025-04-28 10:09:50 +03:00
public Action(String name) {
super(name);
}
public Action(Consumer<IAction> task) {
super(task);
}
2025-04-26 20:52:08 -04:00
public Action(String name, int maxDuration, int startAfterTicks, Consumer<IAction> task) {
super(name, maxDuration, startAfterTicks, task);
}
2025-04-28 07:19:59 +03:00
public Action(String name, int startAfterTicks, Consumer<IAction> task) {
super(name, startAfterTicks, task);
}
public Action(String name, Consumer<IAction> task) {
super(name, task);
}
public Action(int startAfterTicks, Consumer<IAction> task) {
super(startAfterTicks, task);
}
2025-04-26 20:52:08 -04:00
@Override public int getCheckCount() { return 0; }
2025-04-26 20:52:08 -04:00
@Override public int getMaxChecks() { return 0; }
}
2025-04-28 07:41:29 +03:00
private class ConditionalAction extends ActionBase implements IConditionalAction {
private Supplier<Boolean> predicate;
private Supplier<Boolean> terminateWhen;
private Consumer<IAction> onTermination;
2025-04-28 08:01:15 +03:00
private int maxChecks = -1;
2025-04-26 20:52:08 -04:00
private int checkCount = 0;
private boolean taskExecuted;
2025-04-28 08:01:15 +03:00
public ConditionalAction(Supplier<Boolean> predicate, Consumer<IAction> task) {
super(task);
this.predicate = predicate;
}
public ConditionalAction(String name, Supplier<Boolean> predicate, Consumer<IAction> task) {
super(name, task);
this.predicate = predicate;
}
public ConditionalAction(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
this(predicate, task);
this.terminateWhen = terminateWhen;
}
public ConditionalAction(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
this(name, predicate, task);
this.terminateWhen = terminateWhen;
}
public ConditionalAction(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTerminate, Consumer<IAction> task) {
2025-04-28 08:01:15 +03:00
this(predicate, task);
this.terminateWhen = terminateWhen;
this.onTermination = onTerminate;
2025-04-28 08:01:15 +03:00
}
public ConditionalAction(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTerminate, Consumer<IAction> task) {
2025-04-28 08:01:15 +03:00
this(name, predicate, task);
this.terminateWhen = terminateWhen;
this.onTermination = onTerminate;
2025-04-28 08:01:15 +03:00
}
2025-04-26 20:52:08 -04:00
@Override
public void tick(int ticksExisted) {
if (isDone()) return;
if (ticksExisted % updateEveryXTick == 0) {
checkCount++;
boolean terminated = terminateWhen != null && terminateWhen.get();
if (terminated && onTermination != null)
onTermination.accept(this);
if ((maxChecks >= 0 && checkCount > maxChecks) || terminated) {
2025-04-26 20:52:08 -04:00
markDone();
return;
}
if (predicate.get()) {
2025-04-26 20:52:08 -04:00
task.accept(this);
taskExecuted = true;
}
2025-04-26 20:52:08 -04:00
}
2025-04-28 08:26:00 +03:00
duration++;
2025-04-26 20:52:08 -04:00
}
@Override
public IConditionalAction setMaxChecks(int maxChecks) {
this.maxChecks = maxChecks;
return this;
}
@Override
public IConditionalAction setPredicate(Supplier<Boolean> predicate) {
this.predicate = predicate;
return this;
}
@Override
public IConditionalAction setTerminationPredicate(Supplier<Boolean> terminateWhen) {
this.terminateWhen = terminateWhen;
return this;
}
@Override
public IConditionalAction setTerminationTask(Consumer<IAction> onTermination) {
this.onTermination = onTermination;
return this;
}
@Override
public boolean wasTaskExecuted() {
return taskExecuted;
}
2025-04-26 20:52:08 -04:00
@Override public int getCheckCount() { return checkCount; }
@Override public int getMaxChecks() { return maxChecks; }
2025-04-28 08:17:57 +03:00
/////////////////////////////////////////////////
/////////////////////////////////////////////////
2025-04-28 08:19:08 +03:00
// Chaining methods
2025-04-28 08:17:57 +03:00
@Override
public IConditionalAction after(IConditionalAction after) {
return scheduleAction(after);
2025-04-28 08:17:57 +03:00
}
@Override
public IConditionalAction after(Supplier<Boolean> predicate, Consumer<IAction> task) {
return after(create(predicate, task));
}
@Override
public IConditionalAction after(String name, Supplier<Boolean> predicate, Consumer<IAction> task) {
return after(create(name, predicate, task));
}
@Override
public IConditionalAction after(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
return after(create(predicate, terminateWhen, task));
}
@Override
public IConditionalAction after(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> task) {
return after(create(name, predicate, terminateWhen, task));
2025-04-28 08:17:57 +03:00
}
@Override
public IConditionalAction after(Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTermination, Consumer<IAction> task) {
return after(create(predicate, terminateWhen, onTermination, task));
2025-04-28 08:17:57 +03:00
}
@Override
public IConditionalAction after(String name, Supplier<Boolean> predicate, Supplier<Boolean> terminateWhen, Consumer<IAction> onTermination, Consumer<IAction> task) {
return after(create(name, predicate, terminateWhen, onTermination, task));
}
2025-04-28 08:17:57 +03:00
2025-04-26 20:52:08 -04:00
}
}