A Workflow Behaviour Tree

     ## ai/workflow_example/

     ## bTree inmplmentation

        move the dev memo
          dev/ai/
          dev/scenedialogue_detailed_explanation/
        to
          ai/workflow_btree/
          ai/fountain2model/
          ai/model2yaml/
            - [ ] audio directory with short/long
              - [ ] how can I create sync text & audio?
          ai/model2src/

            App/uiHandler
              enableBehaviorTree = false
                create anim/button/sprite/filter/particles/physics
                create action for play/pause etc
              enableBehaviorTree = true
                  actions/*/***_actopns
                  actions/*/audio_actions
                  ```
                  UI:dispatchEvent{name = xxx}
                    [show elara]
                    [sfx rustling]
                    [vo vo_elara_01]
                  ```

                  so you can create same action names in Kwik editor

            main.lua
              scale = 0.5 or 0.25

         test

          ```
           elseif env.mode == "behaviorTree" then
              local cabinTest = require("tests.cabinSceneTest")
          ```

     ## Linear Dialogue implementaion
          ↓ change it "A linear dialogue"
          ai/workflow_example/#solar2d-implementation-for-the-last-spark

          modify to use display.newImageRect with @2x, @4x pngs
          add create_layered_psd.py to genarate the pngs
->
|    [Show cabin interior]
|    [Show lumin seed]
|    [Show Elara]
|    [Play rustling sound]
|    [Play wind sound]
|    [Play Elara voice line 1]
|    [Change to forest scene]
|    [Play footsteps sound]
|    [Play crow caw sound]
|    [Show forest narration]
|    [Play wolf growl]
|    [Show wolf]
|    [Play tension music]
|    [Change Elara to scared]
|    [Play Elara voice line 2]
|    ?
|    |    ->
|    |    |    (player choice fight)
|    |    |    [Focus on wolf]
|    |    |    [Present choice: "Fight it!"]
|    |    |    [Go to fight scene]
|    |    ->
|    |    |    (player choice calm)
|    |    |    [Focus on Elara]
|    |    |    [Present choice: "Try to calm it."]
|    |    |    [Go to calm scene]
|    |    ->
|    |    |    (player choice retreat)
|    |    |    [Focus on cabin]
|    |    |    [Present choice: "Run back to the cabin!"]
|    |    |    [Go to retreat scene]
  • File structure

    .
    ├── actions
    │   ├── audio_actions.lua
    │   ├── cabin_actions.lua
    │   ├── elara_actions.lua
    │   ├── focus_actions.lua
    │   ├── lumin_seed_actions.lua
    │   ├── scene_actions.lua
    │   ├── ui_actions.lua
    │   └── wolf_actions.lua
    ├── btree.lua
    ├── cabin_scene.tree
    ├── conditions
    │   └── player_choice.lua
    ├── controllers
    │   ├── action_controller.lua
    │   └── condition_controller.lua
    ├── forest_scene.tree
    ├── main.lua
    ├── models
    │   ├── cabin.lua
    │   ├── elara.lua
    │   ├── lumin_seed.lua
    │   └── wolf.lua
    ├── tree.txt
    ├── utils
    │   └── common_helpers.lua
    └── views
        ├── cabin_display.lua
        ├── display_manager.lua
        ├── elara_display.lua
        ├── lumin_seed_display.lua
        └── wolf_display.lua
  • controllers/condition_controller.lua

    function M.loadAllConditions()
        -- Load consolidated player choice module
        local playerChoiceModule = require("conditions.player_choice")
    
        -- Register individual player choice conditions
        conditions.player_choice_fight = {
            initialize = playerChoiceModule.initialize,
            evaluate = function() return playerChoiceModule.evaluate("fight") end
        }
        conditions.player_choice_calm = {
            initialize = playerChoiceModule.initialize,
            evaluate = function() return playerChoiceModule.evaluate("calm") end
        }
        conditions.player_choice_retreat = {
            initialize = playerChoiceModule.initialize,
            evaluate = function() return playerChoiceModule.evaluate("retreat") end
        }
  • controllers/action_controller.lua

    function M.execute(actionName)
      -- Parse action name format: "module.action" or legacy single action names
      local moduleName, specificAction = string.match(actionName, "([^.]+)%.(.+)")
    
      if moduleName and specificAction then
          -- New format: module.action (e.g., "elara.show")
          if actions[moduleName] then
              print("Action Controller: Executing " .. moduleName .. "." .. specificAction)
              return actions[moduleName].execute(specificAction)
          else
              print("Action Controller: Unknown module - " .. moduleName)
              return false
          end
      else
          -- Legacy format: single action name (for backward compatibility)
          -- Map old action names to new consolidated modules
          local actionMap = {
              -- Elara actions
              show_elara = { module = "elara", action = "show" },
              change_elara_to_scared = { module = "elara", action = "change_to_scared" },
              play_elara_voice_line_1 = { module = "elara", action = "play_voice_line_1" },
              play_elara_voice_line_2 = { module = "elara", action = "play_voice_line_2" },
    
              -- Wolf actions
              show_wolf = { module = "wolf", action = "show" },
              play_wolf_growl = { module = "wolf", action = "play_growl" },
    
              -- Cabin actions
              show_cabin_interior = { module = "cabin", action = "show_interior" },
              focus_on_cabin = { module = "cabin", action = "focus_on" },
    
              -- Lumin Seed actions
              show_lumin_seed = { module = "lumin_seed", action = "show" },
    
              -- Audio actions
              play_rustling_sound = { module = "audio", action = "rustling_sound" },
              play_wind_sound = { module = "audio", action = "wind_sound" },
              play_footsteps_sound = { module = "audio", action = "footsteps_sound" },
              play_crow_caw_sound = { module = "audio", action = "crow_caw_sound" },
              play_tension_music = { module = "audio", action = "tension_music" },
    
              -- Scene actions
              change_to_forest_scene = { module = "scene", action = "change_to_forest" },
              go_to_fight_scene = { module = "scene", action = "go_to_fight" },
              go_to_calm_scene = { module = "scene", action = "go_to_calm" },
              go_to_retreat_scene = { module = "scene", action = "go_to_retreat" },
    
              -- UI actions
              show_forest_narration = { module = "ui", action = "show_forest_narration" },
              present_choice_fight = { module = "ui", action = "present_choice_fight" },
              present_choice_calm = { module = "ui", action = "present_choice_calm" },
              present_choice_retreat = { module = "ui", action = "present_choice_retreat" },
    
              -- Focus actions
              focus_on_wolf = { module = "focus", action = "focus_on_wolf" },
              focus_on_elara = { module = "focus", action = "focus_on_elara" },
              focus_on_cabin = { module = "focus", action = "focus_on_cabin" }
          }
  • actions/wolf_actions.lua

    -- Action constants
    M.ACTIONS = {
        SHOW = "show",
        PLAY_GROWL = "play_growl"
    }
    
    function M.execute(action)
        if not action then
            print("Error: No action specified for Wolf")
            return bt.FAILED
        end
    
        if action == M.ACTIONS.SHOW then
            return M.showWolf()
        elseif action == M.ACTIONS.PLAY_GROWL then
            return M.playGrowl()
        else
            print("Error: Unknown Wolf action - " .. tostring(action))
            return bt.FAILED
        end
    end
    
    function M.showWolf()
        if not sceneObjects.wolf then
            print("Error: Wolf object not found")
            return bt.FAILED
        end
    
        -- Make wolf visible
        sceneObjects.wolf.isVisible = true
        sceneObjects.wolf.alpha = 0
        transition.fadeIn(sceneObjects.wolf, { time = 1000 })
    
        print("Showing wolf")
        return bt.SUCCESS
    end
    
    function M.playGrowl()
        -- Play wolf growl sound effect
        audio.play(audio.loadSound("sounds/wolf_growl.mp3"), { channel = 3 })
    
        print("Playing wolf growl")
        return bt.SUCCESS
    end