add asc_behavior_remove() default tip

Thu, 03 Jul 2025 23:01:52 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 03 Jul 2025 23:01:52 +0200
changeset 176
cb2f60f48337
parent 175
e5544920377e

add asc_behavior_remove()

src/ascension/behavior.h file | annotate | diff | comparison | revisions
src/behavior.c file | annotate | diff | comparison | revisions
src/context.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/ascension/behavior.h	Wed Jul 02 23:55:50 2025 +0200
+++ b/src/ascension/behavior.h	Thu Jul 03 23:01:52 2025 +0200
@@ -44,6 +44,7 @@
     uint64_t last_execution;
     uint64_t interval;
     bool enabled;
+    bool killed;
     // TODO: more useful attributes
 };
 
@@ -69,14 +70,39 @@
 AscBehavior *asc_behavior_add_(AscSceneNode *node, struct asc_behavior_create_args args);
 #define asc_behavior_add(node,...) asc_behavior_add_(node, (struct asc_behavior_create_args){__VA_ARGS__})
 
-// TODO: asc_behavior_remove()
+/**
+ * Removes and destroys a behavior.
+ *
+ * If you just want to temporarily pause a behavior, use asc_behavior_disable().
+ *
+ * @param behavior the behavior to remove
+ * @see asc_behavior_disable()
+ */
+void asc_behavior_remove(AscBehavior *behavior);
 
+/**
+ * Used internally. Do not invoke manually.
+ *
+ * @param behavior the behavior
+ */
 void asc_behavior_trigger(AscBehavior *behavior);
 
+/**
+ * Enables a behavior
+ *
+ * Has no effect on killed behaviors.
+ *
+ * @param behavior the behavior to enable
+ */
 static inline void asc_behavior_enable(AscBehavior *behavior) {
-    behavior->enabled = true;
+    behavior->enabled = !behavior->killed;
 }
 
+/**
+ * Disables a behavior.
+ *
+ * @param behavior the behavior to disable
+ */
 static inline void asc_behavior_disable(AscBehavior *behavior) {
     behavior->enabled = false;
 }
--- a/src/behavior.c	Wed Jul 02 23:55:50 2025 +0200
+++ b/src/behavior.c	Thu Jul 03 23:01:52 2025 +0200
@@ -55,6 +55,7 @@
     AscBehavior *behavior = cxListEmplace(node->behaviors);
     assert(behavior != NULL);
     behavior->enabled = true;
+    behavior->killed = false;
     behavior->node = node;
     behavior->func = args.func;
     behavior->destroy_func = args.destroy_func;
@@ -64,8 +65,14 @@
     return behavior;
 }
 
+void asc_behavior_remove(AscBehavior *behavior) {
+    // TODO: implement some sort of ID for behaviors which can also be used for logging
+    behavior->killed = true;
+    behavior->enabled = false;
+}
+
 void asc_behavior_trigger(AscBehavior *behavior) {
-    if (!behavior->enabled) return;
+    assert(behavior->enabled);
     if (behavior->last_execution + behavior->interval > asc_context.total_nanos) return;
 
     behavior->func(behavior);
--- a/src/context.c	Wed Jul 02 23:55:50 2025 +0200
+++ b/src/context.c	Thu Jul 03 23:01:52 2025 +0200
@@ -198,8 +198,6 @@
     }
 
     // compute frame time
-    // TODO: think about whether frame rate is actually a per-window thing
-    //       the answer is hopefully NO, because that would mean behaviors are also a per-window thing
     uint64_t frame_nanos, ns;
     do {
         ns = asc_nanos();
--- a/src/scene.c	Wed Jul 02 23:55:50 2025 +0200
+++ b/src/scene.c	Thu Jul 03 23:01:52 2025 +0200
@@ -58,6 +58,7 @@
         cxListFree(scene->internal.render_groups[i]);
         scene->internal.render_groups[i] = NULL;
     }
+    // TODO: add names to scenes
     asc_dprintf("Destroyed scene %"PRIxPTR, (uintptr_t) scene);
     asc_scene_node_free(scene->root);
 }
@@ -68,9 +69,14 @@
         offsetof(AscSceneNode, next)
     );
     cx_foreach(AscSceneNode*, node, iter) {
-        CxIterator behavior_iter = cxListIterator(node->behaviors);
+        CxIterator behavior_iter = cxListMutIterator(node->behaviors);
         cx_foreach(AscBehavior*, behavior, behavior_iter) {
-            asc_behavior_trigger(behavior);
+            if (behavior->enabled) {
+                asc_behavior_trigger(behavior);
+            }
+            if (behavior->killed) {
+                cxIteratorFlagRemoval(behavior_iter);
+            }
         }
     }
 }
--- a/src/text.c	Wed Jul 02 23:55:50 2025 +0200
+++ b/src/text.c	Thu Jul 03 23:01:52 2025 +0200
@@ -125,6 +125,7 @@
     return node;
 }
 
+// TODO: maybe we should prefer a clean signature and molest the caller with the cast
 void asc_text_printf(
         AscSceneNode *node,
         const char *format,
--- a/test/snake/snake.c	Wed Jul 02 23:55:50 2025 +0200
+++ b/test/snake/snake.c	Thu Jul 03 23:01:52 2025 +0200
@@ -105,6 +105,16 @@
     asc_ui_add_node(node);
 }
 
+static void update_score_counter(AscBehavior *behavior) {
+    AscSceneNode *node = behavior->node;
+    static unsigned test_score = 0;
+    test_score += 100;
+    asc_text_printf(node, "Score: %u", test_score);
+    if (test_score > 9000) {
+        asc_behavior_remove(behavior);
+    }
+}
+
 static void create_score_counter(void) {
     asc_font(ASC_FONT_BOLD, 16);
     asc_ink_rgb(0, 255, 0);
@@ -113,6 +123,8 @@
         .x = 10, .y = 10,
         .text = "Score: 0"
     );
+    // TODO: remove this test behavior
+    asc_behavior_add(node, .func = update_score_counter, .interval = asc_milliseconds(250));
     asc_ui_add_node(node);
 }
 

mercurial