2012-06-29 16:44:26 +02:00
//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
2020-07-30 11:42:28 -03:00
// Remere's Map Editor is free software: you can redistribute it and/or modify
2012-06-29 16:44:26 +02:00
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2016-09-29 19:24:45 -03:00
//
2020-07-30 11:42:28 -03:00
// Remere's Map Editor is distributed in the hope that it will be useful,
2012-06-29 16:44:26 +02:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2020-07-30 11:42:28 -03:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2012-06-29 16:44:26 +02:00
// GNU General Public License for more details.
2016-09-29 19:24:45 -03:00
//
2012-06-29 16:44:26 +02:00
// You should have received a copy of the GNU General Public License
2020-07-30 11:42:28 -03:00
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2012-06-29 16:44:26 +02:00
//////////////////////////////////////////////////////////////////////
# include "main.h"
# include "sprites.h"
# include "graphics.h"
2023-10-09 23:02:37 -03:00
# include "artprovider.h"
2012-06-29 16:44:26 +02:00
# include "filehandle.h"
# include "settings.h"
# include "gui.h"
2015-02-17 10:41:13 -03:00
# include "otml.h"
2023-10-09 23:02:37 -03:00
# include <wx/rawbmp.h>
2012-06-29 16:44:26 +02:00
# include "pngfiles.h"
// All 133 template colors
static uint32_t TemplateOutfitLookupTable [ ] = {
2023-10-09 19:12:16 -07:00
0xFFFFFF ,
0xFFD4BF ,
0xFFE9BF ,
0xFFFFBF ,
0xE9FFBF ,
0xD4FFBF ,
0xBFFFBF ,
0xBFFFD4 ,
0xBFFFE9 ,
0xBFFFFF ,
0xBFE9FF ,
0xBFD4FF ,
0xBFBFFF ,
0xD4BFFF ,
0xE9BFFF ,
0xFFBFFF ,
0xFFBFE9 ,
0xFFBFD4 ,
0xFFBFBF ,
0xDADADA ,
0xBF9F8F ,
0xBFAF8F ,
0xBFBF8F ,
0xAFBF8F ,
0x9FBF8F ,
0x8FBF8F ,
0x8FBF9F ,
0x8FBFAF ,
0x8FBFBF ,
0x8FAFBF ,
0x8F9FBF ,
0x8F8FBF ,
0x9F8FBF ,
0xAF8FBF ,
0xBF8FBF ,
0xBF8FAF ,
0xBF8F9F ,
0xBF8F8F ,
0xB6B6B6 ,
0xBF7F5F ,
0xBFAF8F ,
0xBFBF5F ,
0x9FBF5F ,
0x7FBF5F ,
0x5FBF5F ,
0x5FBF7F ,
0x5FBF9F ,
0x5FBFBF ,
0x5F9FBF ,
0x5F7FBF ,
0x5F5FBF ,
0x7F5FBF ,
0x9F5FBF ,
0xBF5FBF ,
0xBF5F9F ,
0xBF5F7F ,
0xBF5F5F ,
0x919191 ,
0xBF6A3F ,
0xBF943F ,
0xBFBF3F ,
0x94BF3F ,
0x6ABF3F ,
0x3FBF3F ,
0x3FBF6A ,
0x3FBF94 ,
0x3FBFBF ,
0x3F94BF ,
0x3F6ABF ,
0x3F3FBF ,
0x6A3FBF ,
0x943FBF ,
0xBF3FBF ,
0xBF3F94 ,
0xBF3F6A ,
0xBF3F3F ,
0x6D6D6D ,
0xFF5500 ,
0xFFAA00 ,
0xFFFF00 ,
0xAAFF00 ,
0x54FF00 ,
0x00FF00 ,
0x00FF54 ,
0x00FFAA ,
0x00FFFF ,
0x00A9FF ,
0x0055FF ,
0x0000FF ,
0x5500FF ,
0xA900FF ,
0xFE00FF ,
0xFF00AA ,
0xFF0055 ,
0xFF0000 ,
0x484848 ,
0xBF3F00 ,
0xBF7F00 ,
0xBFBF00 ,
0x7FBF00 ,
0x3FBF00 ,
0x00BF00 ,
0x00BF3F ,
0x00BF7F ,
0x00BFBF ,
0x007FBF ,
0x003FBF ,
0x0000BF ,
0x3F00BF ,
0x7F00BF ,
0xBF00BF ,
0xBF007F ,
0xBF003F ,
0xBF0000 ,
0x242424 ,
0x7F2A00 ,
0x7F5500 ,
0x7F7F00 ,
0x557F00 ,
0x2A7F00 ,
0x007F00 ,
0x007F2A ,
0x007F55 ,
0x007F7F ,
0x00547F ,
0x002A7F ,
0x00007F ,
0x2A007F ,
0x54007F ,
0x7F007F ,
0x7F0055 ,
0x7F002A ,
2012-06-29 16:44:26 +02:00
0x7F0000 ,
} ;
GraphicManager : : GraphicManager ( ) :
2014-01-22 20:16:17 +01:00
client_version ( nullptr ) ,
2012-06-29 16:44:26 +02:00
unloaded ( true ) ,
2015-02-05 21:07:44 -03:00
dat_format ( DAT_FORMAT_UNKNOWN ) ,
2015-02-17 10:41:13 -03:00
otfi_found ( false ) ,
2015-02-05 21:07:44 -03:00
is_extended ( false ) ,
2015-02-17 10:41:13 -03:00
has_transparency ( false ) ,
has_frame_durations ( false ) ,
has_frame_groups ( false ) ,
2012-06-29 16:44:26 +02:00
loaded_textures ( 0 ) ,
2023-10-09 19:12:16 -07:00
lastclean ( 0 ) {
2023-10-15 18:51:57 -03:00
animation_timer = newd < wxStopWatch > ( ) ;
2016-11-09 12:17:17 -03:00
animation_timer - > Start ( ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GraphicManager : : ~ GraphicManager ( ) {
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
delete iter - > second ;
}
2023-10-09 19:12:16 -07:00
for ( ImageMap : : iterator iter = image_space . begin ( ) ; iter ! = image_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
delete iter - > second ;
}
2016-11-09 12:17:17 -03:00
2023-10-09 23:02:37 -03:00
sprite_space . clear ( ) ;
image_space . clear ( ) ;
2016-11-09 12:17:17 -03:00
delete animation_timer ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : hasTransparency ( ) const {
2015-02-17 10:41:13 -03:00
return has_transparency ;
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : isUnloaded ( ) const {
2012-06-29 16:44:26 +02:00
return unloaded ;
}
2023-10-09 19:12:16 -07:00
GLuint GraphicManager : : getFreeTextureID ( ) {
2012-06-29 16:44:26 +02:00
static GLuint id_counter = 0x10000000 ;
return id_counter + + ; // This should (hopefully) never run out
}
2023-10-09 19:12:16 -07:00
void GraphicManager : : clear ( ) {
2012-06-29 16:44:26 +02:00
SpriteMap new_sprite_space ;
2023-10-09 19:12:16 -07:00
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
if ( iter - > first > = 0 ) { // Don't clean internal sprites
2012-06-29 16:44:26 +02:00
delete iter - > second ;
} else {
new_sprite_space . insert ( std : : make_pair ( iter - > first , iter - > second ) ) ;
}
}
2023-10-09 19:12:16 -07:00
for ( ImageMap : : iterator iter = image_space . begin ( ) ; iter ! = image_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
delete iter - > second ;
}
sprite_space . swap ( new_sprite_space ) ;
image_space . clear ( ) ;
cleanup_list . clear ( ) ;
item_count = 0 ;
creature_count = 0 ;
loaded_textures = 0 ;
2014-01-22 20:16:17 +01:00
lastclean = time ( nullptr ) ;
2012-06-29 16:44:26 +02:00
spritefile = " " ;
unloaded = true ;
}
2023-10-09 19:12:16 -07:00
void GraphicManager : : cleanSoftwareSprites ( ) {
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
if ( iter - > first > = 0 ) { // Don't clean internal sprites
2012-06-29 16:44:26 +02:00
iter - > second - > unloadDC ( ) ;
}
}
}
2023-10-09 19:12:16 -07:00
Sprite * GraphicManager : : getSprite ( int id ) {
2012-06-29 16:44:26 +02:00
SpriteMap : : iterator it = sprite_space . find ( id ) ;
2023-10-09 19:12:16 -07:00
if ( it ! = sprite_space . end ( ) ) {
2012-06-29 16:44:26 +02:00
return it - > second ;
}
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GameSprite * GraphicManager : : getCreatureSprite ( int id ) {
if ( id < 0 ) {
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
SpriteMap : : iterator it = sprite_space . find ( id + item_count ) ;
if ( it ! = sprite_space . end ( ) ) {
2012-06-29 16:44:26 +02:00
return static_cast < GameSprite * > ( it - > second ) ;
}
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GameSprite * GraphicManager : : getEditorSprite ( int id ) {
if ( id > = 0 ) {
2023-10-09 23:02:37 -03:00
return nullptr ;
}
2012-06-29 16:44:26 +02:00
2023-10-09 23:02:37 -03:00
SpriteMap : : iterator it = sprite_space . find ( id ) ;
2023-10-09 19:12:16 -07:00
if ( it ! = sprite_space . end ( ) ) {
2023-10-09 23:02:37 -03:00
return dynamic_cast < GameSprite * > ( it - > second ) ;
}
return nullptr ;
2012-06-29 16:44:26 +02:00
}
# define loadPNGFile(name) _wxGetBitmapFromMemory(name, sizeof(name))
2023-10-15 18:51:57 -03:00
inline std : : shared_ptr < wxBitmap > _wxGetBitmapFromMemory ( const unsigned char * data , int length ) {
2012-06-29 16:44:26 +02:00
wxMemoryInputStream is ( data , length ) ;
2016-11-05 15:28:14 +01:00
wxImage img ( is , " image/png " ) ;
2023-10-09 19:12:16 -07:00
if ( ! img . IsOk ( ) ) {
return nullptr ;
}
2023-10-15 18:51:57 -03:00
return newd < wxBitmap > ( img , - 1 ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : loadEditorSprites ( ) {
2012-06-29 16:44:26 +02:00
// Unused graphics MIGHT be loaded here, but it's a neglectable loss
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_SELECTION_MARKER ] = newd < EditorSprite > (
newd < wxBitmap > ( selection_marker_xpm16x16 ) ,
newd < wxBitmap > ( selection_marker_xpm32x32 )
2023-10-09 19:12:16 -07:00
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_1x1 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_1_small_png ) ,
loadPNGFile ( circular_1_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_3x3 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_2_small_png ) ,
loadPNGFile ( circular_2_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_5x5 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_3_small_png ) ,
loadPNGFile ( circular_3_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_7x7 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_4_small_png ) ,
loadPNGFile ( circular_4_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_9x9 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_5_small_png ) ,
loadPNGFile ( circular_5_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_15x15 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_6_small_png ) ,
loadPNGFile ( circular_6_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_CD_19x19 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( circular_7_small_png ) ,
loadPNGFile ( circular_7_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_1x1 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_1_small_png ) ,
loadPNGFile ( rectangular_1_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_3x3 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_2_small_png ) ,
loadPNGFile ( rectangular_2_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_5x5 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_3_small_png ) ,
loadPNGFile ( rectangular_3_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_7x7 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_4_small_png ) ,
loadPNGFile ( rectangular_4_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_9x9 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_5_small_png ) ,
loadPNGFile ( rectangular_5_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_15x15 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_6_small_png ) ,
loadPNGFile ( rectangular_6_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_BRUSH_SD_19x19 ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( rectangular_7_small_png ) ,
loadPNGFile ( rectangular_7_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_OPTIONAL_BORDER_TOOL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( optional_border_small_png ) ,
loadPNGFile ( optional_border_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_ERASER ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( eraser_small_png ) ,
loadPNGFile ( eraser_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_PZ_TOOL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( protection_zone_small_png ) ,
loadPNGFile ( protection_zone_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_PVPZ_TOOL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( pvp_zone_small_png ) ,
loadPNGFile ( pvp_zone_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_NOLOG_TOOL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( no_logout_small_png ) ,
loadPNGFile ( no_logout_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_NOPVP_TOOL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( no_pvp_small_png ) ,
loadPNGFile ( no_pvp_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_DOOR_NORMAL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( door_normal_small_png ) ,
loadPNGFile ( door_normal_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_DOOR_LOCKED ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( door_locked_small_png ) ,
loadPNGFile ( door_locked_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_DOOR_MAGIC ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( door_magic_small_png ) ,
loadPNGFile ( door_magic_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_DOOR_QUEST ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( door_quest_small_png ) ,
loadPNGFile ( door_quest_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_WINDOW_NORMAL ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( window_normal_small_png ) ,
loadPNGFile ( window_normal_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_WINDOW_HATCH ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( window_hatch_small_png ) ,
loadPNGFile ( window_hatch_png )
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_SELECTION_GEM ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( gem_edit_png ) ,
nullptr
) ;
2023-10-15 18:51:57 -03:00
sprite_space [ EDITOR_SPRITE_DRAWING_GEM ] = newd < EditorSprite > (
2023-10-09 19:12:16 -07:00
loadPNGFile ( gem_move_png ) ,
nullptr
) ;
2012-06-29 16:44:26 +02:00
2023-10-09 23:02:37 -03:00
sprite_space [ EDITOR_SPRITE_SPAWNS ] = GameSprite : : createFromBitmap ( ART_SPAWNS ) ;
sprite_space [ EDITOR_SPRITE_HOUSE_EXIT ] = GameSprite : : createFromBitmap ( ART_HOUSE_EXIT ) ;
sprite_space [ EDITOR_SPRITE_PICKUPABLE_ITEM ] = GameSprite : : createFromBitmap ( ART_PICKUPABLE ) ;
sprite_space [ EDITOR_SPRITE_MOVEABLE_ITEM ] = GameSprite : : createFromBitmap ( ART_MOVEABLE ) ;
sprite_space [ EDITOR_SPRITE_PICKUPABLE_MOVEABLE_ITEM ] = GameSprite : : createFromBitmap ( ART_PICKUPABLE_MOVEABLE ) ;
2012-06-29 16:44:26 +02:00
return true ;
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : loadOTFI ( const FileName & filename , wxString & error , wxArrayString & warnings ) {
2019-07-23 10:21:07 -03:00
wxDir dir ( filename . GetFullPath ( ) ) ;
wxString otfi_file ;
2015-02-17 10:41:13 -03:00
2019-07-23 10:21:07 -03:00
otfi_found = false ;
2023-10-09 19:12:16 -07:00
if ( dir . GetFirst ( & otfi_file , " *.otfi " , wxDIR_FILES ) ) {
2019-07-23 10:21:07 -03:00
wxFileName otfi ( filename . GetFullPath ( ) , otfi_file ) ;
OTMLDocumentPtr doc = OTMLDocument : : parse ( otfi . GetFullPath ( ) . ToStdString ( ) ) ;
2023-10-09 19:12:16 -07:00
if ( doc - > size ( ) = = 0 | | ! doc - > hasChildAt ( " DatSpr " ) ) {
2016-11-05 15:28:14 +01:00
error + = " 'DatSpr' tag not found " ;
2015-02-17 10:41:13 -03:00
return false ;
}
OTMLNodePtr node = doc - > get ( " DatSpr " ) ;
is_extended = node - > valueAt < bool > ( " extended " ) ;
has_transparency = node - > valueAt < bool > ( " transparency " ) ;
has_frame_durations = node - > valueAt < bool > ( " frame-durations " ) ;
has_frame_groups = node - > valueAt < bool > ( " frame-groups " ) ;
2019-07-23 10:21:07 -03:00
std : : string metadata = node - > valueAt < std : : string > ( " metadata-file " , std : : string ( ASSETS_NAME ) + " .dat " ) ;
std : : string sprites = node - > valueAt < std : : string > ( " sprites-file " , std : : string ( ASSETS_NAME ) + " .spr " ) ;
metadata_file = wxFileName ( filename . GetFullPath ( ) , wxString ( metadata ) ) ;
sprites_file = wxFileName ( filename . GetFullPath ( ) , wxString ( sprites ) ) ;
otfi_found = true ;
}
2020-07-30 11:42:28 -03:00
2023-10-09 19:12:16 -07:00
if ( ! otfi_found ) {
2015-02-17 10:41:13 -03:00
is_extended = false ;
has_transparency = false ;
has_frame_durations = false ;
has_frame_groups = false ;
2019-07-23 10:21:07 -03:00
metadata_file = wxFileName ( filename . GetFullPath ( ) , wxString ( ASSETS_NAME ) + " .dat " ) ;
sprites_file = wxFileName ( filename . GetFullPath ( ) , wxString ( ASSETS_NAME ) + " .spr " ) ;
2015-02-17 10:41:13 -03:00
}
2019-07-23 10:21:07 -03:00
2015-02-17 10:41:13 -03:00
return true ;
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : loadSpriteMetadata ( const FileName & datafile , wxString & error , wxArrayString & warnings ) {
2012-06-29 16:44:26 +02:00
// items.otb has most of the info we need. This only loads the GameSprite metadata
FileReadHandle file ( nstr ( datafile . GetFullPath ( ) ) ) ;
2023-10-09 19:12:16 -07:00
if ( ! file . isOk ( ) ) {
2016-11-05 15:28:14 +01:00
error + = " Failed to open " + datafile . GetFullPath ( ) + " for reading \n The error reported was: " + wxstr ( file . getErrorMessage ( ) ) ;
2012-06-29 16:44:26 +02:00
return false ;
}
uint16_t effect_count , distance_count ;
2012-07-01 16:35:50 +02:00
uint32_t datSignature ;
file . getU32 ( datSignature ) ;
2023-10-09 19:12:16 -07:00
// get max id
2012-06-29 16:44:26 +02:00
file . getU16 ( item_count ) ;
file . getU16 ( creature_count ) ;
file . getU16 ( effect_count ) ;
file . getU16 ( distance_count ) ;
2018-10-14 19:54:08 -03:00
uint32_t minID = 100 ; // items start with id 100
2012-06-29 16:44:26 +02:00
// We don't load distance/effects, if we would, just add effect_count & distance_count here
2017-05-19 15:19:10 -03:00
uint32_t maxID = item_count + creature_count ;
2012-06-29 16:44:26 +02:00
2015-02-05 21:07:44 -03:00
dat_format = client_version - > getDatFormatForSignature ( datSignature ) ;
2015-02-17 10:41:13 -03:00
2023-10-09 19:12:16 -07:00
if ( dat_format = = DAT_FORMAT_UNKNOWN ) {
2022-03-03 13:41:11 -08:00
error + = " Failed to open " + datafile . GetFullPath ( ) + " for reading \n Could not locate datSignature (0x " < < wxString : : Format ( wxT ( " %02x " ) , datSignature ) < < " ) compatible with client version " < < client_version - > getName ( ) ;
return false ;
}
2023-10-09 19:12:16 -07:00
if ( ! otfi_found ) {
2021-06-23 21:30:37 -03:00
is_extended = dat_format > = DAT_FORMAT_11 ;
has_frame_durations = dat_format > = DAT_FORMAT_11 ;
has_frame_groups = dat_format > = DAT_FORMAT_11 ;
2015-02-17 10:41:13 -03:00
}
2015-02-05 21:07:44 -03:00
2017-05-19 15:19:10 -03:00
uint16_t id = minID ;
2012-06-29 16:44:26 +02:00
// loop through all ItemDatabase until we reach the end of file
2023-10-09 19:12:16 -07:00
while ( id < = maxID ) {
2023-10-15 21:52:40 +00:00
const auto & sType = newd < GameSprite > ( ) ;
2012-06-29 16:44:26 +02:00
sprite_space [ id ] = sType ;
sType - > id = id ;
2012-07-01 15:55:44 +02:00
// Load the sprite flags
2023-10-09 19:12:16 -07:00
if ( ! loadSpriteMetadataFlags ( file , sType , error , warnings ) ) {
2012-07-01 15:55:44 +02:00
wxString msg ;
2016-11-05 15:28:14 +01:00
msg < < " Failed to load flags for sprite " < < sType - > id ;
2012-07-01 15:55:44 +02:00
warnings . push_back ( msg ) ;
}
2012-06-29 16:44:26 +02:00
2015-02-01 13:42:24 -03:00
// Reads the group count
2015-02-17 10:41:13 -03:00
uint8_t group_count = 1 ;
2023-10-09 19:12:16 -07:00
if ( has_frame_groups & & id > item_count ) {
2015-02-03 22:59:14 -03:00
file . getU8 ( group_count ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
for ( uint32_t k = 0 ; k < group_count ; + + k ) {
2015-02-01 13:42:24 -03:00
// Skipping the group type
2023-10-09 19:12:16 -07:00
if ( has_frame_groups & & id > item_count ) {
2015-02-01 13:42:24 -03:00
file . skip ( 1 ) ;
}
// Size and GameSprite data
file . getByte ( sType - > width ) ;
file . getByte ( sType - > height ) ;
// Skipping the exact size
2023-10-09 19:12:16 -07:00
if ( ( sType - > width > 1 ) | | ( sType - > height > 1 ) ) {
2015-02-01 13:42:24 -03:00
file . skip ( 1 ) ;
2012-07-01 16:49:44 +02:00
}
2015-02-01 13:42:24 -03:00
2015-02-03 22:59:14 -03:00
file . getU8 ( sType - > layers ) ; // Number of blendframes (some sprites consist of several merged sprites)
file . getU8 ( sType - > pattern_x ) ;
file . getU8 ( sType - > pattern_y ) ;
2021-06-23 21:30:37 -03:00
file . getU8 ( sType - > pattern_z ) ;
2015-02-03 22:59:14 -03:00
file . getU8 ( sType - > frames ) ; // Length of animation
2020-07-30 11:42:28 -03:00
2023-10-09 19:12:16 -07:00
if ( sType - > frames > 1 ) {
2016-11-09 12:17:17 -03:00
uint8_t async = 0 ;
int loop_count = 0 ;
int8_t start_frame = 0 ;
2023-10-09 19:12:16 -07:00
if ( has_frame_durations ) {
2016-11-09 12:17:17 -03:00
file . getByte ( async ) ;
file . get32 ( loop_count ) ;
file . getSByte ( start_frame ) ;
}
2023-10-15 18:51:57 -03:00
sType - > animator = newd < Animator > ( sType - > frames , start_frame , loop_count , async = = 1 ) ;
2023-10-09 19:12:16 -07:00
if ( has_frame_durations ) {
for ( int i = 0 ; i < sType - > frames ; i + + ) {
2016-11-09 12:17:17 -03:00
uint32_t min ;
uint32_t max ;
file . getU32 ( min ) ;
file . getU32 ( max ) ;
FrameDuration * frame_duration = sType - > animator - > getFrameDuration ( i ) ;
frame_duration - > setValues ( int ( min ) , int ( max ) ) ;
}
sType - > animator - > reset ( ) ;
}
2012-07-01 16:35:50 +02:00
}
2015-02-01 13:42:24 -03:00
2023-10-09 19:12:16 -07:00
sType - > numsprites = ( int ) sType - > width * ( int ) sType - > height * ( int ) sType - > layers * ( int ) sType - > pattern_x * ( int ) sType - > pattern_y * sType - > pattern_z * ( int ) sType - > frames ;
2015-02-01 13:42:24 -03:00
// Read the sprite ids
2023-10-09 19:12:16 -07:00
for ( uint32_t i = 0 ; i < sType - > numsprites ; + + i ) {
2015-02-01 13:42:24 -03:00
uint32_t sprite_id ;
2023-10-09 19:12:16 -07:00
if ( is_extended ) {
2015-02-01 13:42:24 -03:00
file . getU32 ( sprite_id ) ;
2015-02-05 21:07:44 -03:00
} else {
2015-02-01 13:42:24 -03:00
uint16_t u16 = 0 ;
file . getU16 ( u16 ) ;
sprite_id = u16 ;
}
2023-10-09 19:12:16 -07:00
if ( image_space [ sprite_id ] = = nullptr ) {
2023-10-15 21:52:40 +00:00
const auto & img = newd < GameSprite : : NormalImage > ( ) ;
2015-02-01 13:42:24 -03:00
img - > id = sprite_id ;
image_space [ sprite_id ] = img ;
}
2023-10-15 18:51:57 -03:00
sType - > spriteList . push_back ( static_self_cast < GameSprite : : NormalImage > ( image_space [ sprite_id ] ) ) ;
2012-06-29 16:44:26 +02:00
}
}
+ + id ;
}
return true ;
}
2023-10-15 18:51:57 -03:00
bool GraphicManager : : loadSpriteMetadataFlags ( FileReadHandle & file , std : : shared_ptr < GameSprite > sType , wxString & error , wxArrayString & warnings ) {
2015-02-06 10:30:02 -03:00
uint8_t prev_flag = 0 ;
uint8_t flag = DatFlagLast ;
2012-07-01 15:55:44 +02:00
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < DatFlagLast ; + + i ) {
2015-02-06 10:30:02 -03:00
prev_flag = flag ;
file . getU8 ( flag ) ;
2012-07-01 15:55:44 +02:00
2023-10-09 19:12:16 -07:00
if ( flag = = DatFlagLast ) {
2015-02-06 10:30:02 -03:00
return true ;
}
2023-10-09 19:12:16 -07:00
if ( dat_format > = DAT_FORMAT_11 ) {
2015-02-06 10:30:02 -03:00
/* In 10.10+ all attributes from 16 and up were
2023-10-09 19:12:16 -07:00
* incremented by 1 to make space for 16 as
* " No Movement Animation " flag .
*/
if ( flag = = 16 ) {
2015-02-06 10:30:02 -03:00
flag = DatFlagNoMoveAnimation ;
2023-10-09 19:12:16 -07:00
} else if ( flag > 16 ) {
2015-02-06 10:30:02 -03:00
flag - = 1 ;
2023-10-09 19:12:16 -07:00
}
2015-02-06 10:30:02 -03:00
}
2012-07-01 15:55:44 +02:00
2015-12-06 11:42:37 -03:00
switch ( flag ) {
2015-02-06 10:30:02 -03:00
case DatFlagGroundBorder :
case DatFlagOnBottom :
case DatFlagOnTop :
case DatFlagContainer :
case DatFlagStackable :
case DatFlagForceUse :
case DatFlagMultiUse :
case DatFlagFluidContainer :
case DatFlagSplash :
case DatFlagNotWalkable :
case DatFlagNotMoveable :
case DatFlagBlockProjectile :
case DatFlagNotPathable :
case DatFlagPickupable :
case DatFlagHangable :
case DatFlagHookSouth :
case DatFlagHookEast :
case DatFlagRotateable :
case DatFlagDontHide :
case DatFlagTranslucent :
case DatFlagLyingCorpse :
case DatFlagAnimateAlways :
case DatFlagFullGround :
case DatFlagLook :
2016-06-10 14:40:02 -03:00
case DatFlagWrappable :
case DatFlagUnwrappable :
case DatFlagTopEffect :
2015-02-06 10:30:02 -03:00
case DatFlagFloorChange :
case DatFlagNoMoveAnimation :
case DatFlagChargeable :
break ;
case DatFlagWritable :
case DatFlagWritableOnce :
case DatFlagCloth :
case DatFlagLensHelp :
case DatFlagUsable :
file . skip ( 2 ) ;
break ;
2023-10-09 23:02:37 -03:00
case DatFlagGround :
uint16_t speed ;
file . getU16 ( speed ) ;
2023-10-09 19:12:16 -07:00
sType - > ground_speed = speed ;
2012-07-01 15:55:44 +02:00
break ;
2015-02-06 10:30:02 -03:00
2023-10-09 23:02:37 -03:00
case DatFlagLight : {
SpriteLight light ;
uint16_t intensity ;
uint16_t color ;
file . getU16 ( intensity ) ;
file . getU16 ( color ) ;
sType - > has_light = true ;
2023-10-09 19:12:16 -07:00
sType - > light = SpriteLight { static_cast < uint8_t > ( intensity ) , static_cast < uint8_t > ( color ) } ;
2023-10-09 23:02:37 -03:00
break ;
}
2015-12-06 11:42:37 -03:00
case DatFlagDisplacement : {
2023-10-09 19:12:16 -07:00
if ( dat_format > = DAT_FORMAT_11 ) {
2015-02-06 10:30:02 -03:00
uint16_t offset_x ;
uint16_t offset_y ;
file . getU16 ( offset_x ) ;
file . getU16 ( offset_y ) ;
2023-10-09 23:02:37 -03:00
sType - > draw_offset = wxPoint ( offset_x , offset_y ) ;
} else {
sType - > draw_offset = wxPoint ( 8 , 8 ) ;
2015-02-06 10:30:02 -03:00
}
2012-07-01 15:55:44 +02:00
break ;
}
2015-02-06 10:30:02 -03:00
2015-12-06 11:42:37 -03:00
case DatFlagElevation : {
2015-02-06 10:30:02 -03:00
uint16_t draw_height ;
file . getU16 ( draw_height ) ;
sType - > draw_height = draw_height ;
2012-07-01 15:55:44 +02:00
break ;
}
2015-02-06 10:30:02 -03:00
2015-12-06 11:42:37 -03:00
case DatFlagMinimapColor : {
2015-02-06 10:30:02 -03:00
uint16_t minimap_color ;
file . getU16 ( minimap_color ) ;
sType - > minimap_color = minimap_color ;
2012-07-01 15:55:44 +02:00
break ;
}
2015-02-06 10:30:02 -03:00
2015-12-06 11:42:37 -03:00
case DatFlagMarket : {
2015-02-06 10:30:02 -03:00
file . skip ( 6 ) ;
std : : string marketName ;
file . getString ( marketName ) ;
file . skip ( 4 ) ;
2012-07-01 15:55:44 +02:00
break ;
}
2015-02-06 10:30:02 -03:00
2015-12-06 11:42:37 -03:00
default : {
2012-07-01 15:55:44 +02:00
wxString err ;
2016-11-05 15:28:14 +01:00
err < < " Metadata: Unknown flag: " < < i2ws ( flag ) < < " . Previous flag: " < < i2ws ( prev_flag ) < < " . " ;
2012-07-01 15:55:44 +02:00
warnings . push_back ( err ) ;
break ;
2012-07-01 16:35:50 +02:00
}
2012-07-01 15:55:44 +02:00
}
2014-02-21 13:55:09 +01:00
}
2015-02-06 10:30:02 -03:00
return true ;
2014-02-21 13:55:09 +01:00
}
2023-10-09 19:12:16 -07:00
bool GraphicManager : : loadSpriteData ( const FileName & datafile , wxString & error , wxArrayString & warnings ) {
2012-06-29 16:44:26 +02:00
FileReadHandle fh ( nstr ( datafile . GetFullPath ( ) ) ) ;
2023-10-09 19:12:16 -07:00
if ( ! fh . isOk ( ) ) {
2016-11-05 15:28:14 +01:00
error = " Failed to open file for reading " ;
2012-06-29 16:44:26 +02:00
return false ;
}
2023-10-09 19:12:16 -07:00
# define safe_get(func, ...) \
do { \
if ( ! fh . get # # func ( __VA_ARGS__ ) ) { \
2012-06-29 16:44:26 +02:00
error = wxstr ( fh . getErrorMessage ( ) ) ; \
2023-10-09 19:12:16 -07:00
return false ; \
} \
} while ( false )
2016-09-29 19:24:45 -03:00
2012-07-01 16:35:50 +02:00
uint32_t sprSignature ;
safe_get ( U32 , sprSignature ) ;
2015-01-23 10:30:09 -03:00
2015-02-05 21:07:44 -03:00
uint32_t total_pics = 0 ;
2023-10-09 19:12:16 -07:00
if ( is_extended ) {
2015-02-05 21:07:44 -03:00
safe_get ( U32 , total_pics ) ;
} else {
2015-01-23 10:30:09 -03:00
uint16_t u16 = 0 ;
safe_get ( U16 , u16 ) ;
total_pics = u16 ;
}
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
if ( ! g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) {
2012-06-29 16:44:26 +02:00
spritefile = nstr ( datafile . GetFullPath ( ) ) ;
unloaded = false ;
return true ;
}
2014-02-28 22:34:25 +01:00
std : : vector < uint32_t > sprite_indexes ;
2023-10-09 19:12:16 -07:00
for ( uint32_t i = 0 ; i < total_pics ; + + i ) {
2012-06-29 16:44:26 +02:00
uint32_t index ;
safe_get ( U32 , index ) ;
sprite_indexes . push_back ( index ) ;
}
2012-07-01 16:35:50 +02:00
// Now read individual sprites
2012-06-29 16:44:26 +02:00
int id = 1 ;
2023-10-09 19:12:16 -07:00
for ( std : : vector < uint32_t > : : iterator sprite_iter = sprite_indexes . begin ( ) ; sprite_iter ! = sprite_indexes . end ( ) ; + + sprite_iter , + + id ) {
2014-02-28 22:34:25 +01:00
uint32_t index = * sprite_iter + 3 ;
2012-06-29 16:44:26 +02:00
fh . seek ( index ) ;
uint16_t size ;
safe_get ( U16 , size ) ;
ImageMap : : iterator it = image_space . find ( id ) ;
2023-10-09 19:12:16 -07:00
if ( it ! = image_space . end ( ) ) {
2023-10-15 21:52:40 +00:00
const auto & spr = static_self_cast < GameSprite : : NormalImage > ( it - > second ) ;
2023-10-09 19:12:16 -07:00
if ( spr & & size > 0 ) {
if ( spr - > size > 0 ) {
2012-06-29 16:44:26 +02:00
wxString ss ;
2016-11-05 15:28:14 +01:00
ss < < " items.spr: Duplicate GameSprite id " < < id ;
2012-06-29 16:44:26 +02:00
warnings . push_back ( ss ) ;
2023-10-09 23:02:37 -03:00
fh . skip ( size ) ;
2012-06-29 16:44:26 +02:00
} else {
spr - > id = id ;
spr - > size = size ;
2023-10-15 18:51:57 -03:00
spr - > dump = newd < uint8_t > ( size ) ; ///////////////////////////////////////////////
if ( ! fh . getRAW ( & spr - > dump , size ) ) { ///////////////////////////////////////////
2023-10-09 19:12:16 -07:00
error = wxstr ( fh . getErrorMessage ( ) ) ;
2012-06-29 16:44:26 +02:00
return false ;
}
}
}
} else {
2023-10-09 23:02:37 -03:00
fh . skip ( size ) ;
2012-06-29 16:44:26 +02:00
}
}
# undef safe_get
unloaded = false ;
return true ;
}
2023-10-15 18:51:57 -03:00
bool GraphicManager : : loadSpriteDump ( std : : shared_ptr < uint8_t > target , uint16_t & size , int sprite_id ) {
2023-10-09 19:12:16 -07:00
if ( g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) {
2012-06-29 16:44:26 +02:00
return false ;
2023-10-09 19:12:16 -07:00
}
2012-07-01 16:35:50 +02:00
2023-10-09 19:12:16 -07:00
if ( sprite_id = = 0 ) {
2012-06-29 16:44:26 +02:00
// Empty GameSprite
size = 0 ;
2014-01-22 20:16:17 +01:00
target = nullptr ;
2012-06-29 16:44:26 +02:00
return true ;
}
FileReadHandle fh ( spritefile ) ;
2023-10-09 19:12:16 -07:00
if ( ! fh . isOk ( ) ) {
2012-06-29 16:44:26 +02:00
return false ;
2023-10-09 19:12:16 -07:00
}
2012-06-29 16:44:26 +02:00
unloaded = false ;
2012-07-01 16:35:50 +02:00
2023-10-09 19:12:16 -07:00
if ( ! fh . seek ( ( is_extended ? 4 : 2 ) + sprite_id * sizeof ( uint32_t ) ) ) {
2015-01-23 10:30:09 -03:00
return false ;
2023-10-09 19:12:16 -07:00
}
2012-06-29 16:44:26 +02:00
uint32_t to_seek = 0 ;
2023-10-09 19:12:16 -07:00
if ( fh . getU32 ( to_seek ) ) {
fh . seek ( to_seek + 3 ) ;
2012-06-29 16:44:26 +02:00
uint16_t sprite_size ;
2023-10-09 19:12:16 -07:00
if ( fh . getU16 ( sprite_size ) ) {
2023-10-15 18:51:57 -03:00
target = newd < uint8_t > ( sprite_size ) ;
if ( fh . getRAW ( & target , sprite_size ) ) {
2012-06-29 16:44:26 +02:00
size = sprite_size ;
return true ;
}
2014-01-22 20:16:17 +01:00
target = nullptr ;
2012-06-29 16:44:26 +02:00
}
}
return false ;
}
2023-10-09 19:12:16 -07:00
void GraphicManager : : addSpriteToCleanup ( GameSprite * spr ) {
2012-06-29 16:44:26 +02:00
cleanup_list . push_back ( spr ) ;
// Clean if needed
2023-10-09 19:12:16 -07:00
if ( cleanup_list . size ( ) > std : : max < uint32_t > ( 100 , g_settings . getInteger ( Config : : SOFTWARE_CLEAN_THRESHOLD ) ) ) {
for ( int i = 0 ; i < g_settings . getInteger ( Config : : SOFTWARE_CLEAN_SIZE ) & & static_cast < uint32_t > ( i ) < cleanup_list . size ( ) ; + + i ) {
2012-06-29 16:44:26 +02:00
cleanup_list . front ( ) - > unloadDC ( ) ;
cleanup_list . pop_front ( ) ;
}
}
}
2023-10-09 19:12:16 -07:00
void GraphicManager : : garbageCollection ( ) {
if ( g_settings . getInteger ( Config : : TEXTURE_MANAGEMENT ) ) {
2014-01-22 20:16:17 +01:00
int t = time ( nullptr ) ;
2023-10-09 19:12:16 -07:00
if ( loaded_textures > g_settings . getInteger ( Config : : TEXTURE_CLEAN_THRESHOLD ) & & t - lastclean > g_settings . getInteger ( Config : : TEXTURE_CLEAN_PULSE ) ) {
2012-06-29 16:44:26 +02:00
ImageMap : : iterator iit = image_space . begin ( ) ;
2023-10-09 19:12:16 -07:00
while ( iit ! = image_space . end ( ) ) {
2012-06-29 16:44:26 +02:00
iit - > second - > clean ( t ) ;
+ + iit ;
}
SpriteMap : : iterator sit = sprite_space . begin ( ) ;
2023-10-09 19:12:16 -07:00
while ( sit ! = sprite_space . end ( ) ) {
2023-10-15 21:52:40 +00:00
const auto & gs = static_self_cast < GameSprite > ( sit - > second ) ;
2023-10-09 19:12:16 -07:00
if ( gs ) {
gs - > clean ( t ) ;
}
2012-06-29 16:44:26 +02:00
+ + sit ;
}
lastclean = t ;
}
}
}
2023-10-09 19:12:16 -07:00
EditorSprite : : EditorSprite ( wxBitmap * b16x16 , wxBitmap * b32x32 ) {
2012-06-29 16:44:26 +02:00
bm [ SPRITE_SIZE_16x16 ] = b16x16 ;
bm [ SPRITE_SIZE_32x32 ] = b32x32 ;
}
2023-10-09 19:12:16 -07:00
EditorSprite : : ~ EditorSprite ( ) {
2012-06-29 16:44:26 +02:00
unloadDC ( ) ;
}
2023-10-09 19:12:16 -07:00
void EditorSprite : : DrawTo ( wxDC * dc , SpriteSize sz , int start_x , int start_y , int width , int height ) {
2012-06-29 16:44:26 +02:00
wxBitmap * sp = bm [ sz ] ;
2023-10-09 19:12:16 -07:00
if ( sp ) {
2012-06-29 16:44:26 +02:00
dc - > DrawBitmap ( * sp , start_x , start_y , true ) ;
2023-10-09 19:12:16 -07:00
}
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
void EditorSprite : : unloadDC ( ) {
2012-06-29 16:44:26 +02:00
delete bm [ SPRITE_SIZE_16x16 ] ;
delete bm [ SPRITE_SIZE_32x32 ] ;
2014-01-22 20:16:17 +01:00
bm [ SPRITE_SIZE_16x16 ] = nullptr ;
bm [ SPRITE_SIZE_32x32 ] = nullptr ;
2012-06-29 16:44:26 +02:00
}
GameSprite : : GameSprite ( ) :
id ( 0 ) ,
height ( 0 ) ,
width ( 0 ) ,
2015-02-03 22:59:14 -03:00
layers ( 0 ) ,
pattern_x ( 0 ) ,
pattern_y ( 0 ) ,
pattern_z ( 0 ) ,
2012-06-29 16:44:26 +02:00
frames ( 0 ) ,
numsprites ( 0 ) ,
2016-11-09 12:17:17 -03:00
animator ( nullptr ) ,
2023-10-09 23:02:37 -03:00
ground_speed ( 0 ) ,
2012-06-29 16:44:26 +02:00
draw_height ( 0 ) ,
2023-10-09 19:12:16 -07:00
minimap_color ( 0 ) {
2014-01-22 20:16:17 +01:00
dc [ SPRITE_SIZE_16x16 ] = nullptr ;
dc [ SPRITE_SIZE_32x32 ] = nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GameSprite : : ~ GameSprite ( ) {
2012-06-29 16:44:26 +02:00
unloadDC ( ) ;
}
void GameSprite : : clean ( int time ) {
2023-10-15 18:51:57 -03:00
for ( std : : list < std : : shared_ptr < TemplateImage > > : : iterator iter = instanced_templates . begin ( ) ;
2023-10-09 19:12:16 -07:00
iter ! = instanced_templates . end ( ) ;
+ + iter ) {
2012-06-29 16:44:26 +02:00
( * iter ) - > clean ( time ) ;
}
}
2023-10-09 19:12:16 -07:00
void GameSprite : : unloadDC ( ) {
2012-06-29 16:44:26 +02:00
delete dc [ SPRITE_SIZE_16x16 ] ;
delete dc [ SPRITE_SIZE_32x32 ] ;
2014-01-22 20:16:17 +01:00
dc [ SPRITE_SIZE_16x16 ] = nullptr ;
dc [ SPRITE_SIZE_32x32 ] = nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
int GameSprite : : getIndex ( int width , int height , int layer , int pattern_x , int pattern_y , int pattern_z , int frame ) const {
return ( ( ( ( ( ( frame % this - > frames ) * this - > pattern_z + pattern_z ) * this - > pattern_y + pattern_y ) * this - > pattern_x + pattern_x ) * this - > layers + layer ) * this - > height + height ) * this - > width + width ;
2016-11-09 11:49:58 -03:00
}
2023-10-09 19:12:16 -07:00
GLuint GameSprite : : getHardwareID ( int _x , int _y , int _layer , int _count , int _pattern_x , int _pattern_y , int _pattern_z , int _frame ) {
2014-02-28 22:34:25 +01:00
uint32_t v ;
2023-10-09 19:12:16 -07:00
if ( _count > = 0 & & height < = 1 & & width < = 1 ) {
2012-06-29 16:44:26 +02:00
v = _count ;
} else {
2023-10-09 19:12:16 -07:00
v = ( ( ( ( ( ( _frame ) * pattern_y + _pattern_y ) * pattern_x + _pattern_x ) * layers + _layer ) * height + _y ) * width + _x ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( v > = numsprites ) {
if ( numsprites = = 1 ) {
2012-06-29 16:44:26 +02:00
v = 0 ;
} else {
v % = numsprites ;
}
}
return spriteList [ v ] - > getHardwareID ( ) ;
}
2023-10-15 18:51:57 -03:00
std : : shared_ptr < GameSprite : : TemplateImage > GameSprite : : getTemplateImage ( int sprite_index , const Outfit & outfit ) {
2023-10-09 19:12:16 -07:00
if ( instanced_templates . empty ( ) ) {
2023-10-15 21:52:40 +00:00
const auto & img = newd < TemplateImage > ( this , sprite_index , outfit ) ;
2012-06-29 16:44:26 +02:00
instanced_templates . push_back ( img ) ;
return img ;
}
// While this is linear lookup, it is very rare for the list to contain more than 4-8 entries, so it's faster than a hashmap anyways.
2023-10-15 18:51:57 -03:00
for ( std : : list < std : : shared_ptr < TemplateImage > > : : iterator iter = instanced_templates . begin ( ) ; iter ! = instanced_templates . end ( ) ; + + iter ) {
2023-10-15 21:52:40 +00:00
const auto & img = * iter ;
2023-10-09 19:12:16 -07:00
if ( img - > sprite_index = = sprite_index ) {
2012-06-29 16:44:26 +02:00
uint32_t lookHash = img - > lookHead < < 24 | img - > lookBody < < 16 | img - > lookLegs < < 8 | img - > lookFeet ;
2023-10-09 19:12:16 -07:00
if ( outfit . getColorHash ( ) = = lookHash ) {
2012-06-29 16:44:26 +02:00
return img ;
}
}
}
2023-10-15 21:52:40 +00:00
const auto & img = newd < TemplateImage > ( this , sprite_index , outfit ) ;
2012-06-29 16:44:26 +02:00
instanced_templates . push_back ( img ) ;
return img ;
}
2023-10-09 19:12:16 -07:00
GLuint GameSprite : : getHardwareID ( int _x , int _y , int _dir , int _addon , int _pattern_z , const Outfit & _outfit , int _frame ) {
2021-06-06 20:05:01 -03:00
uint32_t v = getIndex ( _x , _y , 0 , _dir , _addon , _pattern_z , _frame ) ;
2023-10-09 19:12:16 -07:00
if ( v > = numsprites ) {
if ( numsprites = = 1 ) {
2012-06-29 16:44:26 +02:00
v = 0 ;
} else {
v % = numsprites ;
}
}
2023-10-09 19:12:16 -07:00
if ( layers > 1 ) { // Template
2023-10-15 21:52:40 +00:00
std : : shared_ptr < TemplateImage > img = getTemplateImage ( v , _outfit ) ;
2012-06-29 16:44:26 +02:00
return img - > getHardwareID ( ) ;
}
return spriteList [ v ] - > getHardwareID ( ) ;
}
2023-10-15 18:51:57 -03:00
std : : shared_ptr < wxMemoryDC > GameSprite : : getDC ( SpriteSize size ) {
2016-11-09 11:49:58 -03:00
ASSERT ( size = = SPRITE_SIZE_16x16 | | size = = SPRITE_SIZE_32x32 ) ;
2023-10-09 19:12:16 -07:00
if ( ! dc [ size ] ) {
2016-11-09 11:49:58 -03:00
ASSERT ( width > = 1 & & height > = 1 ) ;
2017-07-04 20:07:29 +02:00
const int bgshade = g_settings . getInteger ( Config : : ICON_BACKGROUND ) ;
2023-10-09 23:02:37 -03:00
int image_size = std : : max < int > ( width , height ) * rme : : SpritePixels ;
2017-03-26 16:50:51 -03:00
wxImage image ( image_size , image_size ) ;
2017-10-08 21:17:26 -03:00
image . Clear ( bgshade ) ;
2016-11-09 11:49:58 -03:00
2023-10-09 19:12:16 -07:00
for ( uint8_t l = 0 ; l < layers ; l + + ) {
for ( uint8_t w = 0 ; w < width ; w + + ) {
for ( uint8_t h = 0 ; h < height ; h + + ) {
2016-11-09 11:49:58 -03:00
const int i = getIndex ( w , h , l , 0 , 0 , 0 , 0 ) ;
uint8_t * data = spriteList [ i ] - > getRGBData ( ) ;
2023-10-09 19:12:16 -07:00
if ( data ) {
2023-10-09 23:02:37 -03:00
wxImage img ( rme : : SpritePixels , rme : : SpritePixels , data ) ;
2016-11-09 11:49:58 -03:00
img . SetMaskColour ( 0xFF , 0x00 , 0xFF ) ;
2023-10-09 23:02:37 -03:00
image . Paste ( img , ( width - w - 1 ) * rme : : SpritePixels , ( height - h - 1 ) * rme : : SpritePixels ) ;
2016-11-09 11:49:58 -03:00
img . Destroy ( ) ;
2012-06-29 16:44:26 +02:00
}
}
}
}
// Now comes the resizing / antialiasing
2023-10-09 19:12:16 -07:00
if ( size = = SPRITE_SIZE_16x16 | | image . GetWidth ( ) > rme : : SpritePixels | | image . GetHeight ( ) > rme : : SpritePixels ) {
2017-03-26 16:50:51 -03:00
int new_size = SPRITE_SIZE_16x16 ? 16 : 32 ;
image . Rescale ( new_size , new_size ) ;
2012-06-29 16:44:26 +02:00
}
2016-11-09 11:49:58 -03:00
2016-11-09 13:06:59 -03:00
wxBitmap bmp ( image ) ;
2023-10-15 18:51:57 -03:00
dc [ size ] = newd < wxMemoryDC > ( bmp ) ;
2016-11-09 11:49:58 -03:00
g_gui . gfx . addSpriteToCleanup ( this ) ;
image . Destroy ( ) ;
2012-06-29 16:44:26 +02:00
}
2016-11-09 11:49:58 -03:00
return dc [ size ] ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
void GameSprite : : DrawTo ( wxDC * dc , SpriteSize sz , int start_x , int start_y , int width , int height ) {
if ( width = = - 1 ) {
width = sz = = SPRITE_SIZE_32x32 ? 32 : 16 ;
}
if ( height = = - 1 ) {
height = sz = = SPRITE_SIZE_32x32 ? 32 : 16 ;
}
2023-10-15 21:52:40 +00:00
std : : shared_ptr < wxDC > sdc = getDC ( sz ) ;
2023-10-09 19:12:16 -07:00
if ( sdc ) {
2023-10-15 18:51:57 -03:00
dc - > Blit ( start_x , start_y , width , height , sdc . get ( ) , 0 , 0 , wxCOPY , true ) ;
2015-12-06 11:42:37 -03:00
} else {
2023-10-09 19:12:16 -07:00
const wxBrush & b = dc - > GetBrush ( ) ;
2012-06-29 16:44:26 +02:00
dc - > SetBrush ( * wxRED_BRUSH ) ;
dc - > DrawRectangle ( start_x , start_y , width , height ) ;
dc - > SetBrush ( b ) ;
}
}
GameSprite : : Image : : Image ( ) :
isGLLoaded ( false ) ,
2023-10-09 19:12:16 -07:00
lastaccess ( 0 ) {
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GameSprite : : Image : : ~ Image ( ) {
2012-06-29 16:44:26 +02:00
unloadGLTexture ( 0 ) ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : Image : : createGLTexture ( GLuint textureId ) {
2012-06-29 16:44:26 +02:00
ASSERT ( ! isGLLoaded ) ;
uint8_t * rgba = getRGBAData ( ) ;
2023-10-09 19:12:16 -07:00
if ( ! rgba ) {
2014-03-09 16:53:29 +01:00
return ;
}
2012-06-29 16:44:26 +02:00
isGLLoaded = true ;
2016-09-29 20:30:54 -03:00
g_gui . gfx . loaded_textures + = 1 ;
2012-06-29 16:44:26 +02:00
2023-10-09 23:02:37 -03:00
glBindTexture ( GL_TEXTURE_2D , textureId ) ;
2012-06-29 16:44:26 +02:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ; // Linear Filtering
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ; // Linear Filtering
2014-03-09 16:53:29 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , 0x812F ) ; // GL_CLAMP_TO_EDGE
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , 0x812F ) ; // GL_CLAMP_TO_EDGE
2023-10-09 23:02:37 -03:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , rme : : SpritePixels , rme : : SpritePixels , 0 , GL_RGBA , GL_UNSIGNED_BYTE , rgba ) ;
2012-06-29 16:44:26 +02:00
delete [ ] rgba ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : Image : : unloadGLTexture ( GLuint textureId ) {
2012-06-29 16:44:26 +02:00
isGLLoaded = false ;
2016-09-29 20:30:54 -03:00
g_gui . gfx . loaded_textures - = 1 ;
2023-10-09 23:02:37 -03:00
glDeleteTextures ( 1 , & textureId ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
void GameSprite : : Image : : visit ( ) {
2014-01-22 20:16:17 +01:00
lastaccess = time ( nullptr ) ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
void GameSprite : : Image : : clean ( int time ) {
if ( isGLLoaded & & time - lastaccess > g_settings . getInteger ( Config : : TEXTURE_LONGEVITY ) ) {
2012-06-29 16:44:26 +02:00
unloadGLTexture ( 0 ) ;
}
}
GameSprite : : NormalImage : : NormalImage ( ) :
id ( 0 ) ,
size ( 0 ) ,
2023-10-09 19:12:16 -07:00
dump ( nullptr ) {
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2023-10-15 18:51:57 -03:00
GameSprite : : NormalImage : : ~ NormalImage ( ) = default ;
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
void GameSprite : : NormalImage : : clean ( int time ) {
2012-06-29 16:44:26 +02:00
Image : : clean ( time ) ;
2023-10-09 19:12:16 -07:00
if ( time - lastaccess > 5 & & ! g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) { // We keep dumps around for 5 seconds.
2014-01-22 20:16:17 +01:00
dump = nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2023-10-15 18:51:57 -03:00
std : : shared_ptr < uint8_t > GameSprite : : NormalImage : : getRGBData ( ) {
2023-10-09 19:12:16 -07:00
if ( ! dump ) {
if ( g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) {
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( ! g_gui . gfx . loadSpriteDump ( dump , size , id ) ) {
2016-11-09 11:49:58 -03:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2023-10-09 23:02:37 -03:00
const int pixels_data_size = rme : : SpritePixels * rme : : SpritePixels * 3 ;
2023-10-15 21:52:40 +00:00
const auto & data = newd < uint8_t > ( pixels_data_size ) ;
2016-11-09 11:49:58 -03:00
uint8_t bpp = g_gui . gfx . hasTransparency ( ) ? 4 : 3 ;
int write = 0 ;
int read = 0 ;
// decompress pixels
2023-10-09 19:12:16 -07:00
while ( read < size & & write < pixels_data_size ) {
2016-11-09 11:49:58 -03:00
int transparent = dump [ read ] | dump [ read + 1 ] < < 8 ;
read + = 2 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < transparent & & write < pixels_data_size ; i + + ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = 0xFF ; // red
data [ write + 1 ] = 0x00 ; // green
data [ write + 2 ] = 0xFF ; // blue
write + = 3 ;
}
int colored = dump [ read ] | dump [ read + 1 ] < < 8 ;
read + = 2 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < colored & & write < pixels_data_size ; i + + ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = dump [ read + 0 ] ; // red
data [ write + 1 ] = dump [ read + 1 ] ; // green
data [ write + 2 ] = dump [ read + 2 ] ; // blue
write + = 3 ;
read + = bpp ;
2012-06-29 16:44:26 +02:00
}
}
2016-11-09 11:49:58 -03:00
// fill remaining pixels
2023-10-09 19:12:16 -07:00
while ( write < pixels_data_size ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = 0xFF ; // red
data [ write + 1 ] = 0x00 ; // green
data [ write + 2 ] = 0xFF ; // blue
write + = 3 ;
}
return data ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
uint8_t * GameSprite : : NormalImage : : getRGBAData ( ) {
if ( ! dump ) {
if ( g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) {
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( ! g_gui . gfx . loadSpriteDump ( dump , size , id ) ) {
2016-11-09 11:49:58 -03:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2023-10-09 23:02:37 -03:00
const int pixels_data_size = rme : : SpritePixelsSize * 4 ;
2016-11-09 11:49:58 -03:00
uint8_t * data = newd uint8_t [ pixels_data_size ] ;
bool use_alpha = g_gui . gfx . hasTransparency ( ) ;
uint8_t bpp = use_alpha ? 4 : 3 ;
int write = 0 ;
int read = 0 ;
// decompress pixels
2023-10-09 19:12:16 -07:00
while ( read < size & & write < pixels_data_size ) {
2016-11-09 11:49:58 -03:00
int transparent = dump [ read ] | dump [ read + 1 ] < < 8 ;
2023-10-09 19:12:16 -07:00
if ( use_alpha & & transparent > = rme : : SpritePixelsSize ) { // Corrupted sprite?
2016-11-09 11:49:58 -03:00
break ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 11:49:58 -03:00
read + = 2 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < transparent & & write < pixels_data_size ; i + + ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = 0x00 ; // red
data [ write + 1 ] = 0x00 ; // green
data [ write + 2 ] = 0x00 ; // blue
data [ write + 3 ] = 0x00 ; // alpha
write + = 4 ;
}
int colored = dump [ read ] | dump [ read + 1 ] < < 8 ;
read + = 2 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < colored & & write < pixels_data_size ; i + + ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = dump [ read + 0 ] ; // red
data [ write + 1 ] = dump [ read + 1 ] ; // green
data [ write + 2 ] = dump [ read + 2 ] ; // blue
data [ write + 3 ] = use_alpha ? dump [ read + 3 ] : 0xFF ; // alpha
write + = 4 ;
read + = bpp ;
2012-06-29 16:44:26 +02:00
}
}
2016-11-09 11:49:58 -03:00
// fill remaining pixels
2023-10-09 19:12:16 -07:00
while ( write < pixels_data_size ) {
2016-11-09 11:49:58 -03:00
data [ write + 0 ] = 0x00 ; // red
data [ write + 1 ] = 0x00 ; // green
data [ write + 2 ] = 0x00 ; // blue
data [ write + 3 ] = 0x00 ; // alpha
write + = 4 ;
}
return data ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GLuint GameSprite : : NormalImage : : getHardwareID ( ) {
if ( ! isGLLoaded ) {
2012-06-29 16:44:26 +02:00
createGLTexture ( id ) ;
}
visit ( ) ;
return id ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : NormalImage : : createGLTexture ( GLuint textureId ) {
2012-06-29 16:44:26 +02:00
Image : : createGLTexture ( id ) ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : NormalImage : : unloadGLTexture ( GLuint textureId ) {
2023-10-09 23:02:37 -03:00
Image : : unloadGLTexture ( id ) ;
}
2023-10-09 19:12:16 -07:00
GameSprite : : EditorImage : : EditorImage ( const wxArtID & bitmapId ) :
2023-10-09 23:02:37 -03:00
NormalImage ( ) ,
2023-10-09 19:12:16 -07:00
bitmapId ( bitmapId ) { }
2023-10-09 23:02:37 -03:00
2023-10-09 19:12:16 -07:00
void GameSprite : : EditorImage : : createGLTexture ( GLuint textureId ) {
2023-10-09 23:02:37 -03:00
ASSERT ( ! isGLLoaded ) ;
wxSize size ( rme : : SpritePixels , rme : : SpritePixels ) ;
wxBitmap bitmap = wxArtProvider : : GetBitmap ( bitmapId , wxART_OTHER , size ) ;
wxNativePixelData data ( bitmap ) ;
2023-10-09 19:12:16 -07:00
if ( ! data ) {
return ;
}
2023-10-09 23:02:37 -03:00
const int imageSize = rme : : SpritePixelsSize * 4 ;
2023-10-09 19:12:16 -07:00
GLubyte * imageData = new GLubyte [ imageSize ] ;
2023-10-09 23:02:37 -03:00
int write = 0 ;
wxNativePixelData : : Iterator it ( data ) ;
it . Offset ( data , 0 , 0 ) ;
2023-10-09 19:12:16 -07:00
for ( size_t y = 0 ; y < rme : : SpritePixels ; + + y ) {
2023-10-09 23:02:37 -03:00
wxNativePixelData : : Iterator row_start = it ;
2023-10-09 19:12:16 -07:00
for ( size_t x = 0 ; x < rme : : SpritePixels ; + + x , it + + ) {
2023-10-09 23:02:37 -03:00
uint8_t red = it . Red ( ) ;
uint8_t green = it . Green ( ) ;
uint8_t blue = it . Blue ( ) ;
bool transparent = red = = 0xFF & & green = = 0x00 & & blue = = 0xFF ;
imageData [ write + 0 ] = red ;
imageData [ write + 1 ] = green ;
imageData [ write + 2 ] = blue ;
imageData [ write + 3 ] = transparent ? 0x00 : 0xFF ;
write + = 4 ;
}
it = row_start ;
it . OffsetY ( data , 1 ) ;
}
isGLLoaded = true ;
id = g_gui . gfx . getFreeTextureID ( ) ;
g_gui . gfx . loaded_textures + = 1 ;
glBindTexture ( GL_TEXTURE_2D , id ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ; // Linear Filtering
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ; // Linear Filtering
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , 0x812F ) ; // GL_CLAMP_TO_EDGE
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , 0x812F ) ; // GL_CLAMP_TO_EDGE
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , rme : : SpritePixels , rme : : SpritePixels , 0 , GL_RGBA , GL_UNSIGNED_BYTE , imageData ) ;
delete [ ] imageData ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : EditorImage : : unloadGLTexture ( GLuint textureId ) {
2012-06-29 16:44:26 +02:00
Image : : unloadGLTexture ( id ) ;
}
2023-10-09 19:12:16 -07:00
GameSprite : : TemplateImage : : TemplateImage ( GameSprite * parent , int v , const Outfit & outfit ) :
2012-06-29 16:44:26 +02:00
gl_tid ( 0 ) ,
parent ( parent ) ,
sprite_index ( v ) ,
lookHead ( outfit . lookHead ) ,
lookBody ( outfit . lookBody ) ,
lookLegs ( outfit . lookLegs ) ,
2023-10-09 19:12:16 -07:00
lookFeet ( outfit . lookFeet ) {
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
GameSprite : : TemplateImage : : ~ TemplateImage ( ) {
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
void GameSprite : : TemplateImage : : colorizePixel ( uint8_t color , uint8_t & red , uint8_t & green , uint8_t & blue ) {
2012-06-29 16:44:26 +02:00
// Thanks! Khaos, or was it mips? Hmmm... =)
uint8_t ro = ( TemplateOutfitLookupTable [ color ] & 0xFF0000 ) > > 16 ; // rgb outfit
uint8_t go = ( TemplateOutfitLookupTable [ color ] & 0xFF00 ) > > 8 ;
uint8_t bo = ( TemplateOutfitLookupTable [ color ] & 0xFF ) ;
red = ( uint8_t ) ( red * ( ro / 255.f ) ) ;
green = ( uint8_t ) ( green * ( go / 255.f ) ) ;
blue = ( uint8_t ) ( blue * ( bo / 255.f ) ) ;
}
2023-10-09 19:12:16 -07:00
uint8_t * GameSprite : : TemplateImage : : getRGBData ( ) {
2012-06-29 16:44:26 +02:00
uint8_t * rgbdata = parent - > spriteList [ sprite_index ] - > getRGBData ( ) ;
uint8_t * template_rgbdata = parent - > spriteList [ sprite_index + parent - > height * parent - > width ] - > getRGBData ( ) ;
2023-10-09 19:12:16 -07:00
if ( ! rgbdata ) {
2012-06-29 16:44:26 +02:00
delete [ ] template_rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( ! template_rgbdata ) {
2012-06-29 16:44:26 +02:00
delete [ ] rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( lookHead > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookHead = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookBody > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookBody = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookLegs > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookLegs = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookFeet > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookFeet = 0 ;
}
2023-10-09 19:12:16 -07:00
for ( int y = 0 ; y < rme : : SpritePixels ; + + y ) {
for ( int x = 0 ; x < rme : : SpritePixels ; + + x ) {
uint8_t & red = rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 0 ] ;
uint8_t & green = rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 1 ] ;
uint8_t & blue = rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
uint8_t & tred = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 0 ] ;
uint8_t & tgreen = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 1 ] ;
uint8_t & tblue = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
if ( tred & & tgreen & & ! tblue ) { // yellow => head
2012-06-29 16:44:26 +02:00
colorizePixel ( lookHead , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( tred & & ! tgreen & & ! tblue ) { // red => body
2012-06-29 16:44:26 +02:00
colorizePixel ( lookBody , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( ! tred & & tgreen & & ! tblue ) { // green => legs
2012-06-29 16:44:26 +02:00
colorizePixel ( lookLegs , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( ! tred & & ! tgreen & & tblue ) { // blue => feet
2012-06-29 16:44:26 +02:00
colorizePixel ( lookFeet , red , green , blue ) ;
}
}
}
delete [ ] template_rgbdata ;
return rgbdata ;
}
2023-10-09 19:12:16 -07:00
uint8_t * GameSprite : : TemplateImage : : getRGBAData ( ) {
2012-06-29 16:44:26 +02:00
uint8_t * rgbadata = parent - > spriteList [ sprite_index ] - > getRGBAData ( ) ;
uint8_t * template_rgbdata = parent - > spriteList [ sprite_index + parent - > height * parent - > width ] - > getRGBData ( ) ;
2023-10-09 19:12:16 -07:00
if ( ! rgbadata ) {
2012-06-29 16:44:26 +02:00
delete [ ] template_rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( ! template_rgbdata ) {
2012-06-29 16:44:26 +02:00
delete [ ] rgbadata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
2023-10-09 19:12:16 -07:00
if ( lookHead > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookHead = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookBody > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookBody = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookLegs > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookLegs = 0 ;
}
2023-10-09 19:12:16 -07:00
if ( lookFeet > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
2012-06-29 16:44:26 +02:00
lookFeet = 0 ;
}
2023-10-09 19:12:16 -07:00
for ( int y = 0 ; y < rme : : SpritePixels ; + + y ) {
for ( int x = 0 ; x < rme : : SpritePixels ; + + x ) {
uint8_t & red = rgbadata [ y * rme : : SpritePixels * 4 + x * 4 + 0 ] ;
uint8_t & green = rgbadata [ y * rme : : SpritePixels * 4 + x * 4 + 1 ] ;
uint8_t & blue = rgbadata [ y * rme : : SpritePixels * 4 + x * 4 + 2 ] ;
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
uint8_t & tred = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 0 ] ;
uint8_t & tgreen = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 1 ] ;
uint8_t & tblue = template_rgbdata [ y * rme : : SpritePixels * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
2023-10-09 19:12:16 -07:00
if ( tred & & tgreen & & ! tblue ) { // yellow => head
2012-06-29 16:44:26 +02:00
colorizePixel ( lookHead , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( tred & & ! tgreen & & ! tblue ) { // red => body
2012-06-29 16:44:26 +02:00
colorizePixel ( lookBody , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( ! tred & & tgreen & & ! tblue ) { // green => legs
2012-06-29 16:44:26 +02:00
colorizePixel ( lookLegs , red , green , blue ) ;
2023-10-09 19:12:16 -07:00
} else if ( ! tred & & ! tgreen & & tblue ) { // blue => feet
2012-06-29 16:44:26 +02:00
colorizePixel ( lookFeet , red , green , blue ) ;
}
}
}
delete [ ] template_rgbdata ;
return rgbadata ;
}
2023-10-09 19:12:16 -07:00
GLuint GameSprite : : TemplateImage : : getHardwareID ( ) {
if ( ! isGLLoaded ) {
if ( gl_tid = = 0 ) {
2016-09-29 20:30:54 -03:00
gl_tid = g_gui . gfx . getFreeTextureID ( ) ;
2012-06-29 16:44:26 +02:00
}
createGLTexture ( gl_tid ) ;
2023-10-09 19:12:16 -07:00
if ( ! isGLLoaded ) {
2012-06-29 16:44:26 +02:00
return 0 ;
}
}
visit ( ) ;
return gl_tid ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : TemplateImage : : createGLTexture ( GLuint unused ) {
2012-06-29 16:44:26 +02:00
Image : : createGLTexture ( gl_tid ) ;
}
2023-10-09 19:12:16 -07:00
void GameSprite : : TemplateImage : : unloadGLTexture ( GLuint unused ) {
2012-06-29 16:44:26 +02:00
Image : : unloadGLTexture ( gl_tid ) ;
}
2016-11-09 12:17:17 -03:00
2023-10-15 18:51:57 -03:00
std : : shared_ptr < GameSprite > GameSprite : : createFromBitmap ( const wxArtID & bitmapId ) {
2023-10-15 21:52:40 +00:00
std : : shared_ptr < GameSprite : : EditorImage > image = newd < GameSprite : : EditorImage > ( bitmapId ) ;
2023-10-09 23:02:37 -03:00
2023-10-15 21:52:40 +00:00
std : : shared_ptr < GameSprite > sprite = newd < GameSprite > ( ) ;
2023-10-09 23:02:37 -03:00
sprite - > width = 1 ;
sprite - > height = 1 ;
sprite - > layers = 1 ;
sprite - > pattern_x = 1 ;
sprite - > pattern_y = 1 ;
sprite - > pattern_z = 1 ;
sprite - > frames = 1 ;
sprite - > numsprites = 1 ;
sprite - > spriteList . push_back ( image ) ;
return sprite ;
}
2016-11-09 12:17:17 -03:00
// ============================================================================
// Animator
Animator : : Animator ( int frame_count , int start_frame , int loop_count , bool async ) :
frame_count ( frame_count ) ,
start_frame ( start_frame ) ,
loop_count ( loop_count ) ,
async ( async ) ,
current_frame ( 0 ) ,
current_loop ( 0 ) ,
current_duration ( 0 ) ,
total_duration ( 0 ) ,
direction ( ANIMATION_FORWARD ) ,
last_time ( 0 ) ,
2023-10-09 19:12:16 -07:00
is_complete ( false ) {
2016-11-09 12:17:17 -03:00
ASSERT ( start_frame > = - 1 & & start_frame < frame_count ) ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < frame_count ; i + + ) {
2016-11-09 12:17:17 -03:00
durations . push_back ( newd FrameDuration ( ITEM_FRAME_DURATION , ITEM_FRAME_DURATION ) ) ;
}
reset ( ) ;
}
2023-10-09 19:12:16 -07:00
Animator : : ~ Animator ( ) {
for ( int i = 0 ; i < frame_count ; i + + ) {
2016-11-09 12:17:17 -03:00
delete durations [ i ] ;
}
durations . clear ( ) ;
}
2023-10-09 19:12:16 -07:00
int Animator : : getStartFrame ( ) const {
if ( start_frame > - 1 ) {
2016-11-09 12:17:17 -03:00
return start_frame ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
return uniform_random ( 0 , frame_count - 1 ) ;
}
2023-10-09 19:12:16 -07:00
FrameDuration * Animator : : getFrameDuration ( int frame ) {
2016-11-09 12:17:17 -03:00
ASSERT ( frame > = 0 & & frame < frame_count ) ;
return durations [ frame ] ;
}
2023-10-09 19:12:16 -07:00
int Animator : : getFrame ( ) {
2016-11-09 12:17:17 -03:00
long time = g_gui . gfx . getElapsedTime ( ) ;
2023-10-09 19:12:16 -07:00
if ( time ! = last_time & & ! is_complete ) {
2016-11-09 12:17:17 -03:00
long elapsed = time - last_time ;
2023-10-09 19:12:16 -07:00
if ( elapsed > = current_duration ) {
2016-11-09 12:17:17 -03:00
int frame = 0 ;
2023-10-09 19:12:16 -07:00
if ( loop_count < 0 ) {
2016-11-09 12:17:17 -03:00
frame = getPingPongFrame ( ) ;
2023-10-09 19:12:16 -07:00
} else {
2016-11-09 12:17:17 -03:00
frame = getLoopFrame ( ) ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
2023-10-09 19:12:16 -07:00
if ( current_frame ! = frame ) {
2016-11-09 12:17:17 -03:00
int duration = getDuration ( frame ) - ( elapsed - current_duration ) ;
2023-10-09 19:12:16 -07:00
if ( duration < 0 & & ! async ) {
2016-11-09 12:17:17 -03:00
calculateSynchronous ( ) ;
} else {
current_frame = frame ;
current_duration = std : : max < int > ( 0 , duration ) ;
}
} else {
is_complete = true ;
}
} else {
current_duration - = elapsed ;
}
last_time = time ;
}
return current_frame ;
}
2023-10-09 19:12:16 -07:00
void Animator : : setFrame ( int frame ) {
2016-11-09 12:17:17 -03:00
ASSERT ( frame = = - 1 | | frame = = 255 | | frame = = 254 | | ( frame > = 0 & & frame < frame_count ) ) ;
2023-10-09 19:12:16 -07:00
if ( current_frame = = frame ) {
2016-11-09 12:17:17 -03:00
return ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
2023-10-09 19:12:16 -07:00
if ( async ) {
if ( frame = = 255 ) { // Async mode
2016-11-09 12:17:17 -03:00
current_frame = 0 ;
2023-10-09 19:12:16 -07:00
} else if ( frame = = 254 ) { // Random mode
2016-11-09 12:17:17 -03:00
current_frame = uniform_random ( 0 , frame_count - 1 ) ;
2023-10-09 19:12:16 -07:00
} else if ( frame > = 0 & & frame < frame_count ) {
2016-11-09 12:17:17 -03:00
current_frame = frame ;
2023-10-09 19:12:16 -07:00
} else {
2016-11-09 12:17:17 -03:00
current_frame = getStartFrame ( ) ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
is_complete = false ;
last_time = g_gui . gfx . getElapsedTime ( ) ;
current_duration = getDuration ( current_frame ) ;
current_loop = 0 ;
} else {
calculateSynchronous ( ) ;
}
}
2023-10-09 19:12:16 -07:00
void Animator : : reset ( ) {
2016-11-09 12:17:17 -03:00
total_duration = 0 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < frame_count ; i + + ) {
2016-11-09 12:17:17 -03:00
total_duration + = durations [ i ] - > max ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
is_complete = false ;
direction = ANIMATION_FORWARD ;
current_loop = 0 ;
2017-05-06 12:31:26 -03:00
async = false ;
2016-11-09 12:17:17 -03:00
setFrame ( - 1 ) ;
}
2023-10-09 19:12:16 -07:00
int Animator : : getDuration ( int frame ) const {
2016-11-09 12:17:17 -03:00
ASSERT ( frame > = 0 & & frame < frame_count ) ;
return durations [ frame ] - > getDuration ( ) ;
}
2023-10-09 19:12:16 -07:00
int Animator : : getPingPongFrame ( ) {
2016-11-09 12:17:17 -03:00
int count = direction = = ANIMATION_FORWARD ? 1 : - 1 ;
int next_frame = current_frame + count ;
2023-10-09 19:12:16 -07:00
if ( next_frame < 0 | | next_frame > = frame_count ) {
2016-11-09 12:17:17 -03:00
direction = direction = = ANIMATION_FORWARD ? ANIMATION_BACKWARD : ANIMATION_FORWARD ;
count * = - 1 ;
}
return current_frame + count ;
}
2023-10-09 19:12:16 -07:00
int Animator : : getLoopFrame ( ) {
2016-11-09 12:17:17 -03:00
int next_phase = current_frame + 1 ;
2023-10-09 19:12:16 -07:00
if ( next_phase < frame_count ) {
2016-11-09 12:17:17 -03:00
return next_phase ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
2023-10-09 19:12:16 -07:00
if ( loop_count = = 0 ) {
2016-11-09 12:17:17 -03:00
return 0 ;
2023-10-09 19:12:16 -07:00
}
2016-11-09 12:17:17 -03:00
2023-10-09 19:12:16 -07:00
if ( current_loop < ( loop_count - 1 ) ) {
2016-11-09 12:17:17 -03:00
current_loop + + ;
return 0 ;
}
return current_frame ;
}
2023-10-09 19:12:16 -07:00
void Animator : : calculateSynchronous ( ) {
2016-11-09 12:17:17 -03:00
long time = g_gui . gfx . getElapsedTime ( ) ;
2023-10-09 19:12:16 -07:00
if ( time > 0 & & total_duration > 0 ) {
2016-11-09 12:17:17 -03:00
long elapsed = time % total_duration ;
int total_time = 0 ;
2023-10-09 19:12:16 -07:00
for ( int i = 0 ; i < frame_count ; i + + ) {
2016-11-09 12:17:17 -03:00
int duration = getDuration ( i ) ;
2023-10-09 19:12:16 -07:00
if ( elapsed > = total_time & & elapsed < total_time + duration ) {
2016-11-09 12:17:17 -03:00
current_frame = i ;
current_duration = duration - ( elapsed - total_time ) ;
break ;
}
total_time + = duration ;
}
last_time = time ;
}
}