// // #include "scripting/api/libs/graphics.h" #include "streaminganim.h" #include "freespace.h" namespace scripting { namespace api { bool streaminganim_h::isValid() const { return (ga.num_frames > 0); } streaminganim_h::streaminganim_h(const char* real_filename) { char filename[MAX_FILENAME_LEN]; // make sure no one passed an extension memset(filename, 0, MAX_FILENAME_LEN); strncpy(filename, real_filename, MAX_FILENAME_LEN - 1); char* p = strrchr(filename, '.'); if (p) { mprintf(("Someone passed an extension to streaminganim_h for file '%s'\n", real_filename)); *p = 0; } generic_anim_init(&ga, filename); } streaminganim_h::~streaminganim_h() { // don't bother to check if valid before unloading // generic_anim_unload has safety checks generic_anim_unload(&ga); } streaminganim_h::streaminganim_h(streaminganim_h&& other) noexcept : ga(other.ga) { // Reset the other instance so that we own the only instance generic_anim_init(&other.ga, nullptr); } streaminganim_h& streaminganim_h::operator=(streaminganim_h&& other) noexcept { if (this == &other) return *this; generic_anim_unload(&ga); ga = other.ga; // Reset the other instance so that we own the only instance generic_anim_init(&other.ga, nullptr); return *this; } //**********HANDLE: streamingAnimation ADE_OBJ(l_streaminganim, streaminganim_h, "streaminganim", "Streaming Animation handle"); ADE_VIRTVAR(Loop, l_streaminganim, "boolean", "Make the streaming animation loop.", "boolean", "Is the animation looping, or nil if anim invalid") { streaminganim_h* sah; bool loop = false; if(!ade_get_args(L, "o|b", l_streaminganim.GetPtr(&sah), &loop)) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; if (ADE_SETTING_VAR) { if (loop == false) sah->ga.direction |= GENERIC_ANIM_DIRECTION_NOLOOP; else sah->ga.direction &= ~GENERIC_ANIM_DIRECTION_NOLOOP; } return ((sah->ga.direction & GENERIC_ANIM_DIRECTION_NOLOOP) == false ? ADE_RETURN_TRUE : ADE_RETURN_FALSE); } ADE_VIRTVAR(Pause, l_streaminganim, "boolean", "Pause the streaming animation.", "boolean", "Is the animation paused, or nil if anim invalid") { streaminganim_h* sah; bool pause = false; if(!ade_get_args(L, "o|b", l_streaminganim.GetPtr(&sah), &pause)) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; if (ADE_SETTING_VAR) { if (pause) sah->ga.direction |= GENERIC_ANIM_DIRECTION_PAUSED; else sah->ga.direction &= ~GENERIC_ANIM_DIRECTION_PAUSED; } return ((sah->ga.direction & GENERIC_ANIM_DIRECTION_PAUSED) ? ADE_RETURN_TRUE : ADE_RETURN_FALSE); } ADE_VIRTVAR(Reverse, l_streaminganim, "boolean", "Make the streaming animation play in reverse.", "boolean", "Is the animation playing in reverse, or nil if anim invalid") { streaminganim_h* sah; bool reverse = false; if(!ade_get_args(L, "o|b", l_streaminganim.GetPtr(&sah), &reverse)) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; if (ADE_SETTING_VAR) { if (reverse) sah->ga.direction |= GENERIC_ANIM_DIRECTION_BACKWARDS; else sah->ga.direction &= ~GENERIC_ANIM_DIRECTION_BACKWARDS; } return ((sah->ga.direction & GENERIC_ANIM_DIRECTION_BACKWARDS) ? ADE_RETURN_TRUE : ADE_RETURN_FALSE); } ADE_VIRTVAR(Grayscale, l_streaminganim, "boolean", "Whether the streaming animation is drawn as grayscale multiplied by the current color (the HUD method).", "boolean", "Boolean flag") { streaminganim_h* sah; bool grayscale = false; if (!ade_get_args(L, "o|b", l_streaminganim.GetPtr(&sah), &grayscale)) return ADE_RETURN_NIL; if (!sah->isValid()) return ADE_RETURN_NIL; if (ADE_SETTING_VAR) { LuaError(L, "This variable can only be set before the anim is loaded."); } return sah->ga.use_hud_color ? ADE_RETURN_TRUE : ADE_RETURN_FALSE; } ADE_FUNC(getFilename, l_streaminganim, NULL, "Get the filename of the animation", "string", "Filename or nil if invalid") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; return ade_set_args(L, "s", sah->ga.filename); } ADE_FUNC(getFrameCount, l_streaminganim, nullptr, "Get the number of frames in the animation.", "number", "Total number of frames") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; return ade_set_args(L, "i", sah->ga.num_frames); } ADE_FUNC(getFrameIndex, l_streaminganim, nullptr, "Get the current frame index of the animation", "number", "Current frame index") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; int cframe = sah->ga.current_frame; if (cframe < 0 || cframe >= sah->ga.num_frames) { return ADE_RETURN_NIL; } return ade_set_args(L, "i", ++cframe); // C++ to LUA array index } ADE_FUNC(getHeight, l_streaminganim, nullptr, "Get the height of the animation in pixels", "number", "Height or nil if invalid") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; return ade_set_args(L, "i", sah->ga.height); } ADE_FUNC(getWidth, l_streaminganim, nullptr, "Get the width of the animation in pixels", "number", "Width or nil if invalid") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; return ade_set_args(L, "i", sah->ga.width); } ADE_FUNC(preload, l_streaminganim, NULL, "Load all apng animations into memory, enabling apng frame cache if not already enabled", "boolean", "true if preload was successful, nil if a syntax/type error occurs") { streaminganim_h* sah; if(!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if (sah->ga.type != BM_TYPE_PNG) return ADE_RETURN_NIL; sah->ga.png.anim->preload(); return ADE_RETURN_TRUE; } ADE_FUNC(process, l_streaminganim, "[number x1, number y1, number x2, number y2, number u0, number v0, number u1, number v1, number alpha, boolean draw]", "Processes a streaming animation, including selecting the correct frame & drawing it.", "boolean", "True if processing was successful, otherwise nil") { streaminganim_h* sah; generic_extras ge; int x1 = 0, y1 = 0; int x2 = INT_MAX, y2 = INT_MAX; if(!ade_get_args(L, "o|iiiifffffb", l_streaminganim.GetPtr(&sah), &x1, &y1, &x2, &y2, &ge.u0, &ge.v0, &ge.u1, &ge.v1, &ge.alpha, &ge.draw)) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; if (sah->ga.use_hud_color) { float scale_x = (x2 != INT_MAX) ? i2fl(x2 - x1) / i2fl(sah->ga.width) : 1.0f; float scale_y = (y2 != INT_MAX) ? i2fl(y2 - y1) / i2fl(sah->ga.height) : 1.0f; int base_w = gr_screen.max_w; int base_h = gr_screen.max_h; gr_set_screen_scale(fl2ir(base_w / scale_x), fl2ir(base_h / scale_y)); // generic_anim_render can't use generic_extras, says the function // but we can at least do scaling, as above generic_anim_render(&sah->ga, flFrametime, fl2ir(x1 / scale_x), fl2ir(y1 / scale_y), false); gr_reset_screen_scale(); } else { ge.width = sah->ga.width; ge.height = sah->ga.height; if (x2 != INT_MAX) ge.width = x2 - x1; if (y2 != INT_MAX) ge.height = y2 - y1; // use the currently active resize mode for scripting // (generic_anim_render will use this field when ge is provided) ge.resize_mode = get_resize_mode(); generic_anim_render(&sah->ga, flFrametime, x1, y1, false, &ge); } return ADE_RETURN_TRUE; } ADE_FUNC(reset, l_streaminganim, nullptr, "Reset a streaming animation back to its 1st frame", "boolean", "True if successful, otherwise nil") { streaminganim_h* sah; if(!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; sah->ga.png.anim->goto_start(); sah->ga.current_frame = 0; sah->ga.anim_time = 0.0f; sah->ga.png.previous_frame_time = 0.0f; return ADE_RETURN_TRUE; } ADE_FUNC(timeLeft, l_streaminganim, nullptr, "Get the amount of time left in the animation, in seconds", "number", "Time left in secs or nil if invalid") { streaminganim_h* sah; if (!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; if(!sah->isValid()) return ADE_RETURN_NIL; float timeLeft = 0.0f; if ((sah->ga.direction & GENERIC_ANIM_DIRECTION_BACKWARDS)) timeLeft = sah->ga.anim_time; else timeLeft = sah->ga.total_time - sah->ga.anim_time; // there was an anim that completed with less than 1/1000 seconds left; // let's round under 1/200 seconds because nobody plays at 200 FPS if (timeLeft < 0.005) timeLeft = 0.0f; return ade_set_args(L, "f", timeLeft); } ADE_FUNC(unload, l_streaminganim, NULL, "Unloads a streaming animation from memory", NULL, NULL) { streaminganim_h* sah; if(!ade_get_args(L, "o", l_streaminganim.GetPtr(&sah))) return ADE_RETURN_NIL; // don't bother to check if valid before unloading // generic_anim_unload has safety checks generic_anim_unload(&sah->ga); return ADE_RETURN_TRUE; } } }