X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fanim.c;h=1389dc4e8815451aaa2a49d6d78190f841871e22;hp=c2ec75f64c65a87ea17212d0a66c34773de1a0d4;hb=HEAD;hpb=c07e4d51f30c46731bf28c42954dc7726457984e diff --git a/src/anim.c b/src/anim.c index c2ec75f6..1389dc4e 100644 --- a/src/anim.c +++ b/src/anim.c @@ -17,6 +17,7 @@ #include "files.h" #include "events.h" #include "screens.h" +#include "tape.h" #define DEBUG_ANIM_DELAY 0 @@ -33,22 +34,30 @@ #define NUM_GLOBAL_ANIM_PARTS_AND_TOONS MAX(NUM_GLOBAL_ANIM_PARTS_ALL, \ NUM_GLOBAL_TOON_PARTS) +#define MAX_GLOBAL_ANIM_LIST (NUM_GAME_MODES * \ + NUM_GLOBAL_ANIMS_AND_TOONS * \ + NUM_GLOBAL_ANIM_PARTS_AND_TOONS) + #define ANIM_CLASS_BIT_TITLE_INITIAL 0 #define ANIM_CLASS_BIT_TITLE 1 #define ANIM_CLASS_BIT_MAIN 2 -#define ANIM_CLASS_BIT_SCORES 3 -#define ANIM_CLASS_BIT_SUBMENU 4 -#define ANIM_CLASS_BIT_MENU 5 -#define ANIM_CLASS_BIT_TOONS 6 -#define ANIM_CLASS_BIT_NO_TITLE 7 +#define ANIM_CLASS_BIT_NAMES 3 +#define ANIM_CLASS_BIT_SCORES 4 +#define ANIM_CLASS_BIT_SCORESONLY 5 +#define ANIM_CLASS_BIT_SUBMENU 6 +#define ANIM_CLASS_BIT_MENU 7 +#define ANIM_CLASS_BIT_TOONS 8 +#define ANIM_CLASS_BIT_NO_TITLE 9 -#define NUM_ANIM_CLASSES 8 +#define NUM_ANIM_CLASSES 10 #define ANIM_CLASS_NONE 0 #define ANIM_CLASS_TITLE_INITIAL (1 << ANIM_CLASS_BIT_TITLE_INITIAL) #define ANIM_CLASS_TITLE (1 << ANIM_CLASS_BIT_TITLE) #define ANIM_CLASS_MAIN (1 << ANIM_CLASS_BIT_MAIN) +#define ANIM_CLASS_NAMES (1 << ANIM_CLASS_BIT_NAMES) #define ANIM_CLASS_SCORES (1 << ANIM_CLASS_BIT_SCORES) +#define ANIM_CLASS_SCORESONLY (1 << ANIM_CLASS_BIT_SCORESONLY) #define ANIM_CLASS_SUBMENU (1 << ANIM_CLASS_BIT_SUBMENU) #define ANIM_CLASS_MENU (1 << ANIM_CLASS_BIT_MENU) #define ANIM_CLASS_TOONS (1 << ANIM_CLASS_BIT_TOONS) @@ -58,6 +67,11 @@ ANIM_CLASS_SCORES | \ ANIM_CLASS_NO_TITLE) +#define ANIM_CLASS_TOONS_SCORESONLY (ANIM_CLASS_TOONS | \ + ANIM_CLASS_SCORES | \ + ANIM_CLASS_SCORESONLY | \ + ANIM_CLASS_NO_TITLE) + #define ANIM_CLASS_TOONS_MENU_MAIN (ANIM_CLASS_TOONS | \ ANIM_CLASS_MENU | \ ANIM_CLASS_MAIN | \ @@ -68,6 +82,12 @@ ANIM_CLASS_SUBMENU | \ ANIM_CLASS_NO_TITLE) +#define ANIM_CLASS_TOONS_MENU_SUBMENU_2 (ANIM_CLASS_TOONS | \ + ANIM_CLASS_MENU | \ + ANIM_CLASS_SUBMENU | \ + ANIM_CLASS_NAMES | \ + ANIM_CLASS_NO_TITLE) + // values for global animation states #define ANIM_STATE_INACTIVE 0 #define ANIM_STATE_RESTART (1 << 0) @@ -99,6 +119,8 @@ struct GlobalAnimPartControlInfo struct GraphicInfo graphic_info; struct GraphicInfo control_info; + boolean class_playfield_or_door; + int viewport_x; int viewport_y; int viewport_width; @@ -107,14 +129,21 @@ struct GlobalAnimPartControlInfo int x, y; int step_xoffset, step_yoffset; + int tile_x, tile_y; + int tile_xoffset, tile_yoffset; + unsigned int initial_anim_sync_frame; unsigned int anim_random_frame; - unsigned int step_delay, step_delay_value; + + DelayCounter step_delay; int init_delay_counter; int anim_delay_counter; int post_delay_counter; + int fade_delay_counter; + int fade_alpha; + boolean init_event_state; boolean anim_event_state; @@ -183,12 +212,13 @@ struct GameModeAnimClass { GAME_MODE_LEVELNR, ANIM_CLASS_TOONS_MENU_SUBMENU }, { GAME_MODE_INFO, ANIM_CLASS_TOONS_MENU_SUBMENU }, { GAME_MODE_SETUP, ANIM_CLASS_TOONS_MENU_SUBMENU }, - { GAME_MODE_PSEUDO_NAMESONLY, ANIM_CLASS_TOONS_MENU_SUBMENU }, - { GAME_MODE_PSEUDO_TYPENAMES, ANIM_CLASS_TOONS_MENU_SUBMENU }, + { GAME_MODE_PSEUDO_NAMESONLY, ANIM_CLASS_TOONS_MENU_SUBMENU_2 }, + { GAME_MODE_PSEUDO_TYPENAMES, ANIM_CLASS_TOONS_MENU_SUBMENU_2 }, { GAME_MODE_PSEUDO_MAINONLY, ANIM_CLASS_TOONS_MENU_MAIN }, { GAME_MODE_PSEUDO_TYPENAME, ANIM_CLASS_TOONS_MENU_MAIN }, - { GAME_MODE_PSEUDO_SCORESOLD, ANIM_CLASS_TOONS_SCORES }, - { GAME_MODE_PSEUDO_SCORESNEW, ANIM_CLASS_TOONS_SCORES }, + { GAME_MODE_PSEUDO_SCORESOLD, ANIM_CLASS_TOONS_SCORESONLY }, + { GAME_MODE_PSEUDO_SCORESNEW, ANIM_CLASS_TOONS_SCORESONLY }, + { GAME_MODE_SCOREINFO, ANIM_CLASS_TOONS_SCORES }, { GAME_MODE_EDITOR, ANIM_CLASS_NO_TITLE }, { GAME_MODE_PLAYING, ANIM_CLASS_NO_TITLE }, @@ -204,7 +234,9 @@ struct AnimClassGameMode { ANIM_CLASS_BIT_TITLE_INITIAL, GAME_MODE_TITLE_INITIAL }, { ANIM_CLASS_BIT_TITLE, GAME_MODE_TITLE }, { ANIM_CLASS_BIT_MAIN, GAME_MODE_MAIN }, + { ANIM_CLASS_BIT_NAMES, GAME_MODE_NAMES }, { ANIM_CLASS_BIT_SCORES, GAME_MODE_SCORES }, + { ANIM_CLASS_BIT_SCORESONLY, GAME_MODE_PSEUDO_SCORESONLY }, { ANIM_CLASS_BIT_SUBMENU, GAME_MODE_PSEUDO_SUBMENU }, { ANIM_CLASS_BIT_MENU, GAME_MODE_PSEUDO_MENU }, { ANIM_CLASS_BIT_TOONS, GAME_MODE_PSEUDO_TOONS }, @@ -222,6 +254,8 @@ static void ResetGlobalAnim_Clickable(void); static void ResetGlobalAnim_Clicked(void); static struct GlobalAnimControlInfo global_anim_ctrl[NUM_GAME_MODES]; +static struct GlobalAnimPartControlInfo *global_anim_list[MAX_GLOBAL_ANIM_LIST]; +static int num_global_anim_list = 0; static unsigned int anim_sync_frame = 0; @@ -285,6 +319,12 @@ int getAnimationFrame(int num_frames, int delay, int mode, int start_frame, else frame = gfx.anim_random_frame % num_frames; } + else if (mode & ANIM_LEVEL_NR) // play frames by level number + { + int level_pos = level_nr - gfx.anim_first_level; + + frame = level_pos % num_frames; + } else if (mode & (ANIM_CE_VALUE | ANIM_CE_SCORE | ANIM_CE_DELAY)) { frame = sync_frame % num_frames; @@ -321,33 +361,42 @@ static int getGlobalAnimationPart(struct GlobalAnimMainControlInfo *anim) static int compareGlobalAnimPartControlInfo(const void *obj1, const void *obj2) { const struct GlobalAnimPartControlInfo *o1 = - (struct GlobalAnimPartControlInfo *)obj1; + *(struct GlobalAnimPartControlInfo **)obj1; const struct GlobalAnimPartControlInfo *o2 = - (struct GlobalAnimPartControlInfo *)obj2; + *(struct GlobalAnimPartControlInfo **)obj2; int compare_result; if (o1->control_info.draw_order != o2->control_info.draw_order) compare_result = o1->control_info.draw_order - o2->control_info.draw_order; + else if (o1->mode_nr != o2->mode_nr) + compare_result = o1->mode_nr - o2->mode_nr; + else if (o1->anim_nr != o2->anim_nr) + compare_result = o1->anim_nr - o2->anim_nr; else compare_result = o1->nr - o2->nr; return compare_result; } -static int compareGlobalAnimMainControlInfo(const void *obj1, const void *obj2) +static boolean isPausedOnPlayfieldOrDoor(struct GlobalAnimPartControlInfo *part) { - const struct GlobalAnimMainControlInfo *o1 = - (struct GlobalAnimMainControlInfo *)obj1; - const struct GlobalAnimMainControlInfo *o2 = - (struct GlobalAnimMainControlInfo *)obj2; - int compare_result; + // only pause playfield and door animations when playing + if (game_status != GAME_MODE_PLAYING) + return FALSE; - if (o1->control_info.draw_order != o2->control_info.draw_order) - compare_result = o1->control_info.draw_order - o2->control_info.draw_order; - else - compare_result = o1->nr - o2->nr; + // do not pause animations when game ended (and engine is running) + if (checkGameEnded()) + return FALSE; - return compare_result; + // only pause animations on playfield and doors + if (!part->class_playfield_or_door) + return FALSE; + + // only pause animations when engine is paused or request dialog is active + if (!tape.pausing && !game.request_active) + return FALSE; + + return TRUE; } static void InitToonControls(void) @@ -392,7 +441,8 @@ static void InitToonControls(void) int sound = SND_UNDEFINED; int music = MUS_UNDEFINED; int graphic = IMG_TOON_1 + i; - int control = graphic; + + control = graphic; part->nr = part_nr; part->anim_nr = anim_nr; @@ -418,8 +468,8 @@ static void InitToonControls(void) part->initial_anim_sync_frame = 0; part->anim_random_frame = -1; - part->step_delay = 0; - part->step_delay_value = graphic_info[control].step_delay; + part->step_delay.count = 0; + part->step_delay.value = graphic_info[control].step_delay; part->state = ANIM_STATE_INACTIVE; part->last_anim_status = -1; @@ -525,8 +575,8 @@ static void InitGlobalAnimControls(void) part->initial_anim_sync_frame = 0; part->anim_random_frame = -1; - part->step_delay = 0; - part->step_delay_value = graphic_info[control].step_delay; + part->step_delay.count = 0; + part->step_delay.value = graphic_info[control].step_delay; part->state = ANIM_STATE_INACTIVE; part->last_anim_status = -1; @@ -548,18 +598,14 @@ static void InitGlobalAnimControls(void) anim->has_base = TRUE; } - // apply special settings for pointer-style animations + // apply special settings to pointer-style animations if (part->control_info.class == get_hash_from_key("pointer")) { - // force animation to be on top (must set anim and part control) - if (anim->control_info.draw_order == 0) - anim->control_info.draw_order = 1000000; - if (part->control_info.draw_order == 0) - part->control_info.draw_order = 1000000; - - // force animation to pass-through clicks (must set part control) - if (part->control_info.style == STYLE_DEFAULT) - part->control_info.style |= STYLE_PASSTHROUGH; + // force pointer-style animations to be checked for clicks first + part->control_info.draw_order = 1000000; + + // force pointer-style animations to pass-through clicks + part->control_info.style |= STYLE_PASSTHROUGH; } } @@ -573,26 +619,18 @@ static void InitGlobalAnimControls(void) InitToonControls(); - // sort all animations according to draw_order and animation number + // create list of all animation parts + num_global_anim_list = 0; for (m = 0; m < NUM_GAME_MODES; m++) - { - struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[m]; - - // sort all main animations for this game mode - qsort(ctrl->anim, ctrl->num_anims, - sizeof(struct GlobalAnimMainControlInfo), - compareGlobalAnimMainControlInfo); + for (a = 0; a < global_anim_ctrl[m].num_anims; a++) + for (p = 0; p < global_anim_ctrl[m].anim[a].num_parts_all; p++) + global_anim_list[num_global_anim_list++] = + &global_anim_ctrl[m].anim[a].part[p]; - for (a = 0; a < ctrl->num_anims; a++) - { - struct GlobalAnimMainControlInfo *anim = &ctrl->anim[a]; - - // sort all animation parts for this main animation - qsort(anim->part, anim->num_parts, - sizeof(struct GlobalAnimPartControlInfo), - compareGlobalAnimPartControlInfo); - } - } + // sort list of all animation parts according to draw_order and number + qsort(global_anim_list, num_global_anim_list, + sizeof(struct GlobalAnimPartControlInfo *), + compareGlobalAnimPartControlInfo); for (i = 0; i < NUM_GAME_MODES; i++) game_mode_anim_classes[i] = ANIM_CLASS_NONE; @@ -611,20 +649,143 @@ static void InitGlobalAnimControls(void) anim_classes_last = ANIM_CLASS_NONE; } +static void SetGlobalAnimEventsForCustomElements(int list_pos) +{ + int num_events = GetGlobalAnimEventValueCount(list_pos); + int i; + + for (i = 0; i < num_events; i++) + { + int event = GetGlobalAnimEventValue(list_pos, i); + + if (event & ANIM_EVENT_CE_CHANGE) + { + int nr = (event >> ANIM_EVENT_CE_BIT) & 0xff; + + if (nr >= 0 && nr < NUM_CUSTOM_ELEMENTS) + element_info[EL_CUSTOM_START + nr].has_anim_event = TRUE; + } + } +} + +void InitGlobalAnimEventsForCustomElements(void) +{ + int m, a, p; + int control; + + // custom element events for global animations only relevant while playing + m = GAME_MODE_PLAYING; + + for (a = 0; a < NUM_GLOBAL_ANIMS; a++) + { + int ctrl_id = GLOBAL_ANIM_ID_CONTROL_FIRST + a; + + control = global_anim_info[ctrl_id].graphic[GLOBAL_ANIM_ID_PART_BASE][m]; + + // if no base animation parameters defined, use default values + if (control == IMG_UNDEFINED) + control = IMG_INTERNAL_GLOBAL_ANIM_DEFAULT; + + SetGlobalAnimEventsForCustomElements(graphic_info[control].init_event); + SetGlobalAnimEventsForCustomElements(graphic_info[control].anim_event); + + for (p = 0; p < NUM_GLOBAL_ANIM_PARTS_ALL; p++) + { + control = global_anim_info[ctrl_id].graphic[p][m]; + + if (control == IMG_UNDEFINED) + continue; + + SetGlobalAnimEventsForCustomElements(graphic_info[control].init_event); + SetGlobalAnimEventsForCustomElements(graphic_info[control].anim_event); + } + } +} + void InitGlobalAnimations(void) { InitGlobalAnimControls(); } -static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage) +static void BlitGlobalAnimation(struct GlobalAnimPartControlInfo *part, + Bitmap *src_bitmap, int src_x0, int src_y0, + int drawing_target) { + struct GraphicInfo *g = &part->graphic_info; + struct GraphicInfo *c = &part->control_info; + void (*blit_bitmap)(Bitmap *, Bitmap *, int, int, int, int, int, int) = + (g->draw_masked ? BlitBitmapMasked : BlitBitmap); + void (*blit_screen)(Bitmap *, int, int, int, int, int, int) = + (g->draw_masked ? BlitToScreenMasked : BlitToScreen); Bitmap *fade_bitmap = (drawing_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source : drawing_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL); + int alpha = (c->fade_mode & FADE_MODE_FADE ? part->fade_alpha : g->alpha); + int x, y; + + for (y = 0; y < c->stacked_yfactor; y++) + { + for (x = 0; x < c->stacked_xfactor; x++) + { + int src_x = src_x0; + int src_y = src_y0; + int dst_x = part->x + x * (g->width + c->stacked_xoffset); + int dst_y = part->y + y * (g->height + c->stacked_yoffset); + int cut_x = 0; + int cut_y = 0; + int width = g->width; + int height = g->height; + + if (dst_x < 0) + { + width += dst_x; + cut_x = -dst_x; + dst_x = 0; + } + else if (dst_x > part->viewport_width - g->width) + { + width -= (dst_x - (part->viewport_width - g->width)); + } + + if (dst_y < 0) + { + height += dst_y; + cut_y = -dst_y; + dst_y = 0; + } + else if (dst_y > part->viewport_height - g->height) + { + height -= (dst_y - (part->viewport_height - g->height)); + } + + if (width <= 0 || height <= 0) + continue; + + src_x += cut_x; + src_y += cut_y; + + dst_x += part->viewport_x; + dst_y += part->viewport_y; + + SetBitmapAlphaNextBlit(src_bitmap, alpha); + + if (drawing_target == DRAW_TO_SCREEN) + blit_screen(src_bitmap, src_x, src_y, width, height, + dst_x, dst_y); + else + blit_bitmap(src_bitmap, fade_bitmap, src_x, src_y, width, height, + dst_x, dst_y); + } + } +} + +static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage) +{ int game_mode_anim_action[NUM_GAME_MODES]; int mode_nr; + int i; - if (!setup.toons) + if (!setup.global_animations) return; if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_1 && @@ -715,130 +876,73 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage) } } + // when restarting global animations, do not redraw them, but stop here + if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_RESTART) + return; + if (global.anim_status == GAME_MODE_LOADING) return; - for (mode_nr = 0; mode_nr < NUM_GAME_MODES; mode_nr++) + for (i = 0; i < num_global_anim_list; i++) { - struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr]; - int anim_nr; + struct GlobalAnimPartControlInfo *part = global_anim_list[i]; + struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[part->mode_nr]; + struct GlobalAnimMainControlInfo *anim = &ctrl->anim[part->anim_nr]; + struct GraphicInfo *g = &part->graphic_info; + Bitmap *src_bitmap; + int src_x, src_y; + int sync_frame; + int frame; + int last_anim_random_frame = gfx.anim_random_frame; + + if (!setup.toons && + part->graphic >= IMG_TOON_1 && + part->graphic <= IMG_TOON_20) + continue; // when preparing source fading buffer, only draw animations to be stopped if (drawing_target == DRAW_TO_FADE_SOURCE && - game_mode_anim_action[mode_nr] != ANIM_STOP) + game_mode_anim_action[part->mode_nr] != ANIM_STOP) continue; // when preparing target fading buffer, only draw animations to be started if (drawing_target == DRAW_TO_FADE_TARGET && - game_mode_anim_action[mode_nr] != ANIM_START) + game_mode_anim_action[part->mode_nr] != ANIM_START) continue; -#if 0 - if (mode_nr != GFX_SPECIAL_ARG_DEFAULT && - mode_nr != game_status) + if (!(anim->state & ANIM_STATE_RUNNING)) continue; -#endif - - for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++) - { - struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr]; - struct GraphicInfo *c = &anim->control_info; - int part_first, part_last; - int part_nr; - - if (!(anim->state & ANIM_STATE_RUNNING)) - continue; - - part_first = part_last = anim->active_part_nr; - - if (c->anim_mode & ANIM_ALL || anim->num_parts == 0) - { - int num_parts = anim->num_parts + (anim->has_base ? 1 : 0); - - part_first = 0; - part_last = num_parts - 1; - } - - for (part_nr = part_first; part_nr <= part_last; part_nr++) - { - struct GlobalAnimPartControlInfo *part = &anim->part[part_nr]; - struct GraphicInfo *g = &part->graphic_info; - Bitmap *src_bitmap; - int src_x, src_y; - int width = g->width; - int height = g->height; - int dst_x = part->x; - int dst_y = part->y; - int cut_x = 0; - int cut_y = 0; - int sync_frame; - int frame; - void (*blit_bitmap)(Bitmap *, Bitmap *, int, int, int, int, int, int) = - (g->draw_masked ? BlitBitmapMasked : BlitBitmap); - void (*blit_screen)(Bitmap *, int, int, int, int, int, int) = - (g->draw_masked ? BlitToScreenMasked : BlitToScreen); - int last_anim_random_frame = gfx.anim_random_frame; - - if (!(part->state & ANIM_STATE_RUNNING)) - continue; - - if (part->drawing_stage != drawing_stage) - continue; - if (part->x < 0) - { - dst_x = 0; - width += part->x; - cut_x = -part->x; - } - else if (part->x > part->viewport_width - g->width) - width -= (part->x - (part->viewport_width - g->width)); - - if (part->y < 0) - { - dst_y = 0; - height += part->y; - cut_y = -part->y; - } - else if (part->y > part->viewport_height - g->height) - height -= (part->y - (part->viewport_height - g->height)); - - if (width <= 0 || height <= 0) - continue; + if (!(part->state & ANIM_STATE_RUNNING)) + continue; - dst_x += part->viewport_x; - dst_y += part->viewport_y; + if (part->drawing_stage != drawing_stage) + continue; - sync_frame = anim_sync_frame - part->initial_anim_sync_frame; + // if game is paused, also pause playfield and door animations + if (isPausedOnPlayfieldOrDoor(part)) + part->initial_anim_sync_frame++; - // re-initialize random animation frame after animation delay - if (g->anim_mode == ANIM_RANDOM && - sync_frame % g->anim_delay == 0 && - sync_frame > 0) - part->anim_random_frame = GetSimpleRandom(g->anim_frames); + sync_frame = anim_sync_frame - part->initial_anim_sync_frame; - gfx.anim_random_frame = part->anim_random_frame; + // re-initialize random animation frame after animation delay + if (g->anim_mode == ANIM_RANDOM && + sync_frame % g->anim_delay == 0 && + sync_frame > 0) + part->anim_random_frame = GetSimpleRandom(g->anim_frames); - frame = getAnimationFrame(g->anim_frames, g->anim_delay, - g->anim_mode, g->anim_start_frame, - sync_frame); + gfx.anim_random_frame = part->anim_random_frame; - gfx.anim_random_frame = last_anim_random_frame; + frame = getAnimationFrame(g->anim_frames, g->anim_delay, + g->anim_mode, g->anim_start_frame, + sync_frame); - getGlobalAnimGraphicSource(part->graphic, frame, &src_bitmap, - &src_x, &src_y); + gfx.anim_random_frame = last_anim_random_frame; - src_x += cut_x; - src_y += cut_y; + getGlobalAnimGraphicSource(part->graphic, frame, &src_bitmap, + &src_x, &src_y); - if (drawing_target == DRAW_TO_SCREEN) - blit_screen(src_bitmap, src_x, src_y, width, height, - dst_x, dst_y); - else - blit_bitmap(src_bitmap, fade_bitmap, src_x, src_y, width, height, - dst_x, dst_y); - } - } + BlitGlobalAnimation(part, src_bitmap, src_x, src_y, drawing_target); } if (drawing_target == DRAW_TO_FADE_TARGET) @@ -870,8 +974,6 @@ void DrawGlobalAnimations(int drawing_target, int drawing_stage) ResetGlobalAnim_Clicked(); } - DrawEnvelopeRequestToScreen(drawing_target, drawing_stage); - if (gfx.cursor_mode_override != last_cursor_mode_override) SetMouseCursor(gfx.cursor_mode); } @@ -892,6 +994,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_1; + part->class_playfield_or_door = FALSE; + if (part->control_info.class == get_hash_from_key("window") || part->control_info.class == get_hash_from_key("border")) { @@ -917,7 +1021,7 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part viewport_width = part->graphic_info.width; viewport_height = part->graphic_info.height; - part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_2; + part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_3; // do not use global animation mouse pointer when reloading artwork if (global.anim_status != GAME_MODE_LOADING) @@ -929,6 +1033,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part viewport_y = DY; viewport_width = DXSIZE; viewport_height = DYSIZE; + + part->class_playfield_or_door = TRUE; } else if (part->control_info.class == get_hash_from_key("door_2")) { @@ -946,6 +1052,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part viewport_width = VXSIZE; viewport_height = VYSIZE; } + + part->class_playfield_or_door = TRUE; } else // default: "playfield" { @@ -953,6 +1061,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part viewport_y = REAL_SY; viewport_width = FULL_SXSIZE; viewport_height = FULL_SYSIZE; + + part->class_playfield_or_door = TRUE; } if (viewport_x != part->viewport_x || @@ -1038,6 +1148,13 @@ static void StopGlobalAnimMusic(struct GlobalAnimPartControlInfo *part) if (music == MUS_UNDEFINED) return; + char *anim_music = getMusicInfoEntryFilename(music); + char *curr_music = getCurrentlyPlayingMusicFilename(); + + // do not stop music if global anim music differs from current music + if (!strEqual(curr_music, anim_music)) + return; + StopMusic(); #if 0 @@ -1087,6 +1204,7 @@ static void PlayGlobalAnimSoundIfLoop(struct GlobalAnimPartControlInfo *part) static boolean checkGlobalAnimEvent(int anim_event, int mask) { int mask_anim_only = mask & ~ANIM_EVENT_PART_MASK; + int mask_ce_only = mask & ~ANIM_EVENT_PAGE_MASK; if (mask & ANIM_EVENT_ANY) return (anim_event & ANIM_EVENT_ANY); @@ -1094,6 +1212,9 @@ static boolean checkGlobalAnimEvent(int anim_event, int mask) return (anim_event & ANIM_EVENT_SELF); else if (mask & ANIM_EVENT_UNCLICK_ANY) return (anim_event & ANIM_EVENT_UNCLICK_ANY); + else if (mask & ANIM_EVENT_CE_CHANGE) + return (anim_event == mask || + anim_event == mask_ce_only); else return (anim_event == mask || anim_event == mask_anim_only); @@ -1102,67 +1223,116 @@ static boolean checkGlobalAnimEvent(int anim_event, int mask) static boolean isClickablePart(struct GlobalAnimPartControlInfo *part, int mask) { struct GraphicInfo *c = &part->control_info; - int num_init_events = GetGlobalAnimEventValueCount(c->init_event); - int num_anim_events = GetGlobalAnimEventValueCount(c->anim_event); int i; - for (i = 0; i < num_init_events; i++) + if (part->init_event_state) { - int init_event = GetGlobalAnimEventValue(c->init_event, i); + int num_init_events = GetGlobalAnimEventValueCount(c->init_event); - if (checkGlobalAnimEvent(init_event, mask)) - return TRUE; - } + for (i = 0; i < num_init_events; i++) + { + int init_event = GetGlobalAnimEventValue(c->init_event, i); - for (i = 0; i < num_anim_events; i++) + if (checkGlobalAnimEvent(init_event, mask)) + return TRUE; + } + } + else if (part->anim_event_state) { - int anim_event = GetGlobalAnimEventValue(c->anim_event, i); + int num_anim_events = GetGlobalAnimEventValueCount(c->anim_event); + + for (i = 0; i < num_anim_events; i++) + { + int anim_event = GetGlobalAnimEventValue(c->anim_event, i); - if (checkGlobalAnimEvent(anim_event, mask)) - return TRUE; + if (checkGlobalAnimEvent(anim_event, mask)) + return TRUE; + } } return FALSE; } -static boolean isClickedPart(struct GlobalAnimPartControlInfo *part, - int mx, int my, boolean clicked) +static boolean isInsidePartStacked(struct GlobalAnimPartControlInfo *part, + int mx, int my) { struct GraphicInfo *g = &part->graphic_info; + struct GraphicInfo *c = &part->control_info; int part_x = part->viewport_x + part->x; int part_y = part->viewport_y + part->y; int part_width = g->width; int part_height = g->height; + int x, y; + + for (y = 0; y < c->stacked_yfactor; y++) + { + for (x = 0; x < c->stacked_xfactor; x++) + { + int part_stacked_x = part_x + x * (part_width + c->stacked_xoffset); + int part_stacked_y = part_y + y * (part_height + c->stacked_yoffset); + + if (mx >= part_stacked_x && + mx < part_stacked_x + part_width && + my >= part_stacked_y && + my < part_stacked_y + part_height) + return TRUE; + } + } + return FALSE; +} + +static boolean isClickedPart(struct GlobalAnimPartControlInfo *part, + int mx, int my, boolean clicked) +{ // check if mouse click was detected at all if (!clicked) return FALSE; - // check if mouse click is inside the animation part's viewport + // check if mouse click is outside the animation part's viewport if (mx < part->viewport_x || mx >= part->viewport_x + part->viewport_width || my < part->viewport_y || my >= part->viewport_y + part->viewport_height) return FALSE; - // check if mouse click is inside the animation part's graphic - if (mx < part_x || - mx >= part_x + part_width || - my < part_y || - my >= part_y + part_height) - return FALSE; + // check if mouse click is inside the animation part's (stacked) graphic + if (isInsidePartStacked(part, mx, my)) + return TRUE; - return TRUE; + return FALSE; } static boolean clickBlocked(struct GlobalAnimPartControlInfo *part) { - return (part->control_info.style & STYLE_BLOCK ? TRUE : FALSE); + return ((part->control_info.style & STYLE_BLOCK) ? TRUE : FALSE); } static boolean clickConsumed(struct GlobalAnimPartControlInfo *part) { - return (part->control_info.style & STYLE_PASSTHROUGH ? FALSE : TRUE); + return ((part->control_info.style & STYLE_PASSTHROUGH) ? FALSE : TRUE); +} + +static void SetGlobalAnimPartTileXY(struct GlobalAnimPartControlInfo *part) +{ + // calculate playfield position (with scrolling) for related CE tile + // (do not use FX/FY, which are incorrect during envelope requests) + int FX0 = 2 * TILEX_VAR; // same as FX during DRAW_TO_FIELDBUFFER + int FY0 = 2 * TILEY_VAR; // same as FY during DRAW_TO_FIELDBUFFER + int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos); + int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos); + int sx = FX0 + SCREENX(part->tile_x) * TILEX_VAR; + int sy = FY0 + SCREENY(part->tile_y) * TILEY_VAR; + int cx = SX - REAL_SX; + int cy = SY - REAL_SY; + int x = sx - fx + cx; + int y = sy - fy + cy; + + part->tile_xoffset += part->step_xoffset; + part->tile_yoffset += part->step_yoffset; + + part->x = x + part->tile_xoffset; + part->y = y + part->tile_yoffset; } static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part, @@ -1190,7 +1360,7 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part, { struct GlobalAnimPartControlInfo *part2 = &anim2->part[part2_nr]; - if (!(part2->state & ANIM_STATE_RUNNING)) + if (!(part2->state & (ANIM_STATE_RUNNING | ANIM_STATE_WAITING))) continue; if (isClickablePart(part2, mask)) @@ -1232,6 +1402,67 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part, } } +static void InitGlobalAnim_Triggered_ByCustomElement(int nr, int page, + int x, int y, + int trigger_x, + int trigger_y) +{ + struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[GAME_MODE_PLAYING]; + + int event_value = ANIM_EVENT_CE_CHANGE; + int event_bits = (nr << ANIM_EVENT_CE_BIT) | (page << ANIM_EVENT_PAGE_BIT); + int mask = event_value | event_bits; + int anim2_nr; + + for (anim2_nr = 0; anim2_nr < ctrl->num_anims; anim2_nr++) + { + struct GlobalAnimMainControlInfo *anim2 = &ctrl->anim[anim2_nr]; + int part2_nr; + + for (part2_nr = 0; part2_nr < anim2->num_parts_all; part2_nr++) + { + struct GlobalAnimPartControlInfo *part2 = &anim2->part[part2_nr]; + + if (!(part2->state & (ANIM_STATE_RUNNING | ANIM_STATE_WAITING))) + continue; + + if (isClickablePart(part2, mask) && !part2->triggered) + { + struct GraphicInfo *c = &part2->control_info; + + if (c->position == POS_CE || + c->position == POS_CE_TRIGGER) + { + // store CE tile and offset position to handle scrolling + part2->tile_x = (c->position == POS_CE_TRIGGER ? trigger_x : x); + part2->tile_y = (c->position == POS_CE_TRIGGER ? trigger_y : y); + part2->tile_xoffset = c->x; + part2->tile_yoffset = c->y; + + // set resulting animation position relative to CE tile position + // (but only for ".init_event", not ".anim_event" type events) + if (part2->init_event_state) + SetGlobalAnimPartTileXY(part2); + + // restart animation (by using current sync frame) + part2->initial_anim_sync_frame = anim_sync_frame; + } + + part2->triggered = TRUE; + + // do not trigger any other animation if CE change event was consumed + if (c->style == STYLE_CONSUME_CE_EVENT) + return; + +#if 0 + Debug("anim:InitGlobalAnim_Triggered_ByCustomElement", + "%d.%d TRIGGERED BY CE %d", anim2_nr, part2_nr, nr + 1); +#endif + } + } + } +} + static void HandleGlobalAnimDelay(struct GlobalAnimPartControlInfo *part, int delay_type, char *info_text) { @@ -1270,6 +1501,11 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, struct GraphicInfo *g = &part->graphic_info; struct GraphicInfo *c = &part->control_info; boolean viewport_changed = SetGlobalAnimPart_Viewport(part); + int alpha = (g->alpha != -1 ? g->alpha : SDL_ALPHA_OPAQUE); + + // if game is paused, also pause playfield and door animations + if (isPausedOnPlayfieldOrDoor(part)) + return state; if (viewport_changed) state |= ANIM_STATE_RESTART; @@ -1295,12 +1531,25 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, part->anim_event_state = (c->anim_event != ANIM_EVENT_UNDEFINED); part->initial_anim_sync_frame = - (g->anim_global_sync ? 0 : anim_sync_frame + part->init_delay_counter); + (g->anim_global_sync || g->anim_global_anim_sync ? 0 : + anim_sync_frame + part->init_delay_counter); // do not re-initialize random animation frame after fade-in if (part->anim_random_frame == -1) part->anim_random_frame = GetSimpleRandom(g->anim_frames); + if (c->fade_mode & FADE_MODE_FADE) + { + // when fading in screen, first frame is 100 % transparent or opaque + part->fade_delay_counter = c->fade_delay + 1; + part->fade_alpha = (c->fade_mode == FADE_MODE_FADE_IN ? 0 : alpha); + } + else + { + part->fade_delay_counter = 0; + part->fade_alpha = -1; + } + if (c->direction & MV_HORIZONTAL) { int pos_bottom = part->viewport_height - g->height; @@ -1403,7 +1652,7 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, part->init_event_state) { if (part->initial_anim_sync_frame > 0) - part->initial_anim_sync_frame -= part->init_delay_counter - 1; + part->initial_anim_sync_frame = anim_sync_frame; part->init_delay_counter = 1; part->init_event_state = FALSE; @@ -1432,9 +1681,13 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, HandleGlobalAnimDelay(part, ANIM_DELAY_INIT, "START [INIT_DELAY]"); HandleGlobalAnimEvent(part, ANIM_EVENT_START, "START [ANIM]"); - } - return ANIM_STATE_WAITING; + // continue with state ANIM_STATE_RUNNING (set below) + } + else + { + return ANIM_STATE_WAITING; + } } if (part->init_event_state) @@ -1510,11 +1763,18 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, return ANIM_STATE_WAITING; } + if (part->fade_delay_counter > 0) + { + part->fade_delay_counter--; + part->fade_alpha = alpha * (c->fade_mode == FADE_MODE_FADE_IN ? + c->fade_delay - part->fade_delay_counter : + part->fade_delay_counter) / c->fade_delay; + } + // special case to prevent expiring loop sounds when playing PlayGlobalAnimSoundIfLoop(part); - if (!DelayReachedExt(&part->step_delay, part->step_delay_value, - anim_sync_frame)) + if (!DelayReachedExt(&part->step_delay, anim_sync_frame)) return ANIM_STATE_RUNNING; #if 0 @@ -1529,8 +1789,16 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, } #endif - part->x += part->step_xoffset; - part->y += part->step_yoffset; + if (c->position == POS_CE || + c->position == POS_CE_TRIGGER) + { + SetGlobalAnimPartTileXY(part); + } + else + { + part->x += part->step_xoffset; + part->y += part->step_yoffset; + } anim->last_x = part->x; anim->last_y = part->y; @@ -1653,9 +1921,13 @@ static void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim, for (i = 0; i < num_parts; i++) anim->part[i].state = ANIM_STATE_INACTIVE; - // ... then set current animation parts to "running" + // ... then set current animation part to "running" ... part->state = ANIM_STATE_RUNNING; + // ... unless it is waiting for an initial event + if (part->init_event_state) + part->state = ANIM_STATE_WAITING; + anim->state = HandleGlobalAnim_Part(part, anim->state); if (anim->state & ANIM_STATE_RESTART) @@ -1757,7 +2029,10 @@ static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *part) if (event_action == ANIM_EVENT_ACTION_NONE) return FALSE; - PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, event_action, 0); + if (event_action < MAX_IMAGE_FILES) + PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, event_action, 0); + else + OpenURLFromHash(anim_url_hash, event_action); // check if further actions are allowed to be executed if (part->control_info.style & STYLE_MULTIPLE_ACTIONS) @@ -1768,29 +2043,17 @@ static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *part) static void InitGlobalAnim_Clickable(void) { - int mode_nr; + int i; - for (mode_nr = 0; mode_nr < NUM_GAME_MODES; mode_nr++) + for (i = 0; i < num_global_anim_list; i++) { - struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr]; - int anim_nr; - - for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++) - { - struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr]; - int part_nr; - - for (part_nr = 0; part_nr < anim->num_parts_all; part_nr++) - { - struct GlobalAnimPartControlInfo *part = &anim->part[part_nr]; + struct GlobalAnimPartControlInfo *part = global_anim_list[i]; - if (part->triggered) - part->clicked = TRUE; + if (part->triggered) + part->clicked = TRUE; - part->triggered = FALSE; - part->clickable = FALSE; - } - } + part->triggered = FALSE; + part->clickable = FALSE; } } @@ -1804,103 +2067,93 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event) boolean anything_clicked = FALSE; boolean any_part_clicked = FALSE; boolean any_event_action = FALSE; - int mode_nr; int i; - // check game modes in reverse draw order (to stop when clicked) - for (mode_nr = NUM_GAME_MODES - 1; mode_nr >= 0; mode_nr--) + // check animation parts in reverse draw order (to stop when clicked) + for (i = num_global_anim_list - 1; i >= 0; i--) { - struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr]; - int anim_nr; + struct GlobalAnimPartControlInfo *part = global_anim_list[i]; - // check animations in reverse draw order (to stop when clicked) - for (anim_nr = ctrl->num_anims - 1; anim_nr >= 0; anim_nr--) - { - struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr]; - int part_nr; - - // check animation parts in reverse draw order (to stop when clicked) - for (part_nr = anim->num_parts_all - 1; part_nr >= 0; part_nr--) - { - struct GlobalAnimPartControlInfo *part = &anim->part[part_nr]; + // if request dialog is active, only handle pointer-style animations + if (game.request_active && + part->control_info.class != get_hash_from_key("pointer")) + continue; - if (clicked_event == ANIM_CLICKED_RESET) - { - part->clicked = FALSE; + if (clicked_event == ANIM_CLICKED_RESET) + { + part->clicked = FALSE; - continue; - } + continue; + } - if (!part->clickable) - continue; + if (!part->clickable) + continue; - if (!(part->state & ANIM_STATE_RUNNING)) - continue; + if (!(part->state & ANIM_STATE_RUNNING)) + continue; - // always handle "any" click events (clicking anywhere on screen) ... - if (clicked_event == ANIM_CLICKED_PRESSED && - isClickablePart(part, ANIM_EVENT_ANY)) - { + // always handle "any" click events (clicking anywhere on screen) ... + if (clicked_event == ANIM_CLICKED_PRESSED && + isClickablePart(part, ANIM_EVENT_ANY)) + { #if DEBUG_ANIM_EVENTS - Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY ANY", - part->old_anim_nr + 1, part->old_nr + 1); + Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY ANY", + part->old_anim_nr + 1, part->old_nr + 1); #endif - anything_clicked = part->clicked = TRUE; - click_consumed |= clickConsumed(part); - } + anything_clicked = part->clicked = TRUE; + click_consumed |= clickConsumed(part); + } - // always handle "unclick:any" events (releasing anywhere on screen) ... - if (clicked_event == ANIM_CLICKED_RELEASED && - isClickablePart(part, ANIM_EVENT_UNCLICK_ANY)) - { + // always handle "unclick:any" events (releasing anywhere on screen) ... + if (clicked_event == ANIM_CLICKED_RELEASED && + isClickablePart(part, ANIM_EVENT_UNCLICK_ANY)) + { #if DEBUG_ANIM_EVENTS - Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY UNCLICK:ANY", - part->old_anim_nr + 1, part->old_nr + 1); + Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY UNCLICK:ANY", + part->old_anim_nr + 1, part->old_nr + 1); #endif - anything_clicked = part->clicked = TRUE; - click_consumed |= clickConsumed(part); - } + anything_clicked = part->clicked = TRUE; + click_consumed |= clickConsumed(part); + } - // ... but only handle the first (topmost) clickable animation - if (any_part_clicked) - continue; + // ... but only handle the first (topmost) clickable animation + if (any_part_clicked) + continue; - if (clicked_event == ANIM_CLICKED_PRESSED && - isClickedPart(part, mx, my, TRUE)) - { + if (clicked_event == ANIM_CLICKED_PRESSED && + isClickedPart(part, mx, my, TRUE)) + { #if 0 - Debug("anim:InitGlobalAnim_Clicked", "%d.%d CLICKED [%d]", - anim_nr, part_nr, part->control_info.anim_event_action); + Debug("anim:InitGlobalAnim_Clicked", "%d.%d CLICKED [%d]", + anim_nr, part_nr, part->control_info.anim_event_action); #endif - // after executing event action, ignore any further actions - if (!any_event_action && DoGlobalAnim_EventAction(part)) - any_event_action = TRUE; + // after executing event action, ignore any further actions + if (!any_event_action && DoGlobalAnim_EventAction(part)) + any_event_action = TRUE; - // determine if mouse clicks should be blocked from other animations - any_part_clicked |= clickConsumed(part); + // determine if mouse clicks should be blocked from other animations + any_part_clicked |= clickConsumed(part); - if (isClickablePart(part, ANIM_EVENT_SELF)) - { + if (isClickablePart(part, ANIM_EVENT_SELF)) + { #if DEBUG_ANIM_EVENTS - Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY SELF", - part->old_anim_nr + 1, part->old_nr + 1); + Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY SELF", + part->old_anim_nr + 1, part->old_nr + 1); #endif - anything_clicked = part->clicked = TRUE; - click_consumed |= clickConsumed(part); - } + anything_clicked = part->clicked = TRUE; + click_consumed |= clickConsumed(part); + } - // determine if mouse clicks should be blocked by this animation - click_consumed |= clickBlocked(part); + // determine if mouse clicks should be blocked by this animation + click_consumed |= clickBlocked(part); - // check if this click is defined to trigger other animations - InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action, - ANIM_EVENT_CLICK, "CLICK"); - } - } + // check if this click is defined to trigger other animations + InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action, + ANIM_EVENT_CLICK, "CLICK"); } } @@ -1930,6 +2183,23 @@ static void ResetGlobalAnim_Clicked(void) InitGlobalAnim_Clicked(-1, -1, ANIM_CLICKED_RESET); } +void RestartGlobalAnimsByStatus(int status) +{ + int anim_status_last = global.anim_status; + + global.anim_status = status; + + // force restarting global animations by changed global animation status + DrawGlobalAnimationsExt(DRAW_TO_SCREEN, DRAW_GLOBAL_ANIM_STAGE_RESTART); + + global.anim_status = anim_status_last; +} + +void SetAnimStatusBeforeFading(int status) +{ + anim_status_last_before_fading = status; +} + boolean HandleGlobalAnimClicks(int mx, int my, int button, boolean force_click) { static boolean click_consumed = FALSE; @@ -1960,3 +2230,19 @@ boolean HandleGlobalAnimClicks(int mx, int my, int button, boolean force_click) return click_consumed_current; } + +int getGlobalAnimSyncFrame(void) +{ + return anim_sync_frame; +} + +void HandleGlobalAnimEventByElementChange(int element, int page, int x, int y, + int trigger_x, int trigger_y) +{ + if (!IS_CUSTOM_ELEMENT(element)) + return; + + // custom element stored as 0 to 255, change page stored as 1 to 32 + InitGlobalAnim_Triggered_ByCustomElement(element - EL_CUSTOM_START, page + 1, + x, y, trigger_x, trigger_y); +}