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"
# include "filehandle.h"
# include "settings.h"
# include "gui.h"
2015-02-17 10:41:13 -03:00
# include "otml.h"
2012-06-29 16:44:26 +02:00
# include <wx/mstream.h>
2016-11-09 12:17:17 -03:00
# include <wx/stopwatch.h>
2019-07-23 10:21:07 -03:00
# include <wx/dir.h>
2012-06-29 16:44:26 +02:00
# include "pngfiles.h"
// All 133 template colors
static uint32_t TemplateOutfitLookupTable [ ] = {
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 ,
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 ) ,
lastclean ( 0 )
{
2016-11-09 12:17:17 -03:00
animation_timer = newd wxStopWatch ( ) ;
animation_timer - > Start ( ) ;
2012-06-29 16:44:26 +02:00
}
GraphicManager : : ~ GraphicManager ( )
{
2015-12-06 11:42:37 -03:00
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
delete iter - > second ;
}
2015-12-06 11:42:37 -03: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
delete animation_timer ;
2012-06-29 16:44:26 +02:00
}
2015-02-17 10:41:13 -03:00
bool GraphicManager : : hasTransparency ( ) const
{
return has_transparency ;
}
2012-06-29 16:44:26 +02:00
bool GraphicManager : : isUnloaded ( ) const
{
return unloaded ;
}
GLuint GraphicManager : : getFreeTextureID ( )
{
static GLuint id_counter = 0x10000000 ;
return id_counter + + ; // This should (hopefully) never run out
}
void GraphicManager : : clear ( )
{
SpriteMap new_sprite_space ;
2015-12-06 11:42:37 -03:00
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
if ( iter - > first > = 0 ) { // Don't clean internal sprites
delete iter - > second ;
} else {
new_sprite_space . insert ( std : : make_pair ( iter - > first , iter - > second ) ) ;
}
}
2015-12-06 11:42:37 -03: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 ;
}
void GraphicManager : : cleanSoftwareSprites ( )
{
2015-12-06 11:42:37 -03:00
for ( SpriteMap : : iterator iter = sprite_space . begin ( ) ; iter ! = sprite_space . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
if ( iter - > first > = 0 ) { // Don't clean internal sprites
iter - > second - > unloadDC ( ) ;
}
}
}
Sprite * GraphicManager : : getSprite ( int id )
{
SpriteMap : : iterator it = sprite_space . find ( id ) ;
2015-12-06 11:42:37 -03: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
}
GameSprite * GraphicManager : : getCreatureSprite ( int id )
{
2015-12-06 11:42:37 -03:00
if ( id < 0 ) {
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
SpriteMap : : iterator it = sprite_space . find ( id + item_count ) ;
2015-12-06 11:42:37 -03:00
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
}
uint16_t GraphicManager : : getItemSpriteMaxID ( ) const
{
return item_count ;
}
uint16_t GraphicManager : : getCreatureSpriteMaxID ( ) const
{
return creature_count ;
}
# define loadPNGFile(name) _wxGetBitmapFromMemory(name, sizeof(name))
inline wxBitmap * _wxGetBitmapFromMemory ( const unsigned char * data , int length )
{
wxMemoryInputStream is ( data , length ) ;
2016-11-05 15:28:14 +01:00
wxImage img ( is , " image/png " ) ;
2014-01-22 20:16:17 +01:00
if ( ! img . IsOk ( ) ) return nullptr ;
2012-06-29 16:44:26 +02:00
return newd wxBitmap ( img , - 1 ) ;
}
bool GraphicManager : : loadEditorSprites ( )
{
// Unused graphics MIGHT be loaded here, but it's a neglectable loss
sprite_space [ EDITOR_SPRITE_SELECTION_MARKER ] =
newd EditorSprite (
newd wxBitmap ( selection_marker_xpm16x16 ) ,
newd wxBitmap ( selection_marker_xpm32x32 )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_1x1 ] =
newd EditorSprite (
loadPNGFile ( circular_1_small_png ) ,
loadPNGFile ( circular_1_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_3x3 ] =
newd EditorSprite (
loadPNGFile ( circular_2_small_png ) ,
loadPNGFile ( circular_2_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_5x5 ] =
newd EditorSprite (
loadPNGFile ( circular_3_small_png ) ,
loadPNGFile ( circular_3_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_7x7 ] =
newd EditorSprite (
loadPNGFile ( circular_4_small_png ) ,
loadPNGFile ( circular_4_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_9x9 ] =
newd EditorSprite (
loadPNGFile ( circular_5_small_png ) ,
loadPNGFile ( circular_5_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_15x15 ] =
newd EditorSprite (
loadPNGFile ( circular_6_small_png ) ,
loadPNGFile ( circular_6_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_CD_19x19 ] =
newd EditorSprite (
loadPNGFile ( circular_7_small_png ) ,
loadPNGFile ( circular_7_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_1x1 ] =
newd EditorSprite (
loadPNGFile ( rectangular_1_small_png ) ,
loadPNGFile ( rectangular_1_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_3x3 ] =
newd EditorSprite (
loadPNGFile ( rectangular_2_small_png ) ,
loadPNGFile ( rectangular_2_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_5x5 ] =
newd EditorSprite (
loadPNGFile ( rectangular_3_small_png ) ,
loadPNGFile ( rectangular_3_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_7x7 ] =
newd EditorSprite (
loadPNGFile ( rectangular_4_small_png ) ,
loadPNGFile ( rectangular_4_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_9x9 ] =
newd EditorSprite (
loadPNGFile ( rectangular_5_small_png ) ,
loadPNGFile ( rectangular_5_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_15x15 ] =
newd EditorSprite (
loadPNGFile ( rectangular_6_small_png ) ,
loadPNGFile ( rectangular_6_png )
) ;
sprite_space [ EDITOR_SPRITE_BRUSH_SD_19x19 ] =
newd EditorSprite (
loadPNGFile ( rectangular_7_small_png ) ,
loadPNGFile ( rectangular_7_png )
) ;
sprite_space [ EDITOR_SPRITE_OPTIONAL_BORDER_TOOL ] =
newd EditorSprite (
loadPNGFile ( optional_border_small_png ) ,
loadPNGFile ( optional_border_png )
) ;
sprite_space [ EDITOR_SPRITE_ERASER ] =
newd EditorSprite (
loadPNGFile ( eraser_small_png ) ,
loadPNGFile ( eraser_png )
) ;
sprite_space [ EDITOR_SPRITE_PZ_TOOL ] =
newd EditorSprite (
loadPNGFile ( protection_zone_small_png ) ,
loadPNGFile ( protection_zone_png )
) ;
sprite_space [ EDITOR_SPRITE_PVPZ_TOOL ] =
newd EditorSprite (
loadPNGFile ( pvp_zone_small_png ) ,
loadPNGFile ( pvp_zone_png )
) ;
sprite_space [ EDITOR_SPRITE_NOLOG_TOOL ] =
newd EditorSprite (
loadPNGFile ( no_logout_small_png ) ,
loadPNGFile ( no_logout_png )
) ;
sprite_space [ EDITOR_SPRITE_NOPVP_TOOL ] =
newd EditorSprite (
loadPNGFile ( no_pvp_small_png ) ,
loadPNGFile ( no_pvp_png )
) ;
sprite_space [ EDITOR_SPRITE_DOOR_NORMAL ] =
newd EditorSprite (
loadPNGFile ( door_normal_small_png ) ,
loadPNGFile ( door_normal_png )
) ;
sprite_space [ EDITOR_SPRITE_DOOR_LOCKED ] =
newd EditorSprite (
loadPNGFile ( door_locked_small_png ) ,
loadPNGFile ( door_locked_png )
) ;
sprite_space [ EDITOR_SPRITE_DOOR_MAGIC ] =
newd EditorSprite (
loadPNGFile ( door_magic_small_png ) ,
loadPNGFile ( door_magic_png )
) ;
sprite_space [ EDITOR_SPRITE_DOOR_QUEST ] =
newd EditorSprite (
loadPNGFile ( door_quest_small_png ) ,
loadPNGFile ( door_quest_png )
) ;
sprite_space [ EDITOR_SPRITE_WINDOW_NORMAL ] =
newd EditorSprite (
loadPNGFile ( window_normal_small_png ) ,
loadPNGFile ( window_normal_png )
) ;
sprite_space [ EDITOR_SPRITE_WINDOW_HATCH ] =
newd EditorSprite (
loadPNGFile ( window_hatch_small_png ) ,
loadPNGFile ( window_hatch_png )
) ;
sprite_space [ EDITOR_SPRITE_SELECTION_GEM ] =
newd EditorSprite (
loadPNGFile ( gem_edit_png ) ,
2014-01-22 20:16:17 +01:00
nullptr
2012-06-29 16:44:26 +02:00
) ;
sprite_space [ EDITOR_SPRITE_DRAWING_GEM ] =
newd EditorSprite (
loadPNGFile ( gem_move_png ) ,
2014-01-22 20:16:17 +01:00
nullptr
2012-06-29 16:44:26 +02:00
) ;
return true ;
}
2015-02-17 10:41:13 -03: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 ;
if ( dir . GetFirst ( & otfi_file , " *.otfi " , wxDIR_FILES ) ) {
wxFileName otfi ( filename . GetFullPath ( ) , otfi_file ) ;
OTMLDocumentPtr doc = OTMLDocument : : parse ( otfi . GetFullPath ( ) . ToStdString ( ) ) ;
2015-02-17 10:41:13 -03: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 " ) ;
2022-08-22 14:33:31 -03:00
is_extended = node - > valueAt ( " extended " ) ;
has_transparency = node - > valueAt ( " transparency " ) ;
has_frame_durations = node - > valueAt ( " frame-durations " ) ;
has_frame_groups = node - > valueAt ( " frame-groups " ) ;
std : : string metadata = node - > valueAt ( " metadata-file " , std : : string ( ASSETS_NAME ) + " .dat " ) ;
std : : string sprites = node - > valueAt ( " sprites-file " , std : : string ( ASSETS_NAME ) + " .spr " ) ;
2019-07-23 10:21:07 -03:00
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
2019-07-23 10:21:07 -03: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 ;
}
2012-06-29 16:44:26 +02:00
bool GraphicManager : : loadSpriteMetadata ( const FileName & datafile , wxString & error , wxArrayString & warnings )
{
// items.otb has most of the info we need. This only loads the GameSprite metadata
FileReadHandle file ( nstr ( datafile . GetFullPath ( ) ) ) ;
2015-12-06 11:42:37 -03: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 ) ;
2012-06-29 16:44:26 +02:00
//get max id
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
2022-03-03 13:41:11 -08:00
if ( dat_format = = DAT_FORMAT_UNKNOWN ) {
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 ;
}
2015-02-17 10:41:13 -03: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
2017-05-19 15:19:10 -03:00
while ( id < = maxID ) {
2012-06-29 16:44:26 +02:00
GameSprite * sType = newd GameSprite ( ) ;
sprite_space [ id ] = sType ;
sType - > id = id ;
2012-07-01 15:55:44 +02:00
// Load the sprite flags
2015-12-06 11:42:37 -03: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 ;
2015-12-06 11:42:37 -03: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
}
2015-12-06 11:42:37 -03:00
for ( uint32_t k = 0 ; k < group_count ; + + k ) {
2015-02-01 13:42:24 -03:00
// Skipping the group type
2015-12-06 11:42:37 -03: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
2015-12-06 11:42:37 -03: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
2016-11-09 12:17:17 -03:00
if ( sType - > frames > 1 ) {
uint8_t async = 0 ;
int loop_count = 0 ;
int8_t start_frame = 0 ;
if ( has_frame_durations ) {
file . getByte ( async ) ;
file . get32 ( loop_count ) ;
file . getSByte ( start_frame ) ;
}
sType - > animator = newd Animator ( sType - > frames , start_frame , loop_count , async = = 1 ) ;
if ( has_frame_durations ) {
for ( int i = 0 ; i < sType - > frames ; i + + ) {
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
sType - > numsprites =
( int ) sType - > width * ( int ) sType - > height *
2015-02-03 22:59:14 -03:00
( 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
2015-12-06 11:42:37 -03:00
for ( uint32_t i = 0 ; i < sType - > numsprites ; + + i ) {
2015-02-01 13:42:24 -03:00
uint32_t sprite_id ;
2015-12-06 11:42:37 -03: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 ;
}
2015-12-06 11:42:37 -03:00
if ( image_space [ sprite_id ] = = nullptr ) {
2015-02-01 13:42:24 -03:00
GameSprite : : NormalImage * img = newd GameSprite : : NormalImage ( ) ;
img - > id = sprite_id ;
image_space [ sprite_id ] = img ;
}
sType - > spriteList . push_back ( static_cast < GameSprite : : NormalImage * > ( image_space [ sprite_id ] ) ) ;
2012-06-29 16:44:26 +02:00
}
}
+ + id ;
}
return true ;
}
2015-02-06 10:30:02 -03:00
bool GraphicManager : : loadSpriteMetadataFlags ( FileReadHandle & file , GameSprite * sType , wxString & error , wxArrayString & warnings )
2012-07-01 15:55:44 +02:00
{
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
2015-12-06 11:42:37 -03: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
2015-12-06 11:42:37 -03:00
if ( flag = = DatFlagLast ) {
2015-02-06 10:30:02 -03:00
return true ;
}
2021-06-23 21:30:37 -03: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
* incremented by 1 to make space for 16 as
* " No Movement Animation " flag .
*/
2015-12-06 11:42:37 -03:00
if ( flag = = 16 )
2015-02-06 10:30:02 -03:00
flag = DatFlagNoMoveAnimation ;
2015-12-06 11:42:37 -03:00
else if ( flag > 16 )
2015-02-06 10:30:02 -03:00
flag - = 1 ;
}
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 DatFlagGround :
case DatFlagWritable :
case DatFlagWritableOnce :
case DatFlagCloth :
case DatFlagLensHelp :
case DatFlagUsable :
file . skip ( 2 ) ;
break ;
case DatFlagLight :
2012-07-01 15:55:44 +02:00
file . skip ( 4 ) ;
break ;
2015-02-06 10:30:02 -03:00
2015-12-06 11:42:37 -03:00
case DatFlagDisplacement : {
2021-06-23 21:30:37 -03: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 ) ;
sType - > drawoffset_x = offset_x ;
sType - > drawoffset_y = offset_y ;
}
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
}
2012-06-29 16:44:26 +02:00
bool GraphicManager : : loadSpriteData ( const FileName & datafile , wxString & error , wxArrayString & warnings )
{
FileReadHandle fh ( nstr ( datafile . GetFullPath ( ) ) ) ;
2015-12-06 11:42:37 -03: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 ;
}
# define safe_get(func, ...) do {\
if ( ! fh . get # # func ( __VA_ARGS__ ) ) { \
error = wxstr ( fh . getErrorMessage ( ) ) ; \
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 ;
2015-12-06 11:42:37 -03: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
2016-09-29 20:37:43 -03: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 ;
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 ;
2015-12-06 11:42:37 -03: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 ) ;
if ( it ! = image_space . end ( ) ) {
GameSprite : : NormalImage * spr = dynamic_cast < GameSprite : : NormalImage * > ( it - > second ) ;
if ( spr & & size > 0 ) {
if ( spr - > size > 0 ) {
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 ) ;
fh . seekRelative ( size ) ;
} else {
spr - > id = id ;
spr - > size = size ;
spr - > dump = newd uint8_t [ size ] ;
if ( ! fh . getRAW ( spr - > dump , size ) ) {
error = wxstr ( fh . getErrorMessage ( ) ) ; \
return false ;
}
}
}
} else {
fh . seekRelative ( size ) ;
}
}
# undef safe_get
unloaded = false ;
return true ;
}
bool GraphicManager : : loadSpriteDump ( uint8_t * & target , uint16_t & size , int sprite_id )
{
2016-09-29 20:37:43 -03:00
if ( g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) )
2012-06-29 16:44:26 +02:00
return false ;
2012-07-01 16:35:50 +02:00
2015-12-06 11:42:37 -03: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 ) ;
2015-12-06 11:42:37 -03:00
if ( ! fh . isOk ( ) )
2012-06-29 16:44:26 +02:00
return false ;
unloaded = false ;
2012-07-01 16:35:50 +02:00
2015-12-06 11:42:37 -03:00
if ( ! fh . seek ( ( is_extended ? 4 : 2 ) + sprite_id * sizeof ( uint32_t ) ) )
2015-01-23 10:30:09 -03:00
return false ;
2012-06-29 16:44:26 +02:00
uint32_t to_seek = 0 ;
2015-12-06 11:42:37 -03:00
if ( fh . getU32 ( to_seek ) ) {
2012-06-29 16:44:26 +02:00
fh . seek ( to_seek + 3 ) ;
uint16_t sprite_size ;
2015-12-06 11:42:37 -03:00
if ( fh . getU16 ( sprite_size ) ) {
2012-06-29 16:44:26 +02:00
target = newd uint8_t [ sprite_size ] ;
2015-12-06 11:42:37 -03:00
if ( fh . getRAW ( target , sprite_size ) ) {
2012-06-29 16:44:26 +02:00
size = sprite_size ;
return true ;
}
delete [ ] target ;
2014-01-22 20:16:17 +01:00
target = nullptr ;
2012-06-29 16:44:26 +02:00
}
}
return false ;
}
void GraphicManager : : addSpriteToCleanup ( GameSprite * spr )
{
cleanup_list . push_back ( spr ) ;
// Clean if needed
2016-09-29 20:37:43 -03: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 ( ) ;
}
}
}
void GraphicManager : : garbageCollection ( )
{
2016-09-29 20:37:43 -03:00
if ( g_settings . getInteger ( Config : : TEXTURE_MANAGEMENT ) ) {
2014-01-22 20:16:17 +01:00
int t = time ( nullptr ) ;
2016-09-29 20:37:43 -03: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 ( ) ;
2015-12-06 11:42:37 -03: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 ( ) ;
2015-12-06 11:42:37 -03:00
while ( sit ! = sprite_space . end ( ) ) {
2012-06-29 16:44:26 +02:00
GameSprite * gs = dynamic_cast < GameSprite * > ( sit - > second ) ;
if ( gs ) gs - > clean ( t ) ;
+ + sit ;
}
lastclean = t ;
}
}
}
EditorSprite : : EditorSprite ( wxBitmap * b16x16 , wxBitmap * b32x32 )
{
bm [ SPRITE_SIZE_16x16 ] = b16x16 ;
bm [ SPRITE_SIZE_32x32 ] = b32x32 ;
}
EditorSprite : : ~ EditorSprite ( )
{
unloadDC ( ) ;
}
void EditorSprite : : DrawTo ( wxDC * dc , SpriteSize sz , int start_x , int start_y , int width , int height )
{
wxBitmap * sp = bm [ sz ] ;
if ( sp )
dc - > DrawBitmap ( * sp , start_x , start_y , true ) ;
}
void EditorSprite : : unloadDC ( )
{
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 ) ,
2012-06-29 16:44:26 +02:00
draw_height ( 0 ) ,
drawoffset_x ( 0 ) ,
drawoffset_y ( 0 ) ,
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
}
2015-12-06 11:42:37 -03:00
GameSprite : : ~ GameSprite ( )
{
2012-06-29 16:44:26 +02:00
unloadDC ( ) ;
2015-12-06 11:42:37 -03:00
for ( std : : list < TemplateImage * > : : iterator iter = instanced_templates . begin ( ) ; iter ! = instanced_templates . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
delete * iter ;
}
2016-11-09 12:17:17 -03:00
delete animator ;
2012-06-29 16:44:26 +02:00
}
void GameSprite : : clean ( int time ) {
for ( std : : list < TemplateImage * > : : iterator iter = instanced_templates . begin ( ) ;
iter ! = instanced_templates . end ( ) ;
+ + iter )
{
( * iter ) - > clean ( time ) ;
}
}
2015-12-06 11:42:37 -03: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
}
2015-12-06 11:42:37 -03:00
int GameSprite : : getDrawHeight ( ) const
{
2012-06-29 16:44:26 +02:00
return draw_height ;
}
2015-12-06 11:42:37 -03:00
std : : pair < int , int > GameSprite : : getDrawOffset ( ) const
{
2012-06-29 16:44:26 +02:00
return std : : make_pair ( drawoffset_x , drawoffset_y ) ;
}
2015-12-06 11:42:37 -03:00
uint8_t GameSprite : : getMiniMapColor ( ) const
{
2012-06-29 16:44:26 +02:00
return minimap_color ;
}
2016-11-09 11:49:58 -03: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 ;
}
2015-12-06 11:42:37 -03: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 ;
2012-06-29 16:44:26 +02:00
if ( _count > = 0 & & height < = 1 & & width < = 1 ) {
v = _count ;
} else {
2015-02-03 22:59:14 -03: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
}
if ( v > = numsprites ) {
if ( numsprites = = 1 ) {
v = 0 ;
} else {
v % = numsprites ;
}
}
return spriteList [ v ] - > getHardwareID ( ) ;
}
2015-12-06 11:42:37 -03:00
GameSprite : : TemplateImage * GameSprite : : getTemplateImage ( int sprite_index , const Outfit & outfit )
{
2012-06-29 16:44:26 +02:00
if ( instanced_templates . empty ( ) ) {
TemplateImage * img = newd TemplateImage ( this , sprite_index , outfit ) ;
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.
2015-12-06 11:42:37 -03:00
for ( std : : list < TemplateImage * > : : iterator iter = instanced_templates . begin ( ) ; iter ! = instanced_templates . end ( ) ; + + iter ) {
2012-06-29 16:44:26 +02:00
TemplateImage * img = * iter ;
if ( img - > sprite_index = = sprite_index ) {
uint32_t lookHash = img - > lookHead < < 24 | img - > lookBody < < 16 | img - > lookLegs < < 8 | img - > lookFeet ;
if ( outfit . getColorHash ( ) = = lookHash ) {
return img ;
}
}
}
TemplateImage * img = newd TemplateImage ( this , sprite_index , outfit ) ;
instanced_templates . push_back ( img ) ;
return img ;
}
2021-06-06 20:05:01 -03:00
GLuint GameSprite : : getHardwareID ( int _x , int _y , int _dir , int _addon , int _pattern_z , const Outfit & _outfit , int _frame )
2015-12-06 11:42:37 -03:00
{
2021-06-06 20:05:01 -03:00
uint32_t v = getIndex ( _x , _y , 0 , _dir , _addon , _pattern_z , _frame ) ;
2012-06-29 16:44:26 +02:00
if ( v > = numsprites ) {
if ( numsprites = = 1 ) {
v = 0 ;
} else {
v % = numsprites ;
}
}
2015-02-03 22:59:14 -03:00
if ( layers > 1 ) { // Template
2012-06-29 16:44:26 +02:00
TemplateImage * img = getTemplateImage ( v , _outfit ) ;
return img - > getHardwareID ( ) ;
}
return spriteList [ v ] - > getHardwareID ( ) ;
}
2016-11-09 11:49:58 -03:00
wxMemoryDC * GameSprite : : getDC ( SpriteSize size )
2015-12-06 11:42:37 -03:00
{
2016-11-09 11:49:58 -03:00
ASSERT ( size = = SPRITE_SIZE_16x16 | | size = = SPRITE_SIZE_32x32 ) ;
if ( ! dc [ size ] ) {
ASSERT ( width > = 1 & & height > = 1 ) ;
2017-07-04 20:07:29 +02:00
const int bgshade = g_settings . getInteger ( Config : : ICON_BACKGROUND ) ;
2017-10-08 21:17:26 -03:00
int image_size = std : : max < int > ( width , height ) * SPRITE_PIXELS ;
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
for ( uint8_t l = 0 ; l < layers ; l + + ) {
for ( uint8_t w = 0 ; w < width ; w + + ) {
for ( uint8_t h = 0 ; h < height ; h + + ) {
const int i = getIndex ( w , h , l , 0 , 0 , 0 , 0 ) ;
uint8_t * data = spriteList [ i ] - > getRGBData ( ) ;
if ( data ) {
wxImage img ( SPRITE_PIXELS , SPRITE_PIXELS , data ) ;
img . SetMaskColour ( 0xFF , 0x00 , 0xFF ) ;
image . Paste ( img , ( width - w - 1 ) * SPRITE_PIXELS , ( height - h - 1 ) * SPRITE_PIXELS ) ;
img . Destroy ( ) ;
2012-06-29 16:44:26 +02:00
}
}
}
}
// Now comes the resizing / antialiasing
2016-11-09 11:49:58 -03:00
if ( size = = SPRITE_SIZE_16x16 | | image . GetWidth ( ) > SPRITE_PIXELS | | image . GetHeight ( ) > SPRITE_PIXELS ) {
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 ) ;
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
}
2015-12-06 11:42:37 -03:00
void GameSprite : : DrawTo ( wxDC * dc , SpriteSize sz , int start_x , int start_y , int width , int height )
{
2016-11-12 10:56:02 -03:00
if ( width = = - 1 ) width = sz = = SPRITE_SIZE_32x32 ? 32 : 16 ;
if ( height = = - 1 ) height = sz = = SPRITE_SIZE_32x32 ? 32 : 16 ;
2012-06-29 16:44:26 +02:00
wxDC * sdc = getDC ( sz ) ;
2015-12-06 11:42:37 -03:00
if ( sdc ) {
2012-06-29 16:44:26 +02:00
dc - > Blit ( start_x , start_y , width , height , sdc , 0 , 0 , wxCOPY , true ) ;
2015-12-06 11:42:37 -03:00
} else {
2012-06-29 16:44:26 +02:00
const wxBrush & b = dc - > GetBrush ( ) ;
dc - > SetBrush ( * wxRED_BRUSH ) ;
dc - > DrawRectangle ( start_x , start_y , width , height ) ;
dc - > SetBrush ( b ) ;
}
}
GameSprite : : Image : : Image ( ) :
isGLLoaded ( false ) ,
lastaccess ( 0 )
{
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2014-03-09 16:53:29 +01:00
GameSprite : : Image : : ~ Image ( )
{
2012-06-29 16:44:26 +02:00
unloadGLTexture ( 0 ) ;
}
2014-03-09 16:53:29 +01:00
void GameSprite : : Image : : createGLTexture ( GLuint whatid )
{
2012-06-29 16:44:26 +02:00
ASSERT ( ! isGLLoaded ) ;
uint8_t * rgba = getRGBAData ( ) ;
2015-12-06 11:42:37 -03: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
glBindTexture ( GL_TEXTURE_2D , whatid ) ;
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
2016-11-12 10:56:02 -03:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , SPRITE_PIXELS , SPRITE_PIXELS , 0 , GL_RGBA , GL_UNSIGNED_BYTE , rgba ) ;
2012-06-29 16:44:26 +02:00
delete [ ] rgba ;
# undef SPRITE_SIZE
}
2015-12-06 11:42:37 -03:00
void GameSprite : : Image : : unloadGLTexture ( GLuint whatid )
{
2012-06-29 16:44:26 +02:00
isGLLoaded = false ;
2016-09-29 20:30:54 -03:00
g_gui . gfx . loaded_textures - = 1 ;
2012-06-29 16:44:26 +02:00
glDeleteTextures ( 1 , & whatid ) ;
}
2015-12-06 11:42:37 -03:00
void GameSprite : : Image : : visit ( )
{
2014-01-22 20:16:17 +01:00
lastaccess = time ( nullptr ) ;
2012-06-29 16:44:26 +02:00
}
2015-12-06 11:42:37 -03:00
void GameSprite : : Image : : clean ( int time )
{
2016-09-29 20:37:43 -03:00
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 ) ,
2014-01-22 20:16:17 +01:00
dump ( nullptr )
2012-06-29 16:44:26 +02:00
{
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2015-12-06 11:42:37 -03:00
GameSprite : : NormalImage : : ~ NormalImage ( )
{
2012-06-29 16:44:26 +02:00
delete [ ] dump ;
}
2015-12-06 11:42:37 -03:00
void GameSprite : : NormalImage : : clean ( int time )
{
2012-06-29 16:44:26 +02:00
Image : : clean ( time ) ;
2016-09-29 20:37:43 -03:00
if ( time - lastaccess > 5 & & ! g_settings . getInteger ( Config : : USE_MEMCACHED_SPRITES ) ) { // We keep dumps around for 5 seconds.
2012-06-29 16:44:26 +02:00
delete [ ] dump ;
2014-01-22 20:16:17 +01:00
dump = nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2015-12-06 11:42:37 -03:00
uint8_t * GameSprite : : NormalImage : : getRGBData ( )
{
2016-11-09 11:49:58 -03:00
if ( ! dump ) {
2016-09-29 20:37:43 -03:00
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
}
2016-11-09 11:49:58 -03:00
if ( ! g_gui . gfx . loadSpriteDump ( dump , size , id ) ) {
return nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2016-11-09 11:49:58 -03:00
const int pixels_data_size = SPRITE_PIXELS * SPRITE_PIXELS * 3 ;
uint8_t * data = newd uint8_t [ pixels_data_size ] ;
uint8_t bpp = g_gui . gfx . hasTransparency ( ) ? 4 : 3 ;
int write = 0 ;
int read = 0 ;
// decompress pixels
while ( read < size & & write < pixels_data_size ) {
int transparent = dump [ read ] | dump [ read + 1 ] < < 8 ;
read + = 2 ;
for ( int i = 0 ; i < transparent & & write < pixels_data_size ; i + + ) {
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 ;
for ( int i = 0 ; i < colored & & write < pixels_data_size ; i + + ) {
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
while ( write < pixels_data_size ) {
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
}
2016-11-09 11:49:58 -03:00
2015-12-06 11:42:37 -03:00
uint8_t * GameSprite : : NormalImage : : getRGBAData ( )
{
2016-11-09 11:49:58 -03:00
if ( ! dump ) {
2016-09-29 20:37:43 -03:00
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
}
2016-11-09 11:49:58 -03:00
if ( ! g_gui . gfx . loadSpriteDump ( dump , size , id ) ) {
return nullptr ;
2012-06-29 16:44:26 +02:00
}
}
2017-05-06 12:31:26 -03:00
const int pixels_data_size = SPRITE_PIXELS_SIZE * 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
while ( read < size & & write < pixels_data_size ) {
int transparent = dump [ read ] | dump [ read + 1 ] < < 8 ;
2017-05-06 12:31:26 -03:00
if ( use_alpha & & transparent > = SPRITE_PIXELS_SIZE ) // Corrupted sprite?
2016-11-09 11:49:58 -03:00
break ;
read + = 2 ;
for ( int i = 0 ; i < transparent & & write < pixels_data_size ; i + + ) {
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 ;
for ( int i = 0 ; i < colored & & write < pixels_data_size ; i + + ) {
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
while ( write < pixels_data_size ) {
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
}
2015-12-06 11:42:37 -03:00
GLuint GameSprite : : NormalImage : : getHardwareID ( )
{
if ( ! isGLLoaded ) {
2012-06-29 16:44:26 +02:00
createGLTexture ( id ) ;
}
visit ( ) ;
return id ;
}
2015-12-06 11:42:37 -03:00
void GameSprite : : NormalImage : : createGLTexture ( GLuint ignored )
{
2012-06-29 16:44:26 +02:00
Image : : createGLTexture ( id ) ;
}
2015-12-06 11:42:37 -03:00
void GameSprite : : NormalImage : : unloadGLTexture ( GLuint ignored )
{
2012-06-29 16:44:26 +02:00
Image : : unloadGLTexture ( id ) ;
}
GameSprite : : TemplateImage : : TemplateImage ( GameSprite * parent , int v , const Outfit & outfit ) :
gl_tid ( 0 ) ,
parent ( parent ) ,
sprite_index ( v ) ,
lookHead ( outfit . lookHead ) ,
lookBody ( outfit . lookBody ) ,
lookLegs ( outfit . lookLegs ) ,
lookFeet ( outfit . lookFeet )
{
2015-12-06 11:42:37 -03:00
////
2012-06-29 16:44:26 +02:00
}
2015-12-06 11:42:37 -03:00
GameSprite : : TemplateImage : : ~ TemplateImage ( )
{
////
2012-06-29 16:44:26 +02:00
}
void GameSprite : : TemplateImage : : colorizePixel ( uint8_t color , uint8_t & red , uint8_t & green , uint8_t & blue ) {
// 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 ) ) ;
}
2015-12-06 11:42:37 -03: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 ( ) ;
if ( ! rgbdata ) {
delete [ ] template_rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
if ( ! template_rgbdata ) {
delete [ ] rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
if ( lookHead > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookHead = 0 ;
}
if ( lookBody > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookBody = 0 ;
}
if ( lookLegs > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookLegs = 0 ;
}
if ( lookFeet > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookFeet = 0 ;
}
2016-11-12 10:56:02 -03:00
for ( int y = 0 ; y < SPRITE_PIXELS ; + + y ) {
for ( int x = 0 ; x < SPRITE_PIXELS ; + + x ) {
uint8_t & red = rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 0 ] ;
uint8_t & green = rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 1 ] ;
uint8_t & blue = rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
2016-11-12 10:56:02 -03:00
uint8_t & tred = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 0 ] ;
uint8_t & tgreen = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 1 ] ;
uint8_t & tblue = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
if ( tred & & tgreen & & ! tblue ) { // yellow => head
colorizePixel ( lookHead , red , green , blue ) ;
} else if ( tred & & ! tgreen & & ! tblue ) { // red => body
colorizePixel ( lookBody , red , green , blue ) ;
} else if ( ! tred & & tgreen & & ! tblue ) { // green => legs
colorizePixel ( lookLegs , red , green , blue ) ;
} else if ( ! tred & & ! tgreen & & tblue ) { // blue => feet
colorizePixel ( lookFeet , red , green , blue ) ;
}
}
}
delete [ ] template_rgbdata ;
return rgbdata ;
}
2015-12-06 11:42:37 -03: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 ( ) ;
if ( ! rgbadata ) {
delete [ ] template_rgbdata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
if ( ! template_rgbdata ) {
delete [ ] rgbadata ;
2014-01-22 20:16:17 +01:00
return nullptr ;
2012-06-29 16:44:26 +02:00
}
if ( lookHead > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookHead = 0 ;
}
if ( lookBody > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookBody = 0 ;
}
if ( lookLegs > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookLegs = 0 ;
}
if ( lookFeet > ( sizeof ( TemplateOutfitLookupTable ) / sizeof ( TemplateOutfitLookupTable [ 0 ] ) ) ) {
lookFeet = 0 ;
}
2016-11-12 10:56:02 -03:00
for ( int y = 0 ; y < SPRITE_PIXELS ; + + y ) {
for ( int x = 0 ; x < SPRITE_PIXELS ; + + x ) {
uint8_t & red = rgbadata [ y * SPRITE_PIXELS * 4 + x * 4 + 0 ] ;
uint8_t & green = rgbadata [ y * SPRITE_PIXELS * 4 + x * 4 + 1 ] ;
uint8_t & blue = rgbadata [ y * SPRITE_PIXELS * 4 + x * 4 + 2 ] ;
2012-06-29 16:44:26 +02:00
2016-11-12 10:56:02 -03:00
uint8_t & tred = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 0 ] ;
uint8_t & tgreen = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 1 ] ;
uint8_t & tblue = template_rgbdata [ y * SPRITE_PIXELS * 3 + x * 3 + 2 ] ;
2012-06-29 16:44:26 +02:00
if ( tred & & tgreen & & ! tblue ) { // yellow => head
colorizePixel ( lookHead , red , green , blue ) ;
} else if ( tred & & ! tgreen & & ! tblue ) { // red => body
colorizePixel ( lookBody , red , green , blue ) ;
} else if ( ! tred & & tgreen & & ! tblue ) { // green => legs
colorizePixel ( lookLegs , red , green , blue ) ;
} else if ( ! tred & & ! tgreen & & tblue ) { // blue => feet
colorizePixel ( lookFeet , red , green , blue ) ;
}
}
}
delete [ ] template_rgbdata ;
return rgbadata ;
}
2015-12-06 11:42:37 -03:00
GLuint GameSprite : : TemplateImage : : getHardwareID ( )
{
if ( ! isGLLoaded ) {
2012-06-29 16:44:26 +02:00
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 ) ;
if ( ! isGLLoaded ) {
return 0 ;
}
}
visit ( ) ;
return gl_tid ;
}
2015-12-06 11:42:37 -03:00
void GameSprite : : TemplateImage : : createGLTexture ( GLuint unused )
{
2012-06-29 16:44:26 +02:00
Image : : createGLTexture ( gl_tid ) ;
}
2015-12-06 11:42:37 -03: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
// ============================================================================
// 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 ) ,
is_complete ( false )
{
ASSERT ( start_frame > = - 1 & & start_frame < frame_count ) ;
for ( int i = 0 ; i < frame_count ; i + + ) {
durations . push_back ( newd FrameDuration ( ITEM_FRAME_DURATION , ITEM_FRAME_DURATION ) ) ;
}
reset ( ) ;
}
Animator : : ~ Animator ( )
{
for ( int i = 0 ; i < frame_count ; i + + ) {
delete durations [ i ] ;
}
durations . clear ( ) ;
}
int Animator : : getStartFrame ( ) const
{
if ( start_frame > - 1 )
return start_frame ;
return uniform_random ( 0 , frame_count - 1 ) ;
}
FrameDuration * Animator : : getFrameDuration ( int frame )
{
ASSERT ( frame > = 0 & & frame < frame_count ) ;
return durations [ frame ] ;
}
int Animator : : getFrame ( )
{
long time = g_gui . gfx . getElapsedTime ( ) ;
if ( time ! = last_time & & ! is_complete ) {
long elapsed = time - last_time ;
if ( elapsed > = current_duration ) {
int frame = 0 ;
if ( loop_count < 0 )
frame = getPingPongFrame ( ) ;
else
frame = getLoopFrame ( ) ;
if ( current_frame ! = frame ) {
int duration = getDuration ( frame ) - ( elapsed - current_duration ) ;
if ( duration < 0 & & ! async ) {
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 ;
}
void Animator : : setFrame ( int frame )
{
ASSERT ( frame = = - 1 | | frame = = 255 | | frame = = 254 | | ( frame > = 0 & & frame < frame_count ) ) ;
if ( current_frame = = frame )
return ;
if ( async ) {
if ( frame = = 255 ) // Async mode
current_frame = 0 ;
else if ( frame = = 254 ) // Random mode
current_frame = uniform_random ( 0 , frame_count - 1 ) ;
else if ( frame > = 0 & & frame < frame_count )
current_frame = frame ;
else
current_frame = getStartFrame ( ) ;
is_complete = false ;
last_time = g_gui . gfx . getElapsedTime ( ) ;
current_duration = getDuration ( current_frame ) ;
current_loop = 0 ;
} else {
calculateSynchronous ( ) ;
}
}
void Animator : : reset ( )
{
total_duration = 0 ;
for ( int i = 0 ; i < frame_count ; i + + )
total_duration + = durations [ i ] - > max ;
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 ) ;
}
int Animator : : getDuration ( int frame ) const
{
ASSERT ( frame > = 0 & & frame < frame_count ) ;
return durations [ frame ] - > getDuration ( ) ;
}
int Animator : : getPingPongFrame ( )
{
int count = direction = = ANIMATION_FORWARD ? 1 : - 1 ;
int next_frame = current_frame + count ;
if ( next_frame < 0 | | next_frame > = frame_count ) {
direction = direction = = ANIMATION_FORWARD ? ANIMATION_BACKWARD : ANIMATION_FORWARD ;
count * = - 1 ;
}
return current_frame + count ;
}
int Animator : : getLoopFrame ( )
{
int next_phase = current_frame + 1 ;
if ( next_phase < frame_count )
return next_phase ;
if ( loop_count = = 0 )
return 0 ;
if ( current_loop < ( loop_count - 1 ) ) {
current_loop + + ;
return 0 ;
}
return current_frame ;
}
void Animator : : calculateSynchronous ( )
{
long time = g_gui . gfx . getElapsedTime ( ) ;
if ( time > 0 & & total_duration > 0 ) {
long elapsed = time % total_duration ;
int total_time = 0 ;
for ( int i = 0 ; i < frame_count ; i + + ) {
int duration = getDuration ( i ) ;
if ( elapsed > = total_time & & elapsed < total_time + duration ) {
current_frame = i ;
current_duration = duration - ( elapsed - total_time ) ;
break ;
}
total_time + = duration ;
}
last_time = time ;
}
}