#include "eruta.h" #include "mem.h" #include "sprite.h" #include "spritestate.h" #include "bad.h" #include "monolog.h" #include "callrb.h" /* Sprite state layer functions. */ SpriteStateLayer * spritestatelayer_init_empty(SpriteStateLayer * me) { if (!me) return NULL; me->flags = 0; me->tint = al_map_rgba(1.0, 1.0, 1.0, 0.0); return me; } /* Sprite state action functions. */ SpriteStateAction * spritestateaction_init_empty(SpriteStateAction * me) { if (!me) return NULL; me->done = FALSE; me->loop = SPRITESTATE_ACTION_LOOP; me->next_action = -1; return me; } /* Sprite state functions. */ SpriteState * spritestate_alloc() { return STRUCT_ALLOC(SpriteState); } Sprite * spritestate_sprite_(SpriteState * self, Sprite * sprite) { int index; if(!self) return NULL; self->sprite = sprite; self->action_index = -1; self->action_now = NULL; self->frame_now = NULL; self->frame_index = -1; self->time = 0.0; self->pose_now = SPRITE_STAND; self->direction_now = SPRITE_ALL; /* No cleanup of sprite as it is not owned by the sprite state. */ return self->sprite; } /* Clean up the sprite state. * No cleanup of the sprite itself since the Sprite * is owned by the sprite * list. */ SpriteState * spritestate_done(SpriteState * self) { int index; return self; } SpriteState * spritestate_free(SpriteState * self) { spritestate_done(self); return mem_free(self); } Sprite * spritestate_sprite(SpriteState * self) { if(!self) return NULL; return self->sprite; } /** Thing is passed as a void data pointer to avoid dependencies. */ SpriteState * spritestate_init(SpriteState * self, Sprite * sprite, void * data) { int index; if(!self) return NULL; spritestate_sprite_(self, sprite); self->speedup = 1.0; for (index = 0 ; index < SPRITESTATE_LAYER_MAX; index ++) { spritestatelayer_init_empty(self->layers + index); } for (index = 0 ; index < SPRITESTATE_ACTION_MAX; index ++) { spritestateaction_init_empty(self->actions + index); } self->data = data; return self; } void * spritestate_data(SpriteState * self) { if (!self) return NULL; return self->data; } double spritestate_speedup(SpriteState * self) { if (!self) return 0.0; return self->speedup; } double spritestate_speedup_(SpriteState * self, double speedup) { if (!self) return 0.0; return self->speedup = speedup; } SpriteState * spritestate_new(Sprite * sprite, void * data) { return spritestate_init(spritestate_alloc(), sprite, data); } /** Returns whether or not the sprite state can be drawn or not. */ int spritestate_can_draw_p(SpriteState * self) { if (!self) return FALSE; if (!(self->frame_now)) return FALSE; return TRUE; } /* Draws the given layer of the sprite using the sprite state. */ void spritestate_draw_frame(SpriteState * me, SpriteFrame * frame, Point * at) { int index, stop; SpriteCell * cell; Color * tint; if (!me) return; if (!frame) { /* Draw a red box to show the missing frame */ al_draw_filled_rectangle(at->x, at->y, at->x+16, at->y+16, al_map_rgb(255, 64, 64)); return; }; al_hold_bitmap_drawing(true); stop = spriteframe_maxlayers(frame); for (index = 0; index < stop ; index++) { if ((BIT_ISFLAG(me->layers[index].flags, SPRITESTATE_LAYER_HIDDEN))) { continue; /* Skip invisible layers. */ } cell = spriteframe_cell(frame, index); tint = spritestate_get_layer_tint(me, index); if (tint) { spritecell_draw_tinted(cell, at, (*tint)); } else { spritecell_draw(cell, at); } } al_hold_bitmap_drawing(false); } /* Draw the SpriteState at the given location. This takes the current action, frame, layers and offsets into consideration. */ void spritestate_draw(SpriteState * self, Point * at) { if (!self) return; if (!self->sprite) return; spritestate_draw_frame(self, self->frame_now, at); } /* Sets the spritestate's current action and current frame. Returns self if ok, NULL if out of bounds. */ SpriteState * spritestate_now_(SpriteState * self, int actionnow, int framenow) { SpriteAction * action; SpriteFrame * frame; Sprite * sprite; if (!self) return NULL; sprite = self->sprite; if (!sprite) return NULL; if (bad_outofboundsi(actionnow, 0, sprite_maxactions(sprite))) return NULL; frame = sprite_frame(sprite, actionnow, framenow); if (!frame) { return NULL; } self->frame_index = framenow; self->action_index = actionnow; self->frame_now = frame; self->time = 0.0; return self; } /* Advances the frame of the sprite state if needed, depending on the * looping and stop mode. */ void spritestate_next_frame(SpriteState * self) { int next = self->frame_index + 1; int action; int action_loop; /* Loop if needed. */ if (next >= sprite_framesused(self->sprite, self->action_index)) { action_loop = spritestate_get_action_loop(self, self->action_index); /* It's a one-shot action. Do NOT cycle. */ if (action_loop & SPRITESTATE_ACTION_ONESHOT) { /* If it's a stop action, leave it there, otherwise go back to standing pose. */ if (action_loop & SPRITESTATE_ACTION_STOP) { /* First time at end of a one shot stop action... */ if (!self->actions[self->action_index].done) { /* Notify scripting of end of this loop. */ callrb_sprite_event(self, action_loop, NULL); /* Set action to done to prevent double emitting the event above. */ self->actions[self->action_index].done = TRUE; } } else { /* Notify script side. */ callrb_sprite_event(self, action_loop, NULL); /* Go to back to first frame of standing pose with current direction. */ action = sprite_action_index_for (self->sprite, SPRITE_WALK, self->direction_now); spritestate_pose_(self, SPRITE_WALK); spritestate_now_(self, action, 0); /* Action to done. */ self->actions[self->action_index].done = TRUE; } } else { /* Loop back to first frame.*/ spritestate_now_(self, self->action_index, 0); /* Set action to not done. */ self->actions[self->action_index].done = FALSE; } } else { /* Advance to next frame. */ spritestate_now_(self, self->action_index, next); /* Set action to not done. */ self->actions[self->action_index].done = FALSE; } } /* Updates the spritestate. * dt is the time passed since the last update in seconds (usuallu around 0.02). */ void spritestate_update(SpriteState * self, double dt) { Sprite * sprite; if (!self) return; sprite = self->sprite; if (!sprite) return; if(!self->frame_now) { int x, y, w, h; LOG_LEVEL(__FILE__, "NULL current sprite frame!: %d\n", self->action_index); // try to restore back to first frame if out of whack somehow. spritestate_now_(self, self->action_index, 0); return; } self->time += (self->speedup * dt); if(self->time > spriteframe_duration(self->frame_now)) { spritestate_next_frame(self); } } /* Sets the spritestate's current pose and direction. Will reset the frame to 0. * Returns negative if the pose could not be found in this sprite; * Otherwise returns the index of the used sprite action. * Returns -2 without effect if the pose and direction are identical to the ones * currently set. */ int spritestate_posedirection_(SpriteState * self, int pose, int direction) { int max, index; SpriteAction * action; Sprite * sprite; if (!self) return -1; sprite = self->sprite; if (!sprite) return -1; if ((self->pose_now == pose) && (self->direction_now == direction)) { /* Do nothing if the pose and ditr are not actually changed. */ return -2; } self->pose_now = pose; self->direction_now = direction; max = sprite_maxactions(sprite); for (index = 0; index < max; index ++) { action = sprite_action(sprite, index); if (spriteaction_matches_pose(action, pose, direction)) { spritestate_now_(self, index, 0); return index; } } return -1; } /* Changes the direction but keeps the pose. * No effect if direction is the current active one.*/ int spritestate_direction_(SpriteState * self, int direction) { if (!self) return -1; return spritestate_posedirection_(self, self->pose_now, direction); } /* Changes the pose but keeps the direction. * No effect if pose is the current active one. */ int spritestate_pose_(SpriteState * self, int pose) { if (!self) return -1; return spritestate_posedirection_(self, pose, self->direction_now); } /* Returns true if the layer or self are out of bounds, false if not. */ int spritestate_is_bad_layer(SpriteState * self, int layer) { if (!self) return TRUE; if (layer < 0) return TRUE; if (layer > SPRITESTATE_LAYER_MAX) return TRUE; return FALSE; } /* Returns the current active pose of the sprite state.*/ int spritestate_pose(SpriteState * self) { if (!self) return SPRITE_STAND; return self->pose_now; } /* Returns the current active direction of the sprite state.*/ int spritestate_direction(SpriteState * self) { if (!self) return SPRITE_NO_DIRECTION; return self->direction_now; } /* Sets the tint color for a layer of the sprite. */ int spritestate_tint_layer(SpriteState * self, int layer, Color color) { if (spritestate_is_bad_layer(self, layer)) return -1; self->layers[layer].tint = color; self->layers[layer].flags = BIT_SETFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_TINTED); return layer; } /** Unsets the tint color for a layer of the sprite. */ int spritestate_remove_tint_layer(SpriteState * self, int layer) { if (!spritestate_is_layer_tinted(self, layer)) return -1; self->layers[layer].flags = BIT_UNFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_TINTED); return layer; } /** Returns TRUE if the layer of the sprite is tinted, FALSE if not. */ int spritestate_is_layer_tinted(SpriteState * self, int layer) { if (spritestate_is_bad_layer(self, layer)) return FALSE; if (!(BIT_ISFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_TINTED))) return FALSE; return TRUE; } /** Returns apointer to the tint of the layer or NULL if not tinted or * if the layer is out of bounds. */ Color * spritestate_get_layer_tint(SpriteState * self, int layer) { if (!spritestate_is_layer_tinted(self, layer)) return NULL; return &(self->layers[layer].tint); } /** Hides or unhides the given layer of the sprite. */ int spritestate_set_layer_hidden(SpriteState * self, int layer, int hidden) { if (hidden) { self->layers[layer].flags = BIT_SETFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_HIDDEN); } else { self->layers[layer].flags = BIT_UNFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_HIDDEN); } return spritestate_get_layer_hidden(self, layer); } /** Returns true if the sprite's layer is hidden false if not. */ int spritestate_get_layer_hidden(SpriteState * self, int layer) { if (spritestate_is_bad_layer(self, layer)) return TRUE; return ((BIT_ISFLAG(self->layers[layer].flags, SPRITESTATE_LAYER_HIDDEN))); } /* Returns true if the action or self are out of bounds, false if not. */ int spritestate_is_bad_action(SpriteState * self, int action) { if (!self) return TRUE; if (action < 0) return TRUE; if (action > SPRITESTATE_ACTION_MAX) return TRUE; return FALSE; } /** Sets the loop mode for the given action of the sprite. */ int spritestate_set_action_loop(SpriteState * self, int action, int loopmode) { if (spritestate_is_bad_action(self, action)) return -1; self->actions[action].loop = loopmode; return self->actions[action].loop; } /** Gets the loop mode for the given action of the sprite. */ int spritestate_get_action_loop(SpriteState * self, int action) { if (spritestate_is_bad_action(self, action)) return -1; return self->actions[action].loop; } /** Returns true if the last set action is done, false if not. */ int spritestate_is_action_done(SpriteState * self, int action) { if (spritestate_is_bad_action(self, action)) return -1; return self->actions[action].done; } /** Sets the loop mode for the given pose and direction of the sprite. */ int spritestate_set_pose_direction_loop (SpriteState * self, int pose, int direction, int loopmode) { int action = sprite_action_index_for(self->sprite, pose, direction); return spritestate_set_action_loop(self, action, loopmode); } /** Sets the loop mode for the given pose and direction of the sprite. */ int spritestate_get_pose_direction_loop (SpriteState * self, int pose, int direction) { int action = sprite_action_index_for(self->sprite, pose, direction); return spritestate_get_action_loop(self, action); }