Skip to content

Related: ๅŠจไฝœไธŽๆŠ€่ƒฝๆกฅ.md ยท APIๆฆ‚่งˆ.md ยท ่„šๆœฌAPI.md ยท ../05-ๅ‚่€ƒ/ๆœฏ่ฏญ่กจ.md

๐Ÿ”€ Condition System & Expression Engine (ConditionSystem / ExpressionEngine) โ€‹

This page covers two complementary evaluation tools:

  1. ConditionSystem โ€” a boolean judgment framework (condition chains, DSL programs, operators, composite conditions).
  2. ExpressionEngine โ€” an exp4j-based numeric expression engine (arithmetic, functions, random numbers).

Actions decide "what to do", conditions decide "whether to do it", and expressions compute the concrete numbers for actions. The three are usually used together โ€” see ๅŠจไฝœไธŽๆŠ€่ƒฝๆกฅ.md.


1. ConditionSystem โ€‹

1.1 The Condition interface and ConditionContext โ€‹

kotlin
interface Condition {
    val id: String
    fun evaluate(context: ConditionContext): Boolean
}

The evaluation context ConditionContext:

MemberDescription
variablesVariable bag
traceId (nullable)Trace id
debugWhether to enable debug tracing

Read/write methods:

kotlin
context.setVar("hp", 18.0)
val hp: Double?  = context.getVar("hp")
val name: String = context.getVarString("targetName")
context.traceBuilder()

1.2 Implementing and registering a condition โ€‹

kotlin
class HasEnoughHpCondition : Condition {
    override val id = "has_enough_hp"

    override fun evaluate(context: ConditionContext): Boolean {
        val hp = context.getVar<Double>("hp") ?: return false
        return hp >= 10.0
    }
}

// Register / look up / get all
ConditionRegistry.register(HasEnoughHpCondition())
val cond = ConditionRegistry.get("has_enough_hp")
val all  = ConditionRegistry.all()

1.3 ConditionPipeline โ€” condition chain โ€‹

kotlin
val pipeline = ConditionPipeline()
pipeline.addCondition(HasEnoughHpCondition())
// Or load a DSL program
pipeline.loadProgram(myConditionDslProgram)

val ctx = ConditionContext(variables = mutableMapOf("hp" to 18.0))
val ok: Boolean = pipeline.evaluate(ctx)

1.4 DSL: ConditionNode / ConditionDslProgram โ€‹

Write conditions as nodes, aggregated by logic as AND / OR. The compile / validate / optimize / execute four-stage flow is consistent with the action DSL.

kotlin
val program = ConditionDslProgram(
    id = "can_cast",
    nodes = listOf(
        ConditionNode(id = "n1", type = "var_eq", params = mapOf("key" to "class", "value" to "mage")),
        ConditionNode(id = "n2", type = "exists", params = mapOf("key" to "mana")),
    ),
    logic = "AND"     // "AND" | "OR", defaults to "AND"
)

program.compile()
program.validate()
program.optimize()
val result: Boolean = program.execute(ctx)

ConditionNode(id, type, params): type is one of the built-in operators in the table below, and params holds the arguments for that operator.

1.5 Built-in node operators โ€‹

OperatorMeaning
eqEqual
neqNot equal
existsVariable exists
notnullNot null
containsContains
var_eqVariable equals a value

1.6 CompositeCondition โ€” composite condition โ€‹

Combine multiple conditions into one using a logical operator:

kotlin
val composite = CompositeCondition(
    conditions = listOf(condA, condB, condC),
    operator = LogicOperator.AND      // AND / OR / NOT
)
val ok = composite.evaluate(ctx)

LogicOperator values: AND / OR / NOT.


2. ExpressionEngine โ€” the expression engine โ€‹

ExpressionEngine is based on exp4j, computing a string expression together with a variable table into a single Double.

2.1 evaluate โ€‹

kotlin
val vars: Map<String, Double> = mapOf("level" to 12.0, "base" to 5.0)
val damage: Double = ExpressionEngine.evaluate("base + level * 1.5", vars)
// โ†’ 5 + 12 * 1.5 = 23.0

Signature: evaluate(expression: String, variables: Map<String, Double>): Double

2.2 Random functions โ€‹

The engine has 4 built-in random functions that can be written directly into expressions:

FunctionMeaning
randomDouble(min, max)Uniform random decimal in the [min, max) interval
randomInt(min, max)Random integer in the interval
randomGaussian(mean, stdDev)Normally distributed random number (mean mean, standard deviation stdDev)
randomExponential(lambda)Exponentially distributed random number (rate lambda)
kotlin
// Base damage 10 ยฑ fluctuation, plus a bit of Gaussian jitter
val dmg = ExpressionEngine.evaluate(
    "10 + randomDouble(0, 3) + randomGaussian(0, 0.5)",
    emptyMap()
)
val crit = ExpressionEngine.evaluate("randomInt(1, 100)", emptyMap())   // 1~100 dice roll

2.3 exp4j syntax and built-in functions โ€‹

Supported operations and functions (built into exp4j):

  • Arithmetic: + - * / % (modulo), ^ or pow (power)
  • Comparison and logic: comparison operators, logical operators
  • Math functions: sin cos tan log log10 ln sqrt abs ceil floor exp, etc.
kotlin
ExpressionEngine.evaluate("sqrt(level) * 2 + abs(offset)", mapOf("level" to 16.0, "offset" to -3.0))
// โ†’ 4 * 2 + 3 = 11.0
ExpressionEngine.evaluate("ceil(hp / 2)", mapOf("hp" to 7.0))   // โ†’ 4.0
ExpressionEngine.evaluate("2 ^ 10", emptyMap())                  // โ†’ 1024.0

3. Using conditions + expressions + actions together โ€‹

A typical flow: first compute numbers with expressions and put them into the variable bag, then use conditions to decide whether to execute, and finally run the action.

kotlin
val ctx = ActionContext(player = player, variables = mutableMapOf())

// 1. Compute damage with an expression
val dmg = ExpressionEngine.evaluate("base + level * 1.5", mapOf("base" to 5.0, "level" to 12.0))
ctx.setVar("damage", dmg)
ctx.setVar("hp", player.health)

// 2. Condition check
val condCtx = ConditionContext(variables = ctx.variables)
if (HasEnoughHpCondition().evaluate(condCtx)) {
    // 3. Execute the action
    ActionRegistry.get("heal")?.execute(ctx)
}

๐Ÿ“– Continue reading โ€‹