Skip to content

Commit

Permalink
Merge pull request #449 from xpdota/beta
Browse files Browse the repository at this point in the history
Combined beta changes
  • Loading branch information
xpdota authored Oct 8, 2023
2 parents 0b8b76a + 78bc652 commit 3b3d136
Show file tree
Hide file tree
Showing 75 changed files with 1,660 additions and 774 deletions.
3 changes: 3 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
import gg.xp.xivsupport.events.triggers.easytriggers.actions.GroovyAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.SoundAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitBuffDurationAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitCastDurationAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.ConditionalActionEditor;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.GroovyActionEditor;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.SoundActionEditor;
Expand Down Expand Up @@ -427,12 +429,15 @@ private Component generic(Object object, Object trigger) {
new ConditionDescription<>(ZoneIdFilter.class, Object.class, "Restrict the Zone ID in which this trigger may run", () -> new ZoneIdFilter(inject(XivState.class)), this::generic)
));

// XXX - DO NOT CHANGE NAMES OF THESE CLASSES OR PACKAGE PATH - FQCN IS PART OF DESERIALIZATION!!!
private final List<ActionDescription<?, ?>> actions = new ArrayList<>(List.of(
new ActionDescription<>(CalloutAction.class, Event.class, "Basic TTS/Text Callout", CalloutAction::new, (callout, trigger) -> new CalloutActionPanel(callout)),
new ActionDescription<>(DurationBasedCalloutAction.class, HasDuration.class, "Duration-Based TTS/Text Callout", DurationBasedCalloutAction::new, (callout, trigger) -> new CalloutActionPanel(callout)),
new ActionDescription<>(AutoMarkTargetAction.class, HasTargetEntity.class, "Mark The Target", () -> new AutoMarkTargetAction(inject(GlobalUiRegistry.class)), this::generic),
new ActionDescription<>(ClearAllMarksAction.class, Event.class, "Clear All Marks", () -> new ClearAllMarksAction(inject(GlobalUiRegistry.class)), this::generic),
new ActionDescription<>(WaitAction.class, BaseEvent.class, "Wait a fixed time", WaitAction::new, this::generic),
new ActionDescription<>(WaitBuffDurationAction.class, BuffApplied.class, "Wait until buff duration falls below a specified amount", () -> new WaitBuffDurationAction(inject(StatusEffectRepository.class)), this::generic),
new ActionDescription<>(WaitCastDurationAction.class, AbilityCastStart.class, "Wait until cast duration falls below a specified amount", WaitCastDurationAction::new, this::generic),
new ActionDescription<>(GroovyAction.class, Event.class, "Custom script action", () -> new GroovyAction(inject(GroovyManager.class)), (action, trigger) -> new GroovyActionEditor<>(action, trigger)),
// (ActionDescription<ConditionalAction<BaseEvent>, BaseEvent>) new ActionDescription<>(ConditionalAction.class, BaseEvent.class, "If/Else Conditional Action", ConditionalAction::new, (action, trigger) -> new ConditionalActionEditor(this, action)),
new ActionDescription<>(SoundAction.class, Event.class, "Play Sound", SoundAction::new, (action, trigger) -> new SoundActionEditor(inject(SoundFilesManager.class), inject(SoundFileTab.class), action)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gg.xp.xivsupport.events.triggers.easytriggers.actions;

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.OptBoolean;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.actlines.events.BuffApplied;
import gg.xp.xivsupport.events.actlines.events.HasStatusEffect;
import gg.xp.xivsupport.events.state.combatstate.StatusEffectRepository;
import gg.xp.xivsupport.events.triggers.easytriggers.conditions.Description;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTriggerContext;
import gg.xp.xivsupport.events.triggers.easytriggers.model.SqAction;
import gg.xp.xivsupport.events.triggers.marks.gui.AutoMarkGui;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerController;
import gg.xp.xivsupport.gui.nav.GlobalUiRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WaitBuffDurationAction implements SqAction<BuffApplied> {

private static final Logger log = LoggerFactory.getLogger(WaitBuffDurationAction.class);

@JsonIgnore
private final StatusEffectRepository buffs;

public WaitBuffDurationAction(@JacksonInject(useInput = OptBoolean.FALSE) StatusEffectRepository buffs) {
this.buffs = buffs;
}

@Description("Remaining Duration")
public long remainingDurationMs = 1000;
@Description("Stop Trigger if Buff Removed")
public boolean stopIfGone;

@Override
public String fixedLabel() {
return "Wait Until Buff Duration Below";
}

@Override
public String dynamicLabel() {
return "Wait until remaining cast duration <= %sms".formatted(remainingDurationMs);
}

@Override
public void accept(SequentialTriggerController<BuffApplied> stc, EasyTriggerContext context, BuffApplied event) {
while (true) {
BuffApplied latest = buffs.getLatest(event);
if (latest == null) {
if (stopIfGone) {
context.setStopProcessing(true);
}
return;
}
else {
// TODO: this does not handle the case of a buff being replaced with one of a shorter duration
long msToWait = latest.getEstimatedRemainingDuration().minusMillis(remainingDurationMs).toMillis();
if (msToWait > 0) {
stc.waitMs(msToWait);
}
else {
return;
}
}
}
}

@Override
public void accept(EasyTriggerContext context, BuffApplied event) {
// Handled above
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package gg.xp.xivsupport.events.triggers.easytriggers.actions;

import gg.xp.reevent.events.BaseEvent;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.triggers.easytriggers.conditions.Description;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTriggerContext;
import gg.xp.xivsupport.events.triggers.easytriggers.model.SqAction;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WaitCastDurationAction implements SqAction<AbilityCastStart> {

private static final Logger log = LoggerFactory.getLogger(WaitCastDurationAction.class);

@Description("Remaining Duration")
public long remainingDurationMs = 1000;

@Override
public String fixedLabel() {
return "Wait Until Cast Duration Below";
}

@Override
public String dynamicLabel() {
return "Wait until remaining cast duration <= %sms".formatted(remainingDurationMs);
}

@Override
public void accept(SequentialTriggerController<AbilityCastStart> stc, EasyTriggerContext context, AbilityCastStart event) {
long msToWait = event.getEstimatedRemainingDuration().minusMillis(remainingDurationMs).toMillis();
if (msToWait > 0) {
stc.waitMs(msToWait);
}
}

@Override
public void accept(EasyTriggerContext context, AbilityCastStart event) {
// Handled above
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface IdType {
/**
* @return The class of item that the ID corresponds to.
* @return The class of instance that the ID corresponds to.
*/
Class<?> value();

/**
* @return True if a mapping from the given ID to a concrete item is required. False if you
* @return True if a mapping from the given ID to a concrete instance is required. False if you
* want to accept non-matched items.
*/
boolean matchRequired() default true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import gg.xp.xivsupport.events.triggers.easytriggers.model.Condition;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTrigger;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EventDescription;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerConcurrencyMode;
import gg.xp.xivsupport.gui.GuiMain;
import gg.xp.xivsupport.gui.TitleBorderFullsizePanel;
import gg.xp.xivsupport.gui.extra.PluginTab;
import gg.xp.xivsupport.gui.library.ChooserDialog;
import gg.xp.xivsupport.gui.lists.FriendlyNameListCellRenderer;
import gg.xp.xivsupport.gui.nav.GlobalUiRegistry;
import gg.xp.xivsupport.gui.overlay.RefreshLoop;
import gg.xp.xivsupport.gui.tables.CustomColumn;
Expand Down Expand Up @@ -398,7 +400,17 @@ private class TriggerConfigPanel extends JPanel {
add(GuiUtil.labelFor("Event", eventTypeField), c);
c.gridx++;
add(eventTypeField, c);

c.gridy++;
c.gridx = 0;
JComboBox<SequentialTriggerConcurrencyMode> concModeSelector = new JComboBox<>(SequentialTriggerConcurrencyMode.values());
concModeSelector.setRenderer(new FriendlyNameListCellRenderer());
concModeSelector.setSelectedItem(trigger.getConcurrency());
concModeSelector.addItemListener(l -> {
trigger.setConcurrency((SequentialTriggerConcurrencyMode) concModeSelector.getSelectedItem());
});
add(GuiUtil.labelFor("Concurrency", concModeSelector), c);
c.gridx++;
add(concModeSelector, c);

c.gridx = 0;
c.gridy++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import gg.xp.reevent.events.Event;
import gg.xp.reevent.events.EventContext;
import gg.xp.xivsupport.events.triggers.seq.SequentialTrigger;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerConcurrencyMode;
import gg.xp.xivsupport.events.triggers.seq.SqtTemplates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -30,57 +31,62 @@ public class EasyTrigger<X> implements HasMutableConditions<X>, HasMutableAction
@JsonProperty(defaultValue = "true")
private boolean enabled = true;

@JsonProperty
private SequentialTriggerConcurrencyMode concurrency = SequentialTriggerConcurrencyMode.BLOCK_NEW;

private Class<X> eventType = (Class<X>) Event.class;
private List<Condition<? super X>> conditions = Collections.emptyList();
private List<Action<? super X>> actions = Collections.emptyList();
private String name = "Give me a name";
private int timeoutMs = 600_000;

// To account for the fact that the SQ might be recalculated while running,
// sqCurrent holds whatever is running, while sqBase holds the template
// TODO: unit test for this
private SequentialTrigger<BaseEvent> sqBase = SqtTemplates.nothing();
private SequentialTrigger<BaseEvent> sqCurrent = sqBase;
private EasyTriggerContext ctx;

public EasyTrigger() {
recalc();
// TODO: unit test for updating trigger while running
recalcFully();
}

public void handleEvent(EventContext context, Event event) {
if (!(event instanceof BaseEvent)) {
return;
}
if (sqCurrent.isActive()) {
sqCurrent.feed(context, (BaseEvent) event);
}
if (!enabled || eventType == null || !eventType.isInstance(event)) {
if (!(event instanceof BaseEvent) || !enabled || eventType == null) {
return;
}
X typedEvent = eventType.cast(event);
ctx = new EasyTriggerContext(context, this);
if (conditions.stream().allMatch(cond -> cond.test(ctx, typedEvent))) {
hits++;
sqCurrent = sqBase;
sqCurrent.feed(context, (BaseEvent) event);
}
else {
misses++;
}
sqBase.feed(context, (BaseEvent) event);
}

public void recalc() {
makeWritable();
conditions.sort(Comparator.comparing(Condition::sortOrder));
conditions.forEach(Condition::recalc);
actions.forEach(Action::recalc);
private boolean matchesStartCondition(X event) {
return conditions.stream().allMatch(cond -> cond.test(ctx, event));
}

private void recalcFully() {
sqBase = SqtTemplates.sq(timeoutMs,
eventType,
// The start condition is handled externally
se -> true,
event -> {
X typedEvent = eventType.cast(event);
if (matchesStartCondition(typedEvent)) {
hits++;
return true;
}
else {
misses++;
return false;
}
},
(e1, s) -> {
ctx.runActions((List) actions, s, (BaseEvent) e1);
});
recalc();
}

public void recalc() {
makeWritable();
conditions.sort(Comparator.comparing(Condition::sortOrder));
conditions.forEach(Condition::recalc);
actions.forEach(Action::recalc);
sqBase.setConcurrency(concurrency);
Stream.concat(conditions.stream(), actions.stream()).forEach(item -> {
if (item instanceof HasMutableEventType het) {
het.setEventType(getEventType());
Expand All @@ -95,7 +101,7 @@ public Class<X> getEventType() {

public void setEventType(Class<X> eventType) {
this.eventType = eventType;
recalc();
recalcFully();
}

@Override
Expand Down Expand Up @@ -142,6 +148,15 @@ public void removeCondition(Condition<? super X> condition) {
recalc();
}

public SequentialTriggerConcurrencyMode getConcurrency() {
return concurrency;
}

public void setConcurrency(SequentialTriggerConcurrencyMode concurrency) {
this.concurrency = concurrency;
recalc();
}

@Override
public List<Action<? super X>> getActions() {
return Collections.unmodifiableList(actions);
Expand Down Expand Up @@ -211,4 +226,70 @@ public void setEnabled(boolean enabled) {
// newTrigger.setConditions(new ArrayList<>(conditions));
// return newTrigger;
// }
// public void handleEventOld(EventContext context, Event event) {
// if (!(event instanceof BaseEvent)) {
// return;
// }
// switch (concurrency) {
// // Mirrors standard sequential trigger logic
// case BLOCK_NEW -> {
// // Block new - if currently active, feed.
// if (sqCurrent.isActive()) {
// sqCurrent.feed(context, (BaseEvent) event);
// // TODO: shouldn't there be a 'return' right here?
// return;
// }
// if (!enabled || eventType == null || !eventType.isInstance(event)) {
// return;
// }
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// if (matchesStartCondition(typedEvent)) {
// hits++;
// sqCurrent = sqBase;
// sqCurrent.feed(context, (BaseEvent) event);
// }
// else {
// misses++;
// }
// }
// case REPLACE_OLD -> {
// if (!enabled || eventType == null || !eventType.isInstance(event)) {
// return;
// }
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// if (matchesStartCondition(typedEvent)) {
// hits++;
// if (sqCurrent != null && sqCurrent.isActive()) {
// sqCurrent.stopSilently();
// }
// sqCurrent = sqBase;
// sqCurrent.feed(context, (BaseEvent) event);
// }
// else {
// if (sqCurrent.isActive()) {
// sqCurrent.feed(context, (BaseEvent) event);
// }
// misses++;
// }
// }
// case CONCURRENT -> {
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// var iter = sqMultiCurrent.iterator();
// while (iter.hasNext()) {
// SequentialTrigger<BaseEvent> next = iter.next();
// next.feed(context, (BaseEvent) event);
// if (!next.isActive()) {
// iter.remove();
// }
// }
// if (matchesStartCondition(typedEvent)) {
// hits++;
// sqMultiCurrent.add()
// }
// }
// }
// }
}
Loading

0 comments on commit 3b3d136

Please sign in to comment.