Task Chains
Chain and CancellableChain are just two utility interfaces to provide a method to organize
your code.
You can of course create your own interfaces or use a different thing entirely, this is just a thing I use myself.
- Java
 - Kotlin
 
Chain.java
public interface Chain {
    void invokeOn(Listener listener);
}
CancellableChain.java
public interface CancellableChain extends Chain {
    void cancelOn(Listener listener);
}
Chain.kt
fun interface Chain {
    fun invokeOn(listener: Listener)
}
CancellableChain.kt
interface CancellableChain : Chain {
    fun cancelOn(listener: Listener)
}
Usage example:
(this is (almost) my actual deposit chain)
- Java
 - Kotlin
 
DepositChain.java
// IMPORTANT: notice how I have an 'isCancelled' variable that's set to
// true when the chain is not running
// Having it set to true when not running, and checking if it's already true when trying
// to cancel it again stops the cancel chain from being invoked every time if you use the same
// button to cancel something else as well
public class DepositChain implements CancellableChain {
    private final TeleOpBotComponents bot;
    public RegularDepositChain(TeleOpBotComponents bot) {
        this.bot = bot;
    }
    private boolean isCancelled = true;
    @Override
    public void invokeOn(Listener button) {
        button.and(() -> bot.lift.clippedHeight > 90)
            .onRise(() -> {
                isCancelled = false;
                bot.arm.setToForwardsPos();
                bot.wrist.setToForwardsPos();
            })
            .onFall(() -> {
                if (isCancelled) {
                    return;
                }
                int newTarget = (bot.lift.getTargetHeight() - 300).coerceAtLeast(LIFT_LOW);
                bot.lift.setTargetHeight(newTarget);
                bot.claw.openForDeposit();
                // I've static imported Timer.after() here
                after(350).milliseconds(() -> {
                    finish();
                    bot.lift.goToZero();
                    isCancelled = true;
                });
            });
    }
    @Override
    public void cancelOn(Listener button) {
      button.and(() -> !isCancelled)
          .onRise(this::finish);
    }
    private void finish() {
        bot.claw.close();
        bot.arm.goToRest();
        bot.wrist.setToRestingPos();
        isCancelled = true;
    }
}
ActualUsageExample.java
@TeleOp
public class MyTeleOp extends BlackOp {
    @CreateOnGo
    private TeleOpBotComponents bot;
    @EvalOnGo(method = "getReforgedGamepad2")
    private ReforgedGamepad codriver;
    @EvalOnGo(method = "makeDepositChain")
    private DepositChain depositChain;
    //...
    @Override
    public void go() {
        depositChain.invokeOn(codriver.a);
        depositChain.cancelOn(codriver.x);
        Scheduler.launchOnStart(this);
    }
    private CancellableChain makeDepositChain() {
        return new DepositChain(bot);
    }
}
DepositChain.kt
// IMPORTANT: notice how I have an 'isCancelled' variable that's set to
// true when the chain is not running
// Having it set to true when not running, and checking if it's already true when trying
// to cancel it again stops the cancel chain from being invoked every time if you use the same
// button to cancel something else as well
class DepositChain(val bot: TeleOpBotComponents) : CancellableChain {
    private var isCancelled = true
    override fun invokeOn(button: Listener) = (button + { bot.lift.clippedHeight > 90 })
        .onRise {
            isCancelled = false
            bot.arm.setToForwardsPos()
            bot.wrist.setToForwardsPos()
        }
        .onFall {
            if (isCancelled) {
                return@onFall
            }
            bot.lift.targetHeight = (bot.lift.targetHeight - 300).coerceAtLeast(LIFT_LOW)
            bot.claw.openForDeposit()
            after(350).milliseconds {
                finish()
                bot.lift.goToZero()
                isCancelled = true
            }
        }
        .hook() // just using .hook() so it returns Unit so Kotlin doesn't yell at me
                // it acts as a noop here since it's already hooked
    override fun cancelOn(button: Listener) = (button + { !isCancelled })
        .onRise(::finish)
        .hook()
    private fun finish() {
        bot.claw.close()
        bot.arm.goToRest()
        bot.wrist.setToRestingPos()
        isCancelled = true
    }
}
ActualUsageExample.kt
@TeleOp
class MyTeleOp : BlackOp() {
    private val bot by createOnGo<TeleOpBotComponents>();
    private val codriver by createOnGo<ReforgedGamepad> { gamepad2 }
    private val depositChain by createOnGo< DepositChain >{ bot }
    //...
    override fun go() {
        depositChain.invokeOn(codriver.a)
        depositChain.cancelOn(codriver.x)
        Scheduler.launchOnStart(this)
    }
}