-
Notifications
You must be signed in to change notification settings - Fork 0
ActionJS
YukkuriC edited this page Sep 4, 2026
·
2 revisions
basis for all executables
- customizable
operate&operateInParens, with high degree-of-freedom return value handling:- an
OperationResultlike vanilla: directly accepted - a
Mishap: throws it no matter it's returned or thrown - a list of things among:
-
Iota: append to current stack - number, bool, entity,
Vec3, null, undefined...: auto converted to iota -
SideEffect: append to side-effect list -
SpellContinuationand/orEvalSound: overrides default
-
- an
- additional helper methods for creating
operatevarieties:-
setOperateMutableStack: converts current stack to a mutable iota list, and modifies it freely -
setOperateArgsSplit: auto-extracts first X iotas on stack top asArgsJSobject like most of vanilla spells
-
- assigning
mediaCostto auto-attach media-cost side effect on operate, withpreCheckMediamethods to help check & mishap before logics
let { ActionJS } = HexJS
let {
/* vanilla Hex API omitted */
} = HexJS.APIFlat
let action = new ActionJS()let { ActionJS } = HexJS
let { /* vanilla Hex API omitted */ } = HexJS.APIFlat
let action = new ActionJS()
// outside parens
action.setOperate((env, image, cont) => {
// returns `OperationResult` to override everything
if (you_are_determined) return thePreparedOperationResult
// shortcut 1: a list of stuff
if (list_of_kaleido_scope) return [
// iotas append to stack top
NullIota(), DoubleIota(114514)
// certain raw values wrapped automatically
[GarbageIota(), EntityIota(env.castingEntity), [/* yes it can be nested */]] /* ListIota */,
env.castingEntity /* EntityIota */, env.castingEntity.position() /* Vec3Iota */,
1919810 /* DoubleIota */, null, undefined, // NullIota
cont /* ContinuationIota (Jump) */, // ...
// side effects added to list
OperatorSideEffect.Particles(ParticleSpray.burst(env.castingEntity.position(), 1, 1))
// casting sound and continuation for overrides (once at most each)
HexEvalSounds.THOTH.get(),
SpellContinuation.Done.INSTANCE // no auto-singleton sadly :(
// invalid values: mishap for unsupported
]
// shortcut 2: Mishap = throws it anyway
let mishap = MishapEvalTooMuch()
if (want_it_return) return mishap
else if (want it throw) throw mishap
// invalid values: mishap for unsupported
if (its_invalid) return HexJS
// no return or return undefined: end with no further side effects or stack additions
})
// inside parens
action.setOperateInParens((env, image, cont, myIota) => {
// almost similar as above, only list differences here
// returns `OperationResult` to override almost everything
if (you_are_determined) return thePreparedOperationResult
// returns `ParenthesizedOperationResult` to truly override everything
if (you_are_super_determined) return thePreparedParenthesizedOperationResult
// differences inside list
if (list_of_kaleido_scope) return [
// returning iotas here adds them to parenthesized list instead, same as below
NullIota(), DoubleIota(114514)
// returning prepared ParenthesizedIota is fine too
ParenthesizedIota(NullIota(), false),
]
})action.setOperateMutableStack((stack, env, image, cont) => {
// `stack` is the mutable whole stack from the image; doing modifications on it pass to the new image later
})
action.setOperateArgsSplit(argsCount, (args, env, image, cont) => {
// splits an ArgsJS object of given argument counts
})action.mediaCost = 114514 // set to > 0 results in an additional `OperatorSideEffect.ConsumeMedia` during operates outside parens
action.preCheckMedia(env) // mishaps if CastEnv media < action's media cost
action.preCheckMedia(env, num) // mishaps if CastEnv media < given num
// it's also possible to pass handlers in constructors
let action2 = new ActionJS(funOperate)
let action3 = new ActionJS(funOperate, funOperateInParens)because there's no difference between the effort preparing the full SpellAction logic, and that preparing the very SpellAction$Result.
As an exchange, here's an example of creating an alternative pattern action who does everything a SpellAction does.
ActionRegistryJS.of(
HexPattern.fromAnglesUnchecked('assassass', 'WEST'),
'hexjsneo:spell_action_alternative',
).setOperateMutableStack((stack, env) => {
let takeANum = new Args(stack, 1).double(0)
// precheck and throw first
spellAlternative.preCheckMedia(env, takeANum)
// spellAlternative.mediaCost = takeANum // changing media cost for auto-add ConsumeMedia side effect, or...
let ret = [OperatorSideEffect.ConsumeMedia(takeANum)] // side effects inside return array will be accepted
ret.push(
OperatorSideEffect.AttemptSpell(
// due to some deep dark causes, JS objects can be converted to KT interfaces seamlessly
{
cast: envArg => {
env.castingEntity.tell(`env outside = ${env}; env inside = ${envArg}; cost = ${takeANum}`)
},
},
true,
true,
),
)
// add particle spray(s)
ret.push(OperatorSideEffect.Particles(ParticleSpray.burst(env.castingEntity.position(), 1, 1)))
// optional: override casting sound (1 at max, more will mishap)
ret.push(HexEvalSounds.THOTH.get())
// return all weak-typed override array altogether
return ret
})yes, everything about a SpellAction is the procedure of:
- take arguments
- pre-check media cost (and mishap)
- sequential side effects of:
- consume media
- do spell
- do particles
- might change casting sound
and everything is just covered already, with even more abilities and freedom