/** * @file */ /* Copyright (C) 2002-2013 UFO: Alien Invasion. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ui_main.h" #include "ui_internal.h" #include "ui_parse.h" #include "ui_sprite.h" #include "ui_render.h" const value_t ui_spriteProperties[] = { {"size", V_POS, offsetof(uiSprite_t, size), MEMBER_SIZEOF(uiSprite_t, size)}, {"single", V_BOOL, offsetof(uiSprite_t, single), 0}, {"blend", V_BOOL, offsetof(uiSprite_t, blend), 0}, {"pack64", V_BOOL, offsetof(uiSprite_t, pack64), 0}, {"tiled_17_1_3", V_BOOL, offsetof(uiSprite_t, tiled_17_1_3), 0}, {"tiled_25_1_3", V_BOOL, offsetof(uiSprite_t, tiled_25_1_3), 0}, {"tiled_popup", V_BOOL, offsetof(uiSprite_t, tiled_popup), 0}, {"border", V_INT, offsetof(uiSprite_t, border), MEMBER_SIZEOF(uiSprite_t, border)}, {"texl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_NORMAL]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_NORMAL])}, {"hoveredtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_HOVER]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_HOVER])}, {"disabledtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_DISABLED]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_DISABLED])}, {"clickedtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_CLICKED]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_CLICKED])}, {"image", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_NORMAL]), 0}, {"hoveredimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_HOVER]), 0}, {"disabledimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_DISABLED]), 0}, {"clickedimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_CLICKED]), 0}, {"color", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_NORMAL]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_NORMAL])}, {"hoveredcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_HOVER]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_HOVER])}, {"disabledcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_DISABLED]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_DISABLED])}, {"clickedcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_CLICKED]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_CLICKED])}, {nullptr, V_NULL, 0, 0} }; /** * @brief Search a file name inside pics/ according to the sprite name * If it exists, generate a "single" sprite using the size of the image * @param name Name of the sprite * @return A sprite, else nullptr */ static uiSprite_t* UI_AutoGenerateSprite (const char* name) { uiSprite_t* sprite = nullptr; const char* suffix[SPRITE_STATUS_MAX] = {"", "_hovered", "_disabled", "_clicked"}; char basePicNameBuf[MAX_QPATH]; const image_t* pic; int i; Q_strncpyz(basePicNameBuf, name, sizeof(basePicNameBuf)); pic = UI_LoadImage(basePicNameBuf); if (pic == nullptr) return nullptr; sprite = UI_AllocStaticSprite(basePicNameBuf); sprite->image[SPRITE_STATUS_NORMAL] = UI_AllocStaticString(basePicNameBuf, 0); sprite->size[0] = pic->width; sprite->size[1] = pic->height; for (i = 1; i < SPRITE_STATUS_MAX; i++) { char picNameBuf[MAX_QPATH]; Com_sprintf(picNameBuf, sizeof(picNameBuf), "%s%s", basePicNameBuf, suffix[i]); pic = UI_LoadImage(picNameBuf); if (pic != nullptr) sprite->image[i] = UI_AllocStaticString(picNameBuf, 0); } return sprite; } /** * @brief Check if an sprite name exists * @param[in] name Name of the sprite * @return True if the sprite exists * @note not very fast; if we use it often we should improve the search */ static bool UI_SpriteExists (const char* name) { int i; for (i = 0; i < ui_global.numSprites; i++) { if (strncmp(name, ui_global.sprites[i].name, MEMBER_SIZEOF(uiSprite_t, name)) != 0) continue; return true; } return false; } /** * @brief Return an sprite by is name * @param[in] name Name of the sprite * @return The requested sprite, else nullptr * @note not very fast; if we use it often we should improve the search */ uiSprite_t* UI_GetSpriteByName (const char* name) { int i; for (i = 0; i < ui_global.numSprites; i++) { if (Q_streq(name, ui_global.sprites[i].name)) return &ui_global.sprites[i]; } return UI_AutoGenerateSprite(name); } /** * @brief Allocate an sprite to the UI static memory * @note Its not a dynamic memory allocation. Please only use it at the loading time * @param[in] name Name of the sprite * @todo Assert out when we are not in parsing/loading stage */ uiSprite_t* UI_AllocStaticSprite (const char* name) { uiSprite_t* result; if (UI_SpriteExists(name)) Com_Error(ERR_FATAL, "UI_AllocStaticSprite: \"%s\" sprite already allocated. Check your scripts.", name); if (ui_global.numSprites >= UI_MAX_SPRITES) Com_Error(ERR_FATAL, "UI_AllocStaticSprite: UI_MAX_SPRITES hit"); result = &ui_global.sprites[ui_global.numSprites]; ui_global.numSprites++; OBJZERO(*result); Q_strncpyz(result->name, name, sizeof(result->name)); return result; } /** * Template to draw a tiled texture with * corner size of 17 pixels, middle size of 1 pixel, * margin between tiles of 3 pixels */ static const int tile_template_17_1_3[] = { 17, 1, 17, 17, 1, 17, 3 }; /** * Template to draw a tiled texture with * corner size of 17 pixels, middle size of 1 pixel, * margin between tiles of 3 pixels */ static const int tile_template_25_1_3[] = { 25, 1, 25, 25, 1, 25, 3 }; /** * Template to draw a tiled texture with * used for popup windows */ static const int tile_template_popup[] = { 20, 1, 19, 46, 1, 19, 3 }; /** * @param[in] status The state of the sprite node * @param[in] sprite Context sprite * @param[in] posX Absolute X position of the top-left corner * @param[in] posY Absolute Y position of the top-left corner * @param[in] sizeX Width of the bounded box * @param[in] sizeY Height of the bounded box * @todo use named const for status */ void UI_DrawSpriteInBox (bool flip, const uiSprite_t* sprite, uiSpriteStatus_t status, int posX, int posY, int sizeX, int sizeY) { float texX; float texY; const char* image; /** @todo Add warning */ if (status >= SPRITE_STATUS_MAX) return; /** @todo merge all this cases */ if (sprite->single || sprite->blend) { texX = sprite->pos[SPRITE_STATUS_NORMAL][0]; texY = sprite->pos[SPRITE_STATUS_NORMAL][1]; image = sprite->image[SPRITE_STATUS_NORMAL]; } else if (sprite->pack64) { texX = sprite->pos[SPRITE_STATUS_NORMAL][0]; texY = sprite->pos[SPRITE_STATUS_NORMAL][1] + (64 * status); image = sprite->image[SPRITE_STATUS_NORMAL]; } else { texX = sprite->pos[status][0]; texY = sprite->pos[status][1]; image = sprite->image[status]; if (!image) { if (texX == 0 && texY == 0) { texX = sprite->pos[SPRITE_STATUS_NORMAL][0]; texY = sprite->pos[SPRITE_STATUS_NORMAL][1]; image = sprite->image[SPRITE_STATUS_NORMAL]; } else { image = sprite->image[SPRITE_STATUS_NORMAL]; } } } if (!image) return; if (sprite->blend) { const vec_t* color = sprite->color[status]; R_Color(color); } if (sprite->tiled_17_1_3) { const vec2_t pos = Vector2FromInt(posX, posY); const vec2_t size = Vector2FromInt(sizeX, sizeY); UI_DrawPanel(pos, size, image, texX, texY, tile_template_17_1_3); } else if (sprite->tiled_25_1_3) { const vec2_t pos = Vector2FromInt(posX, posY); const vec2_t size = Vector2FromInt(sizeX, sizeY); UI_DrawPanel(pos, size, image, texX, texY, tile_template_25_1_3); } else if (sprite->tiled_popup) { const vec2_t pos = Vector2FromInt(posX, posY); const vec2_t size = Vector2FromInt(sizeX, sizeY); UI_DrawPanel(pos, size, image, texX, texY, tile_template_popup); } else if (sprite->border != 0) { const vec2_t pos = Vector2FromInt(posX, posY); const vec2_t size = Vector2FromInt(sizeX, sizeY); UI_DrawBorderedPanel(pos, size, image, texX, texY, sprite->size[0], sprite->size[1], sprite->border); } else { posX += (sizeX - sprite->size[0]) / 2; posY += (sizeY - sprite->size[1]) / 2; UI_DrawNormImageByName(flip, posX, posY, sprite->size[0], sprite->size[1], texX + sprite->size[0], texY + sprite->size[1], texX, texY, image); } if (sprite->blend) R_Color(nullptr); }