diff --git a/include/parts.h b/include/parts.h index e814436..db6ff6b 100644 --- a/include/parts.h +++ b/include/parts.h @@ -131,6 +131,7 @@ void PE_set_active_controller(int controller_no); int PE_get_active_controller(void); int PE_get_system_controller(void); void PE_parts_set_want_save(int parts_no, bool want_save); +bool PE_save_thumbnail(struct string *filename, int reduction_factor); bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state); int PE_get_movie_sprite(int parts_no, int state); float PE_parts_get_absolute_x(int parts_no); @@ -247,6 +248,7 @@ void PE_SeekEndMotion(void); void PE_UpdateMotionTime(int time, bool skip); bool PE_IsMotion(void); int PE_GetMotionEndTime(void); +void PE_PauseMotion(bool pause); // text.c bool PE_SetText(int parts_no, struct string *text, int state); diff --git a/src/hll/ChipmunkSpriteEngine.c b/src/hll/ChipmunkSpriteEngine.c index b44f96d..c656578 100644 --- a/src/hll/ChipmunkSpriteEngine.c +++ b/src/hll/ChipmunkSpriteEngine.c @@ -111,8 +111,8 @@ HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_SetBrightness, int sp_n HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_GetBrightness, int sp_no); static bool keep_previous_view = true; -HLL_WARN_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on); -HLL_WARN_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode); +HLL_QUIET_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on); +HLL_QUIET_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode); HLL_QUIET_UNIMPLEMENTED(false, bool, ChipmunkSpriteEngine, KeepPreviousView); static void ChipmunkSpriteEngine_Sleep(void) diff --git a/src/hll/PartsEngine.c b/src/hll/PartsEngine.c index 08d445c..3d758e4 100644 --- a/src/hll/PartsEngine.c +++ b/src/hll/PartsEngine.c @@ -260,10 +260,19 @@ static int PartsEngine_PartsFunc(int func_id, struct page **array_int, REQUIRE_INTS(1); ints[0].i = PE_get_system_controller(); return 1; + case 3: // void PauseMotion(bool pause) + REQUIRE_INTS(1); + PE_PauseMotion(!!ints[0].i); + return 1; case 4: // void SetWantSave(int parts_no, bool want_save) REQUIRE_INTS(2); PE_parts_set_want_save(ints[0].i, !!ints[1].i); return 1; + case 6: // bool SaveThumbnail(string filename, int reduction_factor) + REQUIRE_INTS(2); + REQUIRE_STRINGS(1); + ints[1].i = PE_save_thumbnail(heap_get_string(strings[0].i), ints[0].i); + return 1; case 40: // float PARTS_GetAbsoluteX(int number) REQUIRE_INTS(1); REQUIRE_FLOATS(1); diff --git a/src/hll/SACT2.c b/src/hll/SACT2.c index 19ca021..94fb24d 100644 --- a/src/hll/SACT2.c +++ b/src/hll/SACT2.c @@ -1031,6 +1031,7 @@ int sact_TRANS_Begin(int type) int sact_TRANS_Update(float rate) { + sprite_call_plugins(); if (scene_is_dirty) scene_render(); return effect_update(rate); diff --git a/src/hll/StoatSpriteEngine.c b/src/hll/StoatSpriteEngine.c index cd9b1b9..11e92e6 100644 --- a/src/hll/StoatSpriteEngine.c +++ b/src/hll/StoatSpriteEngine.c @@ -188,8 +188,8 @@ float StoatSpriteEngine_FPS_Get(void) HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, VIEW_SetOffsetPos, int x, int y); static bool keep_previous_view = true; -HLL_WARN_UNIMPLEMENTED((keep_previous_view = on, true), bool, StoatSpriteEngine, KeepPreviousView_SetMode, bool on); -HLL_WARN_UNIMPLEMENTED(keep_previous_view, bool, StoatSpriteEngine, KeepPreviousView_GetMode); +HLL_QUIET_UNIMPLEMENTED((keep_previous_view = on, true), bool, StoatSpriteEngine, KeepPreviousView_SetMode, bool on); +HLL_QUIET_UNIMPLEMENTED(keep_previous_view, bool, StoatSpriteEngine, KeepPreviousView_GetMode); /* * NOTE: The multisprite "type" alters the behavior of the multisprite functions. diff --git a/src/parts/motion.c b/src/parts/motion.c index 13a593f..2d29d0d 100644 --- a/src/parts/motion.c +++ b/src/parts/motion.c @@ -38,6 +38,8 @@ static struct parts_motion_list global_motion_list = // true after call to BeginMotion until the motion ends or EndMotion is called static bool is_motion = false; +// true while the motion is paused (time does not advance) +static bool is_motion_paused = false; // the elapsed time of the current motion static int motion_t = 0; // the end time of the current motion @@ -498,6 +500,7 @@ void PE_BeginMotion(void) // FIXME: starting a motion seems to clear non-default states motion_t = 0; is_motion = true; + is_motion_paused = false; parts_init_all_motion(); } @@ -508,6 +511,7 @@ void PE_EndMotion(void) motion_t = 0; motion_end_t = 0; is_motion = false; + is_motion_paused = false; parts_fini_all_motion(); } @@ -531,6 +535,8 @@ void PE_SeekEndMotion(void) void PE_UpdateMotionTime(int time, possibly_unused bool skip) { // TODO: use skip + if (is_motion_paused) + return; PE_SetMotionTime(motion_t + time); } @@ -543,3 +549,20 @@ int PE_GetMotionEndTime(void) { return motion_end_t; } + +void PE_PauseMotion(bool pause) +{ + if (parts_began_click) + return; + if (!is_motion) + return; + if (is_motion_paused == pause) + return; + is_motion_paused = pause; + + // While paused, the per-frame advance stops updating the whole-screen + // vibration offset. Reset it to the origin so the screen doesn't stay + // stuck at the last shake position. + if (pause) + parts_set_global_pos((Point){ 0, 0 }); +} diff --git a/src/parts/parts.c b/src/parts/parts.c index fd8ec8f..9279277 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -40,7 +40,6 @@ struct parts_controller_stack ctrl_stack; bool parts_multi_controller; static void ctrl_stack_init(void); -static void ctrl_stack_fini(void); #define PARTS_PARAMS_INITIALIZER (struct parts_params) { \ .z = 1, \ @@ -1062,7 +1061,7 @@ void PE_Reset(void) { PE_ReleaseAllParts(); PE_ReleaseMessage(); - ctrl_stack_fini(); + ctrl_stack_init(); sact_ModuleFini(); } @@ -1955,6 +1954,44 @@ bool PE_SetThumbnailMode(bool mode) return true; } +bool PE_save_thumbnail(struct string *filename, int reduction_factor) +{ + if (reduction_factor < 1) + reduction_factor = 1; + + Texture *src = gfx_main_surface(); + int w = src->w / reduction_factor; + int h = src->h / reduction_factor; + + // Downscale by repeatedly halving until we are within a factor of two of + // the target size, then do the final stretch. This avoids the aliasing + // that a single bilinear minification would otherwise produce. + Texture tmp, *cur = src; + bool have_tmp = false; + while (cur->w / 2 > w && cur->h / 2 > h) { + Texture next; + gfx_init_texture_blank(&next, cur->w / 2, cur->h / 2); + gfx_copy_stretch_with_alpha_map(&next, 0, 0, next.w, next.h, cur, 0, 0, cur->w, cur->h); + if (have_tmp) + gfx_delete_texture(&tmp); + tmp = next; + cur = &tmp; + have_tmp = true; + } + + Texture dst; + gfx_init_texture_blank(&dst, w, h); + gfx_copy_stretch_with_alpha_map(&dst, 0, 0, w, h, cur, 0, 0, cur->w, cur->h); + if (have_tmp) + gfx_delete_texture(&tmp); + + char *path = savedir_path(filename->text); + int r = gfx_save_texture(&dst, path, ALCG_QNT); + free(path); + gfx_delete_texture(&dst); + return !!r; +} + void PE_SetInputState(int parts_no, int state) { if (!parts_state_valid(--state)) { @@ -2072,11 +2109,6 @@ static void ctrl_stack_init(void) PE_AddController(-1); } -static void ctrl_stack_fini(void) -{ - memset(&ctrl_stack, 0, sizeof(ctrl_stack)); -} - // Adds a new controller to the stack and makes it active. The `index` // parameter specifies the position in the stack at which to insert the new // controller; -1 means "insert directly after the currently active