summaryrefslogtreecommitdiff
path: root/saedit/interactor.c
diff options
context:
space:
mode:
Diffstat (limited to 'saedit/interactor.c')
-rw-r--r--saedit/interactor.c439
1 files changed, 439 insertions, 0 deletions
diff --git a/saedit/interactor.c b/saedit/interactor.c
new file mode 100644
index 0000000..85e55fa
--- /dev/null
+++ b/saedit/interactor.c
@@ -0,0 +1,439 @@
+#include <glib.h>
+#include <stdlib.h>
+
+#include "interactor.h"
+#include "imageset.h"
+#include "action.h"
+#include "animation.h"
+#include "errors.h"
+
+struct _Interactor {
+ const SpriteContext *context;
+ const Action *action;
+ const Animation *animation;
+ gchar *direction;
+ gboolean rand_checked;
+ GList *element;
+ gint delay;
+
+ guint loop_tag;
+ guint tick_length;
+
+ InteractionUpdatedFunc updated_cb;
+
+ GList *repeaters;
+};
+
+static gboolean
+interactor_updated_func (Interactor *interactor) {
+ g_return_val_if_fail (interactor->updated_cb != NULL, FALSE);
+
+ interactor->updated_cb (interactor);
+ return FALSE;
+}
+
+static void
+interactor_updated (Interactor *interactor) {
+ if (interactor->updated_cb == NULL)
+ return;
+
+ g_main_context_invoke (
+ NULL,
+ (GSourceFunc) interactor_updated_func,
+ interactor
+ );
+}
+
+static AnimElement *
+interactor_get_element (const Interactor *interactor) {
+ if (interactor->element == NULL)
+ return NULL;
+ return (AnimElement *) interactor->element->data;
+}
+
+Interactor *
+interactor_new (
+ const SpriteContext *context
+) {
+ Interactor *interactor = g_new0 (Interactor, 1);
+
+ interactor->context = context;
+ interactor->repeaters = NULL;
+ return interactor;
+}
+
+gboolean
+interactor_reset_animation (
+ Interactor *interactor
+) {
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_reset_animation ((Interactor *) l->data);
+
+ if (interactor->action == NULL)
+ return FALSE;
+
+ interactor->animation = action_get_animation (
+ interactor->action,
+ interactor->direction
+ );
+
+ g_return_val_if_fail (interactor->animation != NULL, FALSE);
+
+ interactor->element = interactor->animation->elements;
+ interactor->delay = 0;
+ interactor->rand_checked = TRUE;
+ interactor_updated (interactor);
+ interactor_play (interactor, 0);
+
+ return TRUE;
+}
+
+gboolean
+interactor_set_action (
+ Interactor *interactor,
+ gint hp,
+ const gchar *name
+) {
+ Action *action;
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_set_action ((Interactor *) l->data, hp, name);
+
+ if (
+ interactor->action != NULL &&
+ action_hp_and_name_equals (interactor->action, hp, name)
+ )
+ return FALSE;
+
+ action = sprite_context_get_action (
+ interactor->context, hp, name
+ );
+
+ if (action == NULL)
+ return FALSE;
+
+ interactor->action = action;
+ return interactor_reset_animation (interactor);
+}
+
+gboolean
+interactor_set_direction (
+ Interactor *interactor,
+ const gchar *direction
+) {
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_set_direction ((Interactor *) l->data, direction);
+
+ if (g_strcmp0 (interactor->direction, direction) != 0) {
+ if (interactor->direction != NULL)
+ g_free (interactor->direction);
+
+ interactor->direction = g_strdup (direction);
+ return interactor_reset_animation (interactor);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+_animation_element_rand_check (
+ const AnimElement *element
+) {
+ if (element->rand == 100) return TRUE;
+ if (element->rand == 0) return FALSE;
+ return rand() % 100 < element->rand;
+}
+
+gboolean
+interactor_play (
+ Interactor *interactor,
+ gint time
+) {
+ gboolean updated = FALSE;
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_play ((Interactor *) l->data, time);
+
+ g_return_val_if_fail (time >= 0, FALSE);
+
+ if (interactor->action == NULL)
+ return FALSE;
+ if (interactor->animation == NULL)
+ return FALSE;
+ g_return_val_if_fail (interactor->element != NULL, FALSE);
+
+ interactor->delay += time;
+
+ while (TRUE) {
+ AnimElement *element = interactor_get_element (interactor);
+ gint e_delay = element->delay;
+
+ if ( interactor->rand_checked ||
+ _animation_element_rand_check (element)
+ ) {
+ interactor->rand_checked = TRUE;
+
+ if (interactor->delay < e_delay)
+ break;
+
+ interactor->delay -= e_delay;
+ interactor->rand_checked = FALSE;
+ updated = TRUE;
+
+ if (element->type == ELEMENT_END) {
+ interactor_reset_animation (interactor);
+ return FALSE;
+ } else
+
+ if (element->type == ELEMENT_FRAME) {
+ if (e_delay == 0)
+ break;
+ } else
+
+ if (element->type == ELEMENT_PAUSE) {
+ if (e_delay == 0)
+ break;
+ } else
+
+ if (element->type == ELEMENT_JUMP) {
+ gint delay = interactor->delay;
+
+ gboolean found = interactor_set_action (
+ interactor,
+ interactor->action->hp,
+ element->str
+ );
+
+ if (!found) {
+ /* TODO: report about this */
+ return FALSE;
+ }
+
+ return interactor_play (
+ interactor,
+ delay
+ );
+ } else
+
+ if (element->type == ELEMENT_LABEL) {
+
+ } else
+
+ if (element->type == ELEMENT_GOTO) {
+ GList *nelem =
+ interactor->animation->elements;
+
+ while (nelem != NULL) {
+ AnimElement *current =
+ (AnimElement *) nelem->data;
+ if (current->type == ELEMENT_LABEL) {
+ if (g_strcmp0 (
+ current->str,
+ element->str) == 0
+ )
+ break;
+ }
+
+ nelem = g_list_next (nelem);
+ }
+
+ if (nelem != NULL) {
+ interactor->element = nelem;
+ continue;
+ } else {
+ post_error ("Playback", "Specified goto label not found");
+ return FALSE;
+ }
+ }
+ }
+
+ interactor->element = g_list_next (interactor->element);
+ if (interactor->element == NULL)
+ interactor->element = interactor->animation->elements;
+ }
+
+ if (updated)
+ interactor_updated (interactor);
+
+ return TRUE;
+}
+
+const GdkPixbuf *
+interactor_get_sprite (const Interactor *interactor) {
+ if (interactor->element == NULL)
+ return NULL;
+ return interactor_get_element (interactor)->sprite;
+}
+
+void
+interactor_get_offset (
+ const Interactor *interactor,
+ gint *offsetX,
+ gint *offsetY
+) {
+ AnimElement *element = interactor_get_element (interactor);
+ if (element == NULL)
+ return;
+
+ *offsetX = element->offsetX;
+ *offsetY = element->offsetY;
+}
+
+gboolean
+interactor_loop_tick (Interactor *interactor) {
+ gboolean result = interactor_play (
+ interactor,
+ interactor->tick_length
+ );
+
+ if (result == FALSE) {
+ interactor->loop_tag = 0;
+ interactor_updated (interactor);
+ }
+
+ return result;
+}
+
+void
+interactor_loop_start (
+ Interactor *interactor,
+ const guint interval,
+ const guint tick_length
+) {
+ if (interactor->loop_tag != 0)
+ return;
+
+ interactor->tick_length = tick_length;
+ interactor->loop_tag = g_timeout_add (
+ interval,
+ (GSourceFunc) interactor_loop_tick,
+ interactor
+ );
+
+ interactor_updated (interactor);
+}
+
+gboolean
+interactor_loop_stop (Interactor *interactor) {
+ if (interactor->loop_tag == 0)
+ return FALSE;
+
+ g_source_remove (interactor->loop_tag);
+ interactor->loop_tag = 0;
+
+ interactor_updated (interactor);
+
+ return TRUE;
+}
+
+gboolean
+interactor_loop_running (
+ const Interactor *interactor
+) {
+ if (interactor == NULL)
+ return FALSE;
+ return interactor->loop_tag != 0;
+}
+
+void
+interactor_free (Interactor *interactor) {
+ interactor_loop_stop (interactor);
+ g_list_free (interactor->repeaters);
+ g_free (interactor);
+}
+
+void
+interactor_free_with_repeaters (Interactor *interactor) {
+ interactor_loop_stop (interactor);
+
+ g_list_free_full (
+ interactor->repeaters,
+ (GDestroyNotify) interactor_free_with_repeaters
+ );
+
+ g_free (interactor);
+}
+
+void
+interactor_set_updated_callback (
+ Interactor *interactor,
+ InteractionUpdatedFunc callback
+) {
+ interactor->updated_cb = callback;
+}
+
+gint
+interactor_get_line_no (
+ Interactor *interactor
+) {
+ AnimElement *element = interactor_get_element (interactor);
+
+ if (element == NULL)
+ return -1;
+
+ return element->line_no;
+}
+
+const gchar *
+interactor_get_animation_direction (
+ const Interactor *interactor
+) {
+ if (interactor->animation == NULL)
+ return NULL;
+ return interactor->animation->direction;
+}
+
+const gchar *
+interactor_get_direction (
+ const Interactor *interactor
+) {
+ return interactor->direction;
+}
+
+gboolean
+interactor_get_action_hp_and_name (
+ const Interactor *interactor,
+ gint *hp,
+ gchar **name
+) {
+ if (interactor->action == NULL)
+ return FALSE;
+
+ action_get_hp_and_name (interactor->action, hp, name);
+ return TRUE;
+}
+
+void
+interactor_skip_current_frame (
+ Interactor *interactor
+) {
+ AnimElement *element;
+
+ interactor_loop_stop (interactor);
+ element = interactor_get_element (interactor);
+
+ if (element != NULL) {
+ g_return_if_fail (interactor->delay <= element->delay);
+ interactor_play (
+ interactor,
+ element->delay - interactor->delay
+ );
+ }
+}
+
+void
+interactor_add_repeater (
+ Interactor *interactor,
+ Interactor *repeater
+) {
+ interactor->repeaters = g_list_append (
+ interactor->repeaters,
+ repeater
+ );
+}