2018-09-27 00:33:08 +01:00
# include "project.h"
2018-12-26 18:20:51 +00:00
# include "config.h"
2018-09-27 00:33:08 +01:00
# include "history.h"
2018-12-20 23:30:35 +00:00
# include "log.h"
2018-09-27 00:33:08 +01:00
# include "parseutil.h"
2019-04-07 01:58:38 +01:00
# include "paletteutil.h"
2018-09-27 00:33:08 +01:00
# include "tile.h"
# include "tileset.h"
2020-03-04 14:15:00 +00:00
# include "imageexport.h"
2020-03-13 06:23:47 +00:00
# include "map.h"
2018-09-27 00:33:08 +01:00
2020-03-06 03:46:25 +00:00
# include "orderedjson.h"
2018-09-27 00:33:08 +01:00
# include <QDir>
2019-02-01 17:43:25 +00:00
# include <QJsonArray>
# include <QJsonDocument>
# include <QJsonObject>
# include <QJsonValue>
2018-09-27 00:33:08 +01:00
# include <QFile>
# include <QTextStream>
# include <QStandardItem>
# include <QMessageBox>
# include <QRegularExpression>
2019-10-01 00:54:27 +01:00
# include <algorithm>
2018-09-27 00:33:08 +01:00
2020-03-06 03:46:25 +00:00
using OrderedJson = poryjson : : Json ;
using OrderedJsonDoc = poryjson : : JsonDoc ;
2018-09-27 00:33:08 +01:00
int Project : : num_tiles_primary = 512 ;
int Project : : num_tiles_total = 1024 ;
int Project : : num_metatiles_primary = 512 ;
int Project : : num_metatiles_total = 1024 ;
int Project : : num_pals_primary = 6 ;
int Project : : num_pals_total = 13 ;
2020-05-16 21:57:03 +01:00
int Project : : max_map_data_size = 10240 ; // 0x2800
int Project : : default_map_size = 20 ;
2020-07-10 21:34:42 +01:00
int Project : : max_object_events = 64 ;
2018-09-27 00:33:08 +01:00
2021-04-16 14:04:38 +01:00
Project : : Project ( QWidget * parent ) :
QObject ( parent ) ,
eventScriptLabelModel ( this ) ,
eventScriptLabelCompleter ( this )
2018-09-27 00:33:08 +01:00
{
2020-04-12 01:39:50 +01:00
initSignals ( ) ;
2020-04-07 19:20:31 +01:00
}
Project : : ~ Project ( )
{
2020-04-08 01:25:09 +01:00
clearMapCache ( ) ;
clearTilesetCache ( ) ;
2018-09-27 00:33:08 +01:00
}
2020-04-12 01:39:50 +01:00
void Project : : initSignals ( ) {
// detect changes to specific filepaths being monitored
QObject : : connect ( & fileWatcher , & QFileSystemWatcher : : fileChanged , [ this ] ( QString changed ) {
2020-04-25 22:11:14 +01:00
if ( ! porymapConfig . getMonitorFiles ( ) ) return ;
if ( modifiedFileTimestamps . contains ( changed ) ) {
if ( QDateTime : : currentMSecsSinceEpoch ( ) < modifiedFileTimestamps [ changed ] ) {
return ;
}
modifiedFileTimestamps . remove ( changed ) ;
}
2020-04-21 03:02:14 +01:00
static bool showing = false ;
if ( showing ) return ;
2021-02-15 16:43:18 +00:00
QMessageBox notice ( this - > parentWidget ( ) ) ;
2020-04-12 01:39:50 +01:00
notice . setText ( " File Changed " ) ;
notice . setInformativeText ( QString ( " The file %1 has changed on disk. Would you like to reload the project? " )
. arg ( changed . remove ( this - > root + " / " ) ) ) ;
notice . setStandardButtons ( QMessageBox : : No | QMessageBox : : Yes ) ;
notice . setIcon ( QMessageBox : : Question ) ;
QCheckBox showAgainCheck ( " Do not ask again. " ) ;
notice . setCheckBox ( & showAgainCheck ) ;
2020-04-21 03:02:14 +01:00
showing = true ;
2020-04-12 01:39:50 +01:00
int choice = notice . exec ( ) ;
if ( choice = = QMessageBox : : Yes ) {
emit reloadProject ( ) ;
} else if ( choice = = QMessageBox : : No ) {
if ( showAgainCheck . isChecked ( ) ) {
porymapConfig . setMonitorFiles ( false ) ;
2020-04-21 02:54:16 +01:00
emit uncheckMonitorFilesAction ( ) ;
2020-04-12 01:39:50 +01:00
}
}
2020-04-21 03:02:14 +01:00
showing = false ;
2020-04-12 01:39:50 +01:00
} ) ;
}
2019-05-06 17:42:09 +01:00
void Project : : set_root ( QString dir ) {
this - > root = dir ;
this - > parser . set_root ( dir ) ;
}
2018-09-27 00:33:08 +01:00
QString Project : : getProjectTitle ( ) {
if ( ! root . isNull ( ) ) {
return root . section ( ' / ' , - 1 ) ;
} else {
return QString ( ) ;
}
}
2020-04-08 01:25:09 +01:00
void Project : : clearMapCache ( ) {
2021-02-18 22:41:36 +00:00
for ( auto * map : mapCache . values ( ) ) {
if ( map )
delete map ;
2020-04-08 01:25:09 +01:00
}
2021-02-18 22:41:36 +00:00
mapCache . clear ( ) ;
2020-06-06 14:51:36 +01:00
emit mapCacheCleared ( ) ;
2020-04-08 01:25:09 +01:00
}
void Project : : clearTilesetCache ( ) {
2021-02-18 22:41:36 +00:00
for ( auto * tileset : tilesetCache . values ( ) ) {
if ( tileset )
delete tileset ;
2020-04-08 01:25:09 +01:00
}
2021-02-18 22:41:36 +00:00
tilesetCache . clear ( ) ;
2020-04-08 01:25:09 +01:00
}
2018-09-27 00:33:08 +01:00
Map * Project : : loadMap ( QString map_name ) {
Map * map ;
2021-02-15 16:33:30 +00:00
if ( mapCache . contains ( map_name ) ) {
map = mapCache . value ( map_name ) ;
2018-09-27 00:33:08 +01:00
// TODO: uncomment when undo/redo history is fully implemented for all actions.
if ( true /*map->hasUnsavedChanges()*/ ) {
return map ;
}
} else {
map = new Map ;
map - > setName ( map_name ) ;
}
2020-02-12 16:22:40 +00:00
if ( ! ( loadMapData ( map ) & & loadMapLayout ( map ) ) )
2018-12-20 23:30:35 +00:00
return nullptr ;
2021-02-15 16:33:30 +00:00
mapCache . insert ( map_name , map ) ;
2018-09-27 00:33:08 +01:00
return map ;
}
void Project : : setNewMapConnections ( Map * map ) {
map - > connections . clear ( ) ;
}
2020-05-22 22:51:56 +01:00
static QMap < QString , bool > defaultTopLevelMapFields {
{ " id " , true } ,
{ " name " , true } ,
{ " layout " , true } ,
{ " music " , true } ,
{ " region_map_section " , true } ,
{ " requires_flash " , true } ,
{ " weather " , true } ,
{ " map_type " , true } ,
{ " show_map_name " , true } ,
{ " battle_scene " , true } ,
{ " connections " , true } ,
{ " object_events " , true } ,
{ " warp_events " , true } ,
{ " coord_events " , true } ,
{ " bg_events " , true } ,
{ " shared_events_map " , true } ,
{ " shared_scripts_map " , true } ,
} ;
2019-02-03 16:38:45 +00:00
QMap < QString , bool > Project : : getTopLevelMapFields ( ) {
2020-05-22 22:51:56 +01:00
QMap < QString , bool > topLevelMapFields = defaultTopLevelMapFields ;
if ( projectConfig . getBaseGameVersion ( ) ! = BaseGameVersion : : pokeruby ) {
topLevelMapFields . insert ( " allow_cycling " , true ) ;
topLevelMapFields . insert ( " allow_escaping " , true ) ;
topLevelMapFields . insert ( " allow_running " , true ) ;
2019-02-03 16:38:45 +00:00
}
2021-02-16 12:15:47 +00:00
2020-05-22 22:51:56 +01:00
if ( projectConfig . getFloorNumberEnabled ( ) ) {
topLevelMapFields . insert ( " floor_number " , true ) ;
}
return topLevelMapFields ;
2019-02-03 16:38:45 +00:00
}
2019-02-01 17:43:25 +00:00
bool Project : : loadMapData ( Map * map ) {
2018-09-27 00:33:08 +01:00
if ( ! map - > isPersistedToFile ) {
2018-12-20 23:30:35 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
QString mapFilepath = QString ( " %1/data/maps/%2/map.json " ) . arg ( root ) . arg ( map - > name ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument mapDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & mapDoc , mapFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map data from %1 " ) . arg ( mapFilepath ) ) ;
2018-12-20 23:30:35 +00:00
return false ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
QJsonObject mapObj = mapDoc . object ( ) ;
map - > song = mapObj [ " music " ] . toString ( ) ;
map - > layoutId = mapObj [ " layout " ] . toString ( ) ;
map - > location = mapObj [ " region_map_section " ] . toString ( ) ;
map - > requiresFlash = QString : : number ( mapObj [ " requires_flash " ] . toBool ( ) ) ;
map - > weather = mapObj [ " weather " ] . toString ( ) ;
map - > type = mapObj [ " map_type " ] . toString ( ) ;
map - > requiresFlash = QString : : number ( mapObj [ " requires_flash " ] . toBool ( ) ) ;
map - > show_location = QString : : number ( mapObj [ " show_map_name " ] . toBool ( ) ) ;
map - > battle_scene = mapObj [ " battle_scene " ] . toString ( ) ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getBaseGameVersion ( ) ! = BaseGameVersion : : pokeruby ) {
2020-03-11 05:52:00 +00:00
map - > allowBiking = QString : : number ( mapObj [ " allow_cycling " ] . toBool ( ) ) ;
map - > allowEscapeRope = QString : : number ( mapObj [ " allow_escaping " ] . toBool ( ) ) ;
2019-02-01 17:43:25 +00:00
map - > allowRunning = QString : : number ( mapObj [ " allow_running " ] . toBool ( ) ) ;
2020-05-22 22:51:56 +01:00
}
if ( projectConfig . getFloorNumberEnabled ( ) ) {
2020-03-11 05:52:00 +00:00
map - > floorNumber = mapObj [ " floor_number " ] . toInt ( ) ;
2019-02-01 17:43:25 +00:00
}
2019-02-02 20:56:05 +00:00
map - > sharedEventsMap = mapObj [ " shared_events_map " ] . toString ( ) ;
map - > sharedScriptsMap = mapObj [ " shared_scripts_map " ] . toString ( ) ;
2019-02-01 17:43:25 +00:00
// Events
map - > events [ " object_event_group " ] . clear ( ) ;
QJsonArray objectEventsArr = mapObj [ " object_events " ] . toArray ( ) ;
for ( int i = 0 ; i < objectEventsArr . size ( ) ; i + + ) {
QJsonObject event = objectEventsArr [ i ] . toObject ( ) ;
2019-02-03 18:26:27 +00:00
Event * object = new Event ( event , EventType : : Object ) ;
2019-02-01 17:43:25 +00:00
object - > put ( " map_name " , map - > name ) ;
object - > put ( " sprite " , event [ " graphics_id " ] . toString ( ) ) ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getObjectEventInConnectionEnabled ( ) ) {
2020-03-11 20:45:52 +00:00
object - > put ( " in_connection " , event [ " in_connection " ] . toBool ( ) ) ;
}
2019-02-01 17:43:25 +00:00
object - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
object - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
object - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
object - > put ( " movement_type " , event [ " movement_type " ] . toString ( ) ) ;
object - > put ( " radius_x " , QString : : number ( event [ " movement_range_x " ] . toInt ( ) ) ) ;
object - > put ( " radius_y " , QString : : number ( event [ " movement_range_y " ] . toInt ( ) ) ) ;
2019-02-16 22:50:39 +00:00
object - > put ( " trainer_type " , event [ " trainer_type " ] . toString ( ) ) ;
object - > put ( " sight_radius_tree_id " , event [ " trainer_sight_or_berry_tree_id " ] . toString ( ) ) ;
2019-02-01 17:43:25 +00:00
object - > put ( " script_label " , event [ " script " ] . toString ( ) ) ;
object - > put ( " event_flag " , event [ " flag " ] . toString ( ) ) ;
object - > put ( " event_group_type " , " object_event_group " ) ;
map - > events [ " object_event_group " ] . append ( object ) ;
}
map - > events [ " warp_event_group " ] . clear ( ) ;
QJsonArray warpEventsArr = mapObj [ " warp_events " ] . toArray ( ) ;
for ( int i = 0 ; i < warpEventsArr . size ( ) ; i + + ) {
QJsonObject event = warpEventsArr [ i ] . toObject ( ) ;
2019-02-03 18:26:27 +00:00
Event * warp = new Event ( event , EventType : : Warp ) ;
2019-02-01 17:43:25 +00:00
warp - > put ( " map_name " , map - > name ) ;
warp - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
warp - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
warp - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
warp - > put ( " destination_warp " , QString : : number ( event [ " dest_warp_id " ] . toInt ( ) ) ) ;
// Ensure the warp destination map constant is valid before adding it to the warps.
QString mapConstant = event [ " dest_map " ] . toString ( ) ;
2021-02-15 09:21:41 +00:00
if ( mapConstantsToMapNames . contains ( mapConstant ) ) {
warp - > put ( " destination_map_name " , mapConstantsToMapNames . value ( mapConstant ) ) ;
2019-02-01 17:43:25 +00:00
warp - > put ( " event_group_type " , " warp_event_group " ) ;
map - > events [ " warp_event_group " ] . append ( warp ) ;
} else if ( mapConstant = = NONE_MAP_CONSTANT ) {
warp - > put ( " destination_map_name " , NONE_MAP_NAME ) ;
warp - > put ( " event_group_type " , " warp_event_group " ) ;
map - > events [ " warp_event_group " ] . append ( warp ) ;
2018-12-26 18:20:51 +00:00
} else {
2019-02-01 17:43:25 +00:00
logError ( QString ( " Destination map constant '%1' is invalid for warp " ) . arg ( mapConstant ) ) ;
2018-12-26 18:20:51 +00:00
}
2019-02-01 17:43:25 +00:00
}
2018-12-26 18:20:51 +00:00
2019-02-01 17:43:25 +00:00
map - > events [ " heal_event_group " ] . clear ( ) ;
2020-04-29 17:34:49 +01:00
for ( auto it = healLocations . begin ( ) ; it ! = healLocations . end ( ) ; it + + ) {
2019-02-01 17:43:25 +00:00
HealLocation loc = * it ;
//if TRUE map is flyable / has healing location
2021-02-15 09:21:41 +00:00
if ( loc . mapName = = QString ( mapNamesToMapConstants . value ( map - > name ) ) . remove ( 0 , 4 ) ) {
2019-02-01 17:43:25 +00:00
Event * heal = new Event ;
heal - > put ( " map_name " , map - > name ) ;
heal - > put ( " x " , loc . x ) ;
heal - > put ( " y " , loc . y ) ;
2020-03-20 07:09:48 +00:00
heal - > put ( " loc_name " , loc . mapName ) ;
heal - > put ( " id_name " , loc . idName ) ;
2019-02-01 17:43:25 +00:00
heal - > put ( " index " , loc . index ) ;
heal - > put ( " elevation " , 3 ) ; // TODO: change this?
2021-02-15 09:21:41 +00:00
heal - > put ( " destination_map_name " , mapConstantsToMapNames . value ( map - > name ) ) ;
2019-02-01 17:43:25 +00:00
heal - > put ( " event_group_type " , " heal_event_group " ) ;
heal - > put ( " event_type " , EventType : : HealLocation ) ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getHealLocationRespawnDataEnabled ( ) ) {
2021-02-15 09:21:41 +00:00
heal - > put ( " respawn_map " , mapConstantsToMapNames . value ( QString ( " MAP_ " + loc . respawnMap ) ) ) ;
2020-03-20 07:09:48 +00:00
heal - > put ( " respawn_npc " , loc . respawnNPC ) ;
}
2019-02-01 17:43:25 +00:00
map - > events [ " heal_event_group " ] . append ( heal ) ;
2018-12-26 18:20:51 +00:00
}
2019-02-01 17:43:25 +00:00
}
map - > events [ " coord_event_group " ] . clear ( ) ;
QJsonArray coordEventsArr = mapObj [ " coord_events " ] . toArray ( ) ;
for ( int i = 0 ; i < coordEventsArr . size ( ) ; i + + ) {
QJsonObject event = coordEventsArr [ i ] . toObject ( ) ;
QString type = event [ " type " ] . toString ( ) ;
if ( type = = " trigger " ) {
2019-02-03 18:26:27 +00:00
Event * coord = new Event ( event , EventType : : Trigger ) ;
2019-02-01 17:43:25 +00:00
coord - > put ( " map_name " , map - > name ) ;
coord - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
coord - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
coord - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
coord - > put ( " script_var " , event [ " var " ] . toString ( ) ) ;
2019-02-16 20:04:20 +00:00
coord - > put ( " script_var_value " , event [ " var_value " ] . toString ( ) ) ;
2019-02-01 17:43:25 +00:00
coord - > put ( " script_label " , event [ " script " ] . toString ( ) ) ;
coord - > put ( " event_group_type " , " coord_event_group " ) ;
map - > events [ " coord_event_group " ] . append ( coord ) ;
} else if ( type = = " weather " ) {
2019-02-03 18:26:27 +00:00
Event * coord = new Event ( event , EventType : : WeatherTrigger ) ;
2019-02-01 17:43:25 +00:00
coord - > put ( " map_name " , map - > name ) ;
coord - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
coord - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
coord - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
coord - > put ( " weather " , event [ " weather " ] . toString ( ) ) ;
coord - > put ( " event_group_type " , " coord_event_group " ) ;
coord - > put ( " event_type " , EventType : : WeatherTrigger ) ;
map - > events [ " coord_event_group " ] . append ( coord ) ;
2020-02-12 16:43:17 +00:00
} else {
logError ( QString ( " Map %1 coord_event %2 has invalid type '%3'. Must be 'trigger' or 'weather'. " ) . arg ( map - > name ) . arg ( i ) . arg ( type ) ) ;
2018-12-26 18:20:51 +00:00
}
2019-02-01 17:43:25 +00:00
}
2018-12-26 18:20:51 +00:00
2019-02-01 17:43:25 +00:00
map - > events [ " bg_event_group " ] . clear ( ) ;
QJsonArray bgEventsArr = mapObj [ " bg_events " ] . toArray ( ) ;
for ( int i = 0 ; i < bgEventsArr . size ( ) ; i + + ) {
QJsonObject event = bgEventsArr [ i ] . toObject ( ) ;
QString type = event [ " type " ] . toString ( ) ;
if ( type = = " sign " ) {
2019-02-03 18:26:27 +00:00
Event * bg = new Event ( event , EventType : : Sign ) ;
2019-02-01 17:43:25 +00:00
bg - > put ( " map_name " , map - > name ) ;
bg - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
bg - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
bg - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
bg - > put ( " player_facing_direction " , event [ " player_facing_dir " ] . toString ( ) ) ;
bg - > put ( " script_label " , event [ " script " ] . toString ( ) ) ;
bg - > put ( " event_group_type " , " bg_event_group " ) ;
map - > events [ " bg_event_group " ] . append ( bg ) ;
} else if ( type = = " hidden_item " ) {
2019-02-03 18:26:27 +00:00
Event * bg = new Event ( event , EventType : : HiddenItem ) ;
2019-02-01 17:43:25 +00:00
bg - > put ( " map_name " , map - > name ) ;
bg - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
bg - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
bg - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
bg - > put ( " item " , event [ " item " ] . toString ( ) ) ;
bg - > put ( " flag " , event [ " flag " ] . toString ( ) ) ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getHiddenItemQuantityEnabled ( ) ) {
2020-03-11 20:23:07 +00:00
bg - > put ( " quantity " , event [ " quantity " ] . toInt ( ) ) ;
2020-05-22 22:51:56 +01:00
}
if ( projectConfig . getHiddenItemRequiresItemfinderEnabled ( ) ) {
2020-03-11 20:23:07 +00:00
bg - > put ( " underfoot " , event [ " underfoot " ] . toBool ( ) ) ;
}
2019-02-01 17:43:25 +00:00
bg - > put ( " event_group_type " , " bg_event_group " ) ;
map - > events [ " bg_event_group " ] . append ( bg ) ;
} else if ( type = = " secret_base " ) {
2019-02-03 18:26:27 +00:00
Event * bg = new Event ( event , EventType : : SecretBase ) ;
2019-02-01 17:43:25 +00:00
bg - > put ( " map_name " , map - > name ) ;
bg - > put ( " x " , QString : : number ( event [ " x " ] . toInt ( ) ) ) ;
bg - > put ( " y " , QString : : number ( event [ " y " ] . toInt ( ) ) ) ;
bg - > put ( " elevation " , QString : : number ( event [ " elevation " ] . toInt ( ) ) ) ;
bg - > put ( " secret_base_id " , event [ " secret_base_id " ] . toString ( ) ) ;
bg - > put ( " event_group_type " , " bg_event_group " ) ;
map - > events [ " bg_event_group " ] . append ( bg ) ;
2020-02-12 16:43:17 +00:00
} else {
logError ( QString ( " Map %1 bg_event %2 has invalid type '%3'. Must be 'sign', 'hidden_item', or 'secret_base'. " ) . arg ( map - > name ) . arg ( i ) . arg ( type ) ) ;
2018-12-26 18:20:51 +00:00
}
2019-02-01 17:43:25 +00:00
}
2018-12-26 18:20:51 +00:00
2019-02-01 17:43:25 +00:00
map - > connections . clear ( ) ;
QJsonArray connectionsArr = mapObj [ " connections " ] . toArray ( ) ;
if ( ! connectionsArr . isEmpty ( ) ) {
for ( int i = 0 ; i < connectionsArr . size ( ) ; i + + ) {
QJsonObject connectionObj = connectionsArr [ i ] . toObject ( ) ;
MapConnection * connection = new MapConnection ;
connection - > direction = connectionObj [ " direction " ] . toString ( ) ;
connection - > offset = QString : : number ( connectionObj [ " offset " ] . toInt ( ) ) ;
QString mapConstant = connectionObj [ " map " ] . toString ( ) ;
2021-02-15 09:21:41 +00:00
if ( mapConstantsToMapNames . contains ( mapConstant ) ) {
connection - > map_name = mapConstantsToMapNames . value ( mapConstant ) ;
2019-02-01 17:43:25 +00:00
map - > connections . append ( connection ) ;
} else {
logError ( QString ( " Failed to find connected map for map constant '%1' " ) . arg ( mapConstant ) ) ;
}
}
2018-12-26 18:20:51 +00:00
}
2019-02-03 16:38:45 +00:00
// Check for custom fields
QMap < QString , bool > baseFields = this - > getTopLevelMapFields ( ) ;
for ( QString key : mapObj . keys ( ) ) {
if ( ! baseFields . contains ( key ) ) {
map - > customHeaders . insert ( key , mapObj [ key ] . toString ( ) ) ;
}
}
2018-12-20 23:30:35 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2018-10-05 07:02:40 +01:00
QString Project : : readMapLayoutId ( QString map_name ) {
2021-02-15 16:33:30 +00:00
if ( mapCache . contains ( map_name ) ) {
return mapCache . value ( map_name ) - > layoutId ;
2018-10-05 07:02:40 +01:00
}
2019-02-01 17:43:25 +00:00
QString mapFilepath = QString ( " %1/data/maps/%2/map.json " ) . arg ( root ) . arg ( map_name ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument mapDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & mapDoc , mapFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map layout id from %1 " ) . arg ( mapFilepath ) ) ;
2019-09-09 23:24:30 +01:00
return QString ( ) ;
2018-10-05 07:02:40 +01:00
}
2019-02-01 17:43:25 +00:00
QJsonObject mapObj = mapDoc . object ( ) ;
return mapObj [ " layout " ] . toString ( ) ;
2018-10-05 07:02:40 +01:00
}
QString Project : : readMapLocation ( QString map_name ) {
2021-02-15 16:33:30 +00:00
if ( mapCache . contains ( map_name ) ) {
return mapCache . value ( map_name ) - > location ;
2018-10-05 07:02:40 +01:00
}
2019-02-01 17:43:25 +00:00
QString mapFilepath = QString ( " %1/data/maps/%2/map.json " ) . arg ( root ) . arg ( map_name ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument mapDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & mapDoc , mapFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map's region map section from %1 " ) . arg ( mapFilepath ) ) ;
2019-09-09 23:24:30 +01:00
return QString ( ) ;
2018-10-05 07:02:40 +01:00
}
2019-02-01 17:43:25 +00:00
QJsonObject mapObj = mapDoc . object ( ) ;
return mapObj [ " region_map_section " ] . toString ( ) ;
2018-10-05 07:02:40 +01:00
}
2018-09-27 00:33:08 +01:00
void Project : : setNewMapHeader ( Map * map , int mapIndex ) {
2019-02-01 17:43:25 +00:00
map - > layoutId = QString ( " %1 " ) . arg ( mapIndex ) ;
2020-03-16 07:57:39 +00:00
map - > location = mapSectionValueToName . value ( 0 ) ;
2018-09-27 00:33:08 +01:00
map - > requiresFlash = " FALSE " ;
2021-02-15 16:33:30 +00:00
map - > weather = weatherNames . value ( 0 , " WEATHER_NONE " ) ;
map - > type = mapTypes . value ( 0 , " MAP_TYPE_NONE " ) ;
2020-03-16 07:57:39 +00:00
map - > song = defaultSong ;
2018-12-26 20:15:35 +00:00
if ( projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokeruby ) {
map - > show_location = " TRUE " ;
2020-05-22 22:51:56 +01:00
} else {
2020-03-11 05:52:00 +00:00
map - > allowBiking = " 1 " ;
map - > allowEscapeRope = " 0 " ;
map - > allowRunning = " 1 " ;
map - > show_location = " 1 " ;
2020-05-22 22:51:56 +01:00
}
if ( projectConfig . getFloorNumberEnabled ( ) ) {
2020-03-11 05:52:00 +00:00
map - > floorNumber = 0 ;
2018-12-26 20:15:35 +00:00
}
2021-02-15 16:33:30 +00:00
map - > battle_scene = mapBattleScenes . value ( 0 , " MAP_BATTLE_SCENE_NORMAL " ) ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 16:22:40 +00:00
bool Project : : loadMapLayout ( Map * map ) {
2018-09-27 00:33:08 +01:00
if ( ! map - > isPersistedToFile ) {
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 16:22:40 +00:00
if ( mapLayouts . contains ( map - > layoutId ) ) {
2019-02-01 17:43:25 +00:00
map - > layout = mapLayouts [ map - > layoutId ] ;
2020-02-12 16:22:40 +00:00
} else {
logError ( QString ( " Error: Map '%1' has an unknown layout '%2' " ) . arg ( map - > name ) . arg ( map - > layoutId ) ) ;
return false ;
2018-09-27 00:33:08 +01:00
}
2020-04-04 19:14:16 +01:00
// Force these to run even if one fails
bool loadedTilesets = loadMapTilesets ( map ) ;
bool loadedBlockdata = loadBlockdata ( map ) ;
bool loadedBorder = loadMapBorder ( map ) ;
return loadedTilesets
& & loadedBlockdata
& & loadedBorder ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 00:34:08 +00:00
bool Project : : readMapLayouts ( ) {
2018-09-27 00:33:08 +01:00
mapLayouts . clear ( ) ;
2019-02-01 17:43:25 +00:00
mapLayoutsTable . clear ( ) ;
2018-09-27 00:33:08 +01:00
2019-02-01 17:43:25 +00:00
QString layoutsFilepath = QString ( " %1/data/layouts/layouts.json " ) . arg ( root ) ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( layoutsFilepath ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument layoutsDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & layoutsDoc , layoutsFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map layouts from %1 " ) . arg ( layoutsFilepath ) ) ;
2020-02-12 00:34:08 +00:00
return false ;
2019-02-01 17:43:25 +00:00
}
QJsonObject layoutsObj = layoutsDoc . object ( ) ;
2020-02-12 00:34:08 +00:00
QJsonArray layouts = layoutsObj [ " layouts " ] . toArray ( ) ;
if ( layouts . size ( ) = = 0 ) {
logError ( QString ( " 'layouts' array is missing from %1. " ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
layoutsLabel = layoutsObj [ " layouts_table_label " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layoutsLabel . isNull ( ) ) {
layoutsLabel = " gMapLayouts " ;
logWarn ( QString ( " 'layouts_table_label' value is missing from %1. Defaulting to %2 " )
. arg ( layoutsFilepath )
. arg ( layoutsLabel ) ) ;
}
2018-09-27 00:33:08 +01:00
2020-02-12 00:34:08 +00:00
QList < QString > requiredFields = QList < QString > {
" id " ,
" name " ,
" width " ,
" height " ,
" primary_tileset " ,
" secondary_tileset " ,
" border_filepath " ,
" blockdata_filepath " ,
} ;
2020-03-13 06:23:47 +00:00
bool useCustomBorderSize = projectConfig . getUseCustomBorderSize ( ) ;
if ( useCustomBorderSize ) {
requiredFields . append ( " border_width " ) ;
requiredFields . append ( " border_height " ) ;
}
2019-02-01 17:43:25 +00:00
for ( int i = 0 ; i < layouts . size ( ) ; i + + ) {
QJsonObject layoutObj = layouts [ i ] . toObject ( ) ;
2020-03-11 05:52:00 +00:00
if ( layoutObj . isEmpty ( ) )
continue ;
2020-02-12 00:34:08 +00:00
if ( ! parser . ensureFieldsExist ( layoutObj , requiredFields ) ) {
logError ( QString ( " Layout %1 is missing field(s) in %2. " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2018-09-27 00:33:08 +01:00
MapLayout * layout = new MapLayout ( ) ;
2019-02-01 17:43:25 +00:00
layout - > id = layoutObj [ " id " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layout - > id . isEmpty ( ) ) {
logError ( QString ( " Missing 'id' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
layout - > name = layoutObj [ " name " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layout - > name . isEmpty ( ) ) {
logError ( QString ( " Missing 'name' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
int lwidth = layoutObj [ " width " ] . toInt ( ) ;
if ( lwidth < = 0 ) {
logError ( QString ( " Invalid layout 'width' value '%1' on layout %2 in %3. Must be greater than 0. " ) . arg ( lwidth ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
layout - > width = QString : : number ( lwidth ) ;
int lheight = layoutObj [ " height " ] . toInt ( ) ;
if ( lheight < = 0 ) {
logError ( QString ( " Invalid layout 'height' value '%1' on layout %2 in %3. Must be greater than 0. " ) . arg ( lheight ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
layout - > height = QString : : number ( lheight ) ;
2020-03-13 06:23:47 +00:00
if ( useCustomBorderSize ) {
int bwidth = layoutObj [ " border_width " ] . toInt ( ) ;
if ( bwidth < = 0 ) { // 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG
logWarn ( QString ( " Invalid layout 'border_width' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead. " ) . arg ( bwidth ) . arg ( i ) . arg ( layoutsFilepath ) . arg ( DEFAULT_BORDER_WIDTH ) ) ;
bwidth = DEFAULT_BORDER_WIDTH ;
}
layout - > border_width = QString : : number ( bwidth ) ;
int bheight = layoutObj [ " border_height " ] . toInt ( ) ;
if ( bheight < = 0 ) {
2020-03-18 07:12:43 +00:00
logWarn ( QString ( " Invalid layout 'border_height' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead. " ) . arg ( bheight ) . arg ( i ) . arg ( layoutsFilepath ) . arg ( DEFAULT_BORDER_HEIGHT ) ) ;
2020-03-13 06:23:47 +00:00
bheight = DEFAULT_BORDER_HEIGHT ;
}
layout - > border_height = QString : : number ( bheight ) ;
} else {
layout - > border_width = QString : : number ( DEFAULT_BORDER_WIDTH ) ;
layout - > border_height = QString : : number ( DEFAULT_BORDER_HEIGHT ) ;
}
2019-02-01 17:43:25 +00:00
layout - > tileset_primary_label = layoutObj [ " primary_tileset " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layout - > tileset_primary_label . isEmpty ( ) ) {
logError ( QString ( " Missing 'primary_tileset' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
layout - > tileset_secondary_label = layoutObj [ " secondary_tileset " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layout - > tileset_secondary_label . isEmpty ( ) ) {
logError ( QString ( " Missing 'secondary_tileset' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
layout - > border_path = layoutObj [ " border_filepath " ] . toString ( ) ;
2020-02-12 00:34:08 +00:00
if ( layout - > border_path . isEmpty ( ) ) {
logError ( QString ( " Missing 'border_filepath' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
layout - > blockdata_path = layoutObj [ " blockdata_filepath " ] . toString ( ) ;
2020-03-13 06:23:47 +00:00
if ( layout - > blockdata_path . isEmpty ( ) ) {
2020-02-12 00:34:08 +00:00
logError ( QString ( " Missing 'blockdata_filepath' value on layout %1 in %2 " ) . arg ( i ) . arg ( layoutsFilepath ) ) ;
return false ;
}
2019-02-01 17:43:25 +00:00
mapLayouts . insert ( layout - > id , layout ) ;
mapLayoutsTable . append ( layout - > id ) ;
2018-09-27 00:33:08 +01:00
}
// Deep copy
mapLayoutsMaster = mapLayouts ;
mapLayoutsMaster . detach ( ) ;
2019-02-01 17:43:25 +00:00
mapLayoutsTableMaster = mapLayoutsTable ;
mapLayoutsTableMaster . detach ( ) ;
2020-02-12 00:34:08 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
void Project : : saveMapLayouts ( ) {
QString layoutsFilepath = QString ( " %1/data/layouts/layouts.json " ) . arg ( root ) ;
QFile layoutsFile ( layoutsFilepath ) ;
if ( ! layoutsFile . open ( QIODevice : : WriteOnly ) ) {
logError ( QString ( " Error: Could not open %1 for writing " ) . arg ( layoutsFilepath ) ) ;
return ;
2018-09-27 00:33:08 +01:00
}
2020-03-06 03:46:25 +00:00
OrderedJson : : object layoutsObj ;
2019-02-01 17:43:25 +00:00
layoutsObj [ " layouts_table_label " ] = layoutsLabel ;
2020-03-13 06:23:47 +00:00
bool useCustomBorderSize = projectConfig . getUseCustomBorderSize ( ) ;
2020-03-06 03:46:25 +00:00
OrderedJson : : array layoutsArr ;
2019-02-01 17:43:25 +00:00
for ( QString layoutId : mapLayoutsTableMaster ) {
MapLayout * layout = mapLayouts . value ( layoutId ) ;
2020-03-06 03:46:25 +00:00
OrderedJson : : object layoutObj ;
2019-02-01 17:43:25 +00:00
layoutObj [ " id " ] = layout - > id ;
layoutObj [ " name " ] = layout - > name ;
layoutObj [ " width " ] = layout - > width . toInt ( nullptr , 0 ) ;
layoutObj [ " height " ] = layout - > height . toInt ( nullptr , 0 ) ;
2020-03-13 06:23:47 +00:00
if ( useCustomBorderSize ) {
layoutObj [ " border_width " ] = layout - > border_width . toInt ( nullptr , 0 ) ;
layoutObj [ " border_height " ] = layout - > border_height . toInt ( nullptr , 0 ) ;
}
2019-02-01 17:43:25 +00:00
layoutObj [ " primary_tileset " ] = layout - > tileset_primary_label ;
layoutObj [ " secondary_tileset " ] = layout - > tileset_secondary_label ;
layoutObj [ " border_filepath " ] = layout - > border_path ;
layoutObj [ " blockdata_filepath " ] = layout - > blockdata_path ;
2020-03-06 03:46:25 +00:00
layoutsArr . push_back ( layoutObj ) ;
2019-02-01 17:43:25 +00:00
}
2020-04-25 22:11:14 +01:00
ignoreWatchedFileTemporarily ( layoutsFilepath ) ;
2019-02-01 17:43:25 +00:00
layoutsObj [ " layouts " ] = layoutsArr ;
2020-03-06 03:46:25 +00:00
OrderedJson layoutJson ( layoutsObj ) ;
OrderedJsonDoc jsonDoc ( & layoutJson ) ;
jsonDoc . dump ( & layoutsFile ) ;
layoutsFile . close ( ) ;
2020-04-25 22:11:14 +01:00
}
void Project : : ignoreWatchedFileTemporarily ( QString filepath ) {
// Ignore any file-change events for this filepath for the next 5 seconds.
modifiedFileTimestamps . insert ( filepath , QDateTime : : currentMSecsSinceEpoch ( ) + 5000 ) ;
2018-09-27 00:33:08 +01:00
}
void Project : : setNewMapLayout ( Map * map ) {
MapLayout * layout = new MapLayout ( ) ;
2019-02-01 17:43:25 +00:00
layout - > id = MapLayout : : layoutConstantFromName ( map - > name ) ;
layout - > name = QString ( " %1_Layout " ) . arg ( map - > name ) ;
2020-05-16 21:57:03 +01:00
layout - > width = QString : : number ( getDefaultMapSize ( ) ) ;
layout - > height = QString : : number ( getDefaultMapSize ( ) ) ;
2020-03-14 07:44:55 +00:00
layout - > border_width = DEFAULT_BORDER_WIDTH ;
layout - > border_height = DEFAULT_BORDER_HEIGHT ;
2018-09-27 00:33:08 +01:00
layout - > border_path = QString ( " data/layouts/%1/border.bin " ) . arg ( map - > name ) ;
layout - > blockdata_path = QString ( " data/layouts/%1/map.bin " ) . arg ( map - > name ) ;
2020-04-19 15:16:49 +01:00
layout - > tileset_primary_label = tilesetLabels [ " primary " ] . value ( 0 , " gTileset_General " ) ;
layout - > tileset_secondary_label = tilesetLabels [ " secondary " ] . value ( 0 , projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ? " gTileset_PalletTown " : " gTileset_Petalburg " ) ;
2018-09-27 00:33:08 +01:00
map - > layout = layout ;
2019-02-01 17:43:25 +00:00
map - > layoutId = layout - > id ;
2018-09-27 00:33:08 +01:00
// Insert new entry into the global map layouts.
2019-02-01 17:43:25 +00:00
mapLayouts . insert ( layout - > id , layout ) ;
mapLayoutsTable . append ( layout - > id ) ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
void Project : : saveMapGroups ( ) {
QString mapGroupsFilepath = QString ( " %1/data/maps/map_groups.json " ) . arg ( root ) ;
QFile mapGroupsFile ( mapGroupsFilepath ) ;
if ( ! mapGroupsFile . open ( QIODevice : : WriteOnly ) ) {
logError ( QString ( " Error: Could not open %1 for writing " ) . arg ( mapGroupsFilepath ) ) ;
return ;
}
2020-03-06 03:46:25 +00:00
OrderedJson : : object mapGroupsObj ;
2019-02-01 17:43:25 +00:00
mapGroupsObj [ " layouts_table_label " ] = layoutsLabel ;
2020-03-06 03:46:25 +00:00
OrderedJson : : array groupNamesArr ;
2021-02-15 09:21:41 +00:00
for ( QString groupName : this - > groupNames ) {
2020-03-06 03:46:25 +00:00
groupNamesArr . push_back ( groupName ) ;
2019-02-01 17:43:25 +00:00
}
mapGroupsObj [ " group_order " ] = groupNamesArr ;
2018-09-27 00:33:08 +01:00
int groupNum = 0 ;
for ( QStringList mapNames : groupedMapNames ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : array groupArr ;
2018-09-27 00:33:08 +01:00
for ( QString mapName : mapNames ) {
2020-03-06 03:46:25 +00:00
groupArr . push_back ( mapName ) ;
2018-09-27 00:33:08 +01:00
}
2021-02-15 09:21:41 +00:00
mapGroupsObj [ this - > groupNames . at ( groupNum ) ] = groupArr ;
2018-09-27 00:33:08 +01:00
groupNum + + ;
}
2020-04-25 22:11:14 +01:00
ignoreWatchedFileTemporarily ( mapGroupsFilepath ) ;
2020-03-06 03:46:25 +00:00
OrderedJson mapGroupJson ( mapGroupsObj ) ;
OrderedJsonDoc jsonDoc ( & mapGroupJson ) ;
jsonDoc . dump ( & mapGroupsFile ) ;
mapGroupsFile . close ( ) ;
2018-09-27 00:33:08 +01:00
}
2019-06-15 23:49:30 +01:00
void Project : : saveWildMonData ( ) {
2019-07-03 21:21:48 +01:00
if ( ! projectConfig . getEncounterJsonActive ( ) ) return ;
2019-06-20 17:40:36 +01:00
QString wildEncountersJsonFilepath = QString ( " %1/src/data/wild_encounters.json " ) . arg ( root ) ;
2019-06-15 23:49:30 +01:00
QFile wildEncountersFile ( wildEncountersJsonFilepath ) ;
if ( ! wildEncountersFile . open ( QIODevice : : WriteOnly ) ) {
logError ( QString ( " Error: Could not open %1 for writing " ) . arg ( wildEncountersJsonFilepath ) ) ;
return ;
}
2020-03-06 03:46:25 +00:00
OrderedJson : : object wildEncountersObject ;
OrderedJson : : array wildEncounterGroups ;
2019-06-15 23:49:30 +01:00
2019-07-03 02:44:19 +01:00
// gWildMonHeaders label is not mutable
2020-03-06 03:46:25 +00:00
OrderedJson : : object monHeadersObject ;
2019-06-15 23:49:30 +01:00
monHeadersObject [ " label " ] = " gWildMonHeaders " ;
monHeadersObject [ " for_maps " ] = true ;
2019-06-20 17:40:36 +01:00
2020-03-06 03:46:25 +00:00
OrderedJson : : array fieldsInfoArray ;
2019-09-30 00:07:34 +01:00
for ( EncounterField fieldInfo : wildMonFields ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object fieldObject ;
OrderedJson : : array rateArray ;
2019-06-20 17:40:36 +01:00
2019-09-30 00:07:34 +01:00
for ( int rate : fieldInfo . encounterRates ) {
2020-03-06 03:46:25 +00:00
rateArray . push_back ( rate ) ;
2019-09-30 00:07:34 +01:00
}
2019-06-20 17:40:36 +01:00
2019-09-30 00:07:34 +01:00
fieldObject [ " type " ] = fieldInfo . name ;
2019-06-20 17:40:36 +01:00
fieldObject [ " encounter_rates " ] = rateArray ;
2020-03-06 03:46:25 +00:00
OrderedJson : : object groupsObject ;
2019-09-30 00:07:34 +01:00
for ( QString groupName : fieldInfo . groups . keys ( ) ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : array subGroupIndices ;
2019-10-01 00:54:27 +01:00
std : : sort ( fieldInfo . groups [ groupName ] . begin ( ) , fieldInfo . groups [ groupName ] . end ( ) ) ;
2019-09-30 00:07:34 +01:00
for ( int slotIndex : fieldInfo . groups [ groupName ] ) {
2020-03-06 03:46:25 +00:00
subGroupIndices . push_back ( slotIndex ) ;
2019-09-30 00:07:34 +01:00
}
groupsObject [ groupName ] = subGroupIndices ;
}
2020-03-06 03:46:25 +00:00
if ( ! groupsObject . empty ( ) ) fieldObject [ " groups " ] = groupsObject ;
2019-09-30 00:07:34 +01:00
2019-06-20 17:40:36 +01:00
fieldsInfoArray . append ( fieldObject ) ;
}
monHeadersObject [ " fields " ] = fieldsInfoArray ;
2020-03-06 03:46:25 +00:00
OrderedJson : : array encountersArray ;
2020-04-20 15:18:03 +01:00
for ( auto keyPair : wildMonData ) {
QString key = keyPair . first ;
for ( auto grouplLabelPair : wildMonData [ key ] ) {
QString groupLabel = grouplLabelPair . first ;
2020-03-06 03:46:25 +00:00
OrderedJson : : object encounterObject ;
2019-06-20 17:40:36 +01:00
encounterObject [ " map " ] = key ;
2019-07-03 02:44:19 +01:00
encounterObject [ " base_label " ] = groupLabel ;
2019-06-20 17:40:36 +01:00
2020-04-20 15:18:03 +01:00
WildPokemonHeader encounterHeader = wildMonData [ key ] [ groupLabel ] ;
2019-06-20 17:40:36 +01:00
for ( QString fieldName : encounterHeader . wildMons . keys ( ) ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object fieldObject ;
2019-06-20 17:40:36 +01:00
WildMonInfo monInfo = encounterHeader . wildMons . value ( fieldName ) ;
fieldObject [ " encounter_rate " ] = monInfo . encounterRate ;
2020-03-06 03:46:25 +00:00
OrderedJson : : array monArray ;
2019-06-20 17:40:36 +01:00
for ( WildPokemon wildMon : monInfo . wildPokemon ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object monEntry ;
2019-06-20 17:40:36 +01:00
monEntry [ " min_level " ] = wildMon . minLevel ;
monEntry [ " max_level " ] = wildMon . maxLevel ;
monEntry [ " species " ] = wildMon . species ;
2020-03-06 03:46:25 +00:00
monArray . push_back ( monEntry ) ;
2019-06-20 17:40:36 +01:00
}
2019-07-03 02:44:19 +01:00
fieldObject [ " mons " ] = monArray ;
2019-06-20 17:40:36 +01:00
encounterObject [ fieldName ] = fieldObject ;
}
2020-03-06 03:46:25 +00:00
encountersArray . push_back ( encounterObject ) ;
2019-06-20 17:40:36 +01:00
}
2019-06-15 23:49:30 +01:00
}
monHeadersObject [ " encounters " ] = encountersArray ;
2020-03-06 03:46:25 +00:00
wildEncounterGroups . push_back ( monHeadersObject ) ;
2019-06-15 23:49:30 +01:00
// add extra Json objects that are not associated with maps to the file
2020-03-06 03:46:25 +00:00
for ( auto extraObject : extraEncounterGroups ) {
wildEncounterGroups . push_back ( extraObject ) ;
2019-06-15 23:49:30 +01:00
}
wildEncountersObject [ " wild_encounter_groups " ] = wildEncounterGroups ;
2020-04-25 22:11:14 +01:00
ignoreWatchedFileTemporarily ( wildEncountersJsonFilepath ) ;
2020-03-06 03:46:25 +00:00
OrderedJson encounterJson ( wildEncountersObject ) ;
OrderedJsonDoc jsonDoc ( & encounterJson ) ;
jsonDoc . dump ( & wildEncountersFile ) ;
2019-06-15 23:49:30 +01:00
wildEncountersFile . close ( ) ;
}
2018-09-27 00:33:08 +01:00
void Project : : saveMapConstantsHeader ( ) {
2019-02-01 17:43:25 +00:00
QString text = QString ( " #ifndef GUARD_CONSTANTS_MAP_GROUPS_H \n " ) ;
text + = QString ( " #define GUARD_CONSTANTS_MAP_GROUPS_H \n " ) ;
2020-12-29 00:32:44 +00:00
text + = QString ( " \n // \n // DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json \n // \n \n " ) ;
2018-09-27 00:33:08 +01:00
int groupNum = 0 ;
for ( QStringList mapNames : groupedMapNames ) {
text + = QString ( " // Map Group %1 \n " ) . arg ( groupNum ) ;
int maxLength = 0 ;
for ( QString mapName : mapNames ) {
2021-02-15 09:21:41 +00:00
QString mapConstantName = mapNamesToMapConstants . value ( mapName ) ;
2018-09-27 00:33:08 +01:00
if ( mapConstantName . length ( ) > maxLength )
maxLength = mapConstantName . length ( ) ;
}
int groupIndex = 0 ;
for ( QString mapName : mapNames ) {
2021-02-15 09:21:41 +00:00
QString mapConstantName = mapNamesToMapConstants . value ( mapName ) ;
2018-09-27 00:33:08 +01:00
text + = QString ( " #define %1%2(%3 | (%4 << 8)) \n " )
. arg ( mapConstantName )
. arg ( QString ( " " ) . repeated ( maxLength - mapConstantName . length ( ) + 1 ) )
. arg ( groupIndex )
. arg ( groupNum ) ;
groupIndex + + ;
}
text + = QString ( " \n " ) ;
groupNum + + ;
}
2018-12-25 19:27:02 +00:00
text + = QString ( " #define MAP_GROUPS_COUNT %1 \n \n " ) . arg ( groupNum ) ;
2019-02-16 23:00:38 +00:00
text + = QString ( " #endif // GUARD_CONSTANTS_MAP_GROUPS_H \n " ) ;
2020-04-25 22:11:14 +01:00
QString mapGroupFilepath = root + " /include/constants/map_groups.h " ;
ignoreWatchedFileTemporarily ( mapGroupFilepath ) ;
saveTextFile ( mapGroupFilepath , text ) ;
2018-09-27 00:33:08 +01:00
}
// saves heal location coords in root + /src/data/heal_locations.h
// and indexes as defines in root + /include/constants/heal_locations.h
void Project : : saveHealLocationStruct ( Map * map ) {
2020-03-20 07:09:48 +00:00
QString constantPrefix , arrayName ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getHealLocationRespawnDataEnabled ( ) ) {
2020-03-20 07:09:48 +00:00
constantPrefix = " SPAWN_ " ;
arrayName = " sSpawnPoints " ;
} else {
constantPrefix = " HEAL_LOCATION_ " ;
arrayName = " sHealLocations " ;
}
QString data_text = QString ( " %1%2struct HealLocation %3[] = \n { \n " )
2019-04-29 01:20:48 +01:00
. arg ( dataQualifiers . value ( " heal_locations " ) . isStatic ? " static " : " " )
2020-03-20 07:09:48 +00:00
. arg ( dataQualifiers . value ( " heal_locations " ) . isConst ? " const " : " " )
. arg ( arrayName ) ;
2018-09-27 00:33:08 +01:00
QString constants_text = QString ( " #ifndef GUARD_CONSTANTS_HEAL_LOCATIONS_H \n " ) ;
constants_text + = QString ( " #define GUARD_CONSTANTS_HEAL_LOCATIONS_H \n \n " ) ;
2020-04-29 17:34:49 +01:00
QMap < QString , int > healLocationsDupes ;
QSet < QString > healLocationsUnique ;
2018-09-27 00:33:08 +01:00
2020-04-29 17:34:49 +01:00
// set healLocationsDupes and healLocationsUnique
for ( auto it = healLocations . begin ( ) ; it ! = healLocations . end ( ) ; it + + ) {
2018-09-27 00:33:08 +01:00
HealLocation loc = * it ;
2020-03-20 07:09:48 +00:00
QString xname = loc . idName ;
2020-04-29 17:34:49 +01:00
if ( healLocationsUnique . contains ( xname ) ) {
healLocationsDupes [ xname ] = 1 ;
2018-09-27 00:33:08 +01:00
}
2020-04-29 17:34:49 +01:00
healLocationsUnique . insert ( xname ) ;
2018-09-27 00:33:08 +01:00
}
2020-04-29 17:34:49 +01:00
// set new location in healLocations list
2018-09-27 00:33:08 +01:00
if ( map - > events [ " heal_event_group " ] . length ( ) > 0 ) {
for ( Event * healEvent : map - > events [ " heal_event_group " ] ) {
HealLocation hl = HealLocation : : fromEvent ( healEvent ) ;
2020-04-29 17:34:49 +01:00
healLocations [ hl . index - 1 ] = hl ;
2018-09-27 00:33:08 +01:00
}
}
int i = 1 ;
2020-04-29 17:34:49 +01:00
for ( auto map_in : healLocations ) {
2020-03-20 07:09:48 +00:00
// add numbered suffix for duplicate constants
2020-04-29 17:34:49 +01:00
if ( healLocationsDupes . keys ( ) . contains ( map_in . idName ) ) {
QString duplicateName = map_in . idName ;
map_in . idName + = QString ( " _%1 " ) . arg ( healLocationsDupes [ duplicateName ] ) ;
healLocationsDupes [ duplicateName ] + + ;
2020-03-20 07:09:48 +00:00
}
// Save first array (heal location coords), only data array in RSE
data_text + = QString ( " [%1%2 - 1] = {MAP_GROUP(%3), MAP_NUM(%3), %4, %5}, \n " )
. arg ( constantPrefix )
. arg ( map_in . idName )
. arg ( map_in . mapName )
2018-09-27 00:33:08 +01:00
. arg ( map_in . x )
. arg ( map_in . y ) ;
2020-03-20 07:09:48 +00:00
// Save constants
2018-09-27 00:33:08 +01:00
if ( map_in . index ! = 0 ) {
2020-03-20 07:09:48 +00:00
constants_text + = QString ( " #define %1%2 %3 \n " )
. arg ( constantPrefix )
. arg ( map_in . idName )
2018-09-27 00:33:08 +01:00
. arg ( map_in . index ) ;
2020-03-20 07:09:48 +00:00
} else {
constants_text + = QString ( " #define %1%2 %3 \n " )
. arg ( constantPrefix )
. arg ( map_in . idName )
2018-09-27 00:33:08 +01:00
. arg ( i ) ;
}
i + + ;
}
2020-05-22 22:51:56 +01:00
if ( projectConfig . getHealLocationRespawnDataEnabled ( ) ) {
2020-03-20 07:09:48 +00:00
// Save second array (map where player respawns for each heal location)
data_text + = QString ( " }; \n \n %1%2u16 sWhiteoutRespawnHealCenterMapIdxs[][2] = \n { \n " )
. arg ( dataQualifiers . value ( " heal_locations " ) . isStatic ? " static " : " " )
. arg ( dataQualifiers . value ( " heal_locations " ) . isConst ? " const " : " " ) ;
2020-04-29 17:34:49 +01:00
for ( auto map_in : healLocations ) {
2020-03-20 07:09:48 +00:00
data_text + = QString ( " [%1%2 - 1] = {MAP_GROUP(%3), MAP_NUM(%3)}, \n " )
. arg ( constantPrefix )
. arg ( map_in . idName )
. arg ( map_in . respawnMap ) ;
}
// Save third array (object id of NPC player speaks to upon respawning for each heal location)
data_text + = QString ( " }; \n \n %1%2u8 sWhiteoutRespawnHealerNpcIds[] = \n { \n " )
. arg ( dataQualifiers . value ( " heal_locations " ) . isStatic ? " static " : " " )
. arg ( dataQualifiers . value ( " heal_locations " ) . isConst ? " const " : " " ) ;
2020-04-29 17:34:49 +01:00
for ( auto map_in : healLocations ) {
2020-03-20 07:09:48 +00:00
data_text + = QString ( " [%1%2 - 1] = %3, \n " )
. arg ( constantPrefix )
. arg ( map_in . idName )
. arg ( map_in . respawnNPC ) ;
}
}
2018-09-27 00:33:08 +01:00
data_text + = QString ( " }; \n " ) ;
constants_text + = QString ( " \n #endif // GUARD_CONSTANTS_HEAL_LOCATIONS_H \n " ) ;
2020-04-12 01:39:50 +01:00
QString healLocationFilepath = root + " /src/data/heal_locations.h " ;
2020-04-25 22:11:14 +01:00
ignoreWatchedFileTemporarily ( healLocationFilepath ) ;
2020-04-12 01:39:50 +01:00
saveTextFile ( healLocationFilepath , data_text ) ;
QString healLocationConstantsFilepath = root + " /include/constants/heal_locations.h " ;
2020-04-25 22:11:14 +01:00
ignoreWatchedFileTemporarily ( healLocationConstantsFilepath ) ;
2020-04-12 01:39:50 +01:00
saveTextFile ( healLocationConstantsFilepath , constants_text ) ;
2018-09-27 00:33:08 +01:00
}
2018-10-03 01:01:09 +01:00
void Project : : saveTilesets ( Tileset * primaryTileset , Tileset * secondaryTileset ) {
2019-04-04 06:44:31 +01:00
saveTilesetMetatileLabels ( primaryTileset , secondaryTileset ) ;
2018-10-03 01:01:09 +01:00
saveTilesetMetatileAttributes ( primaryTileset ) ;
saveTilesetMetatileAttributes ( secondaryTileset ) ;
saveTilesetMetatiles ( primaryTileset ) ;
saveTilesetMetatiles ( secondaryTileset ) ;
2018-10-03 01:01:24 +01:00
saveTilesetTilesImage ( primaryTileset ) ;
saveTilesetTilesImage ( secondaryTileset ) ;
2020-05-03 16:00:56 +01:00
saveTilesetPalettes ( primaryTileset ) ;
saveTilesetPalettes ( secondaryTileset ) ;
2018-10-03 01:01:09 +01:00
}
2019-04-04 06:44:31 +01:00
void Project : : saveTilesetMetatileLabels ( Tileset * primaryTileset , Tileset * secondaryTileset ) {
QString primaryPrefix = QString ( " METATILE_%1_ " ) . arg ( QString ( primaryTileset - > name ) . replace ( " gTileset_ " , " " ) ) ;
QString secondaryPrefix = QString ( " METATILE_%1_ " ) . arg ( QString ( secondaryTileset - > name ) . replace ( " gTileset_ " , " " ) ) ;
QMap < QString , int > defines ;
bool definesFileModified = false ;
2019-09-09 23:24:30 +01:00
2020-05-22 21:02:11 +01:00
QString metatileLabelsFilename = " include/constants/metatile_labels.h " ;
defines = parser . readCDefines ( metatileLabelsFilename , ( QStringList ( ) < < " METATILE_ " ) ) ;
2019-09-09 23:24:30 +01:00
// Purge old entries from the file.
QStringList definesToRemove ;
for ( QString defineName : defines . keys ( ) ) {
if ( defineName . startsWith ( primaryPrefix ) | | defineName . startsWith ( secondaryPrefix ) ) {
definesToRemove < < defineName ;
2019-04-04 06:44:31 +01:00
}
}
2019-09-09 23:24:30 +01:00
for ( QString defineName : definesToRemove ) {
defines . remove ( defineName ) ;
definesFileModified = true ;
}
2019-04-04 06:44:31 +01:00
// Add the new labels.
2021-02-17 02:45:54 +00:00
for ( int i = 0 ; i < primaryTileset - > metatiles . size ( ) ; i + + ) {
Metatile * metatile = primaryTileset - > metatiles . at ( i ) ;
2019-04-04 06:44:31 +01:00
if ( metatile - > label . size ( ) ! = 0 ) {
QString defineName = QString ( " %1%2 " ) . arg ( primaryPrefix , metatile - > label ) ;
defines . insert ( defineName , i ) ;
definesFileModified = true ;
}
}
2021-02-17 02:45:54 +00:00
for ( int i = 0 ; i < secondaryTileset - > metatiles . size ( ) ; i + + ) {
Metatile * metatile = secondaryTileset - > metatiles . at ( i ) ;
2019-04-04 06:44:31 +01:00
if ( metatile - > label . size ( ) ! = 0 ) {
QString defineName = QString ( " %1%2 " ) . arg ( secondaryPrefix , metatile - > label ) ;
2019-07-18 21:35:00 +01:00
defines . insert ( defineName , i + Project : : num_tiles_primary ) ;
2019-04-04 06:44:31 +01:00
definesFileModified = true ;
}
}
if ( ! definesFileModified ) {
return ;
}
2019-07-18 21:35:00 +01:00
auto getTilesetFromLabel = [ ] ( QString labelName ) {
return QRegularExpression ( " METATILE_(?<tileset>[A-Za-z0-9]+) _ " ).match(labelName).captured( " tileset " ) ;
} ;
2019-04-04 06:44:31 +01:00
QString outputText = " #ifndef GUARD_METATILE_LABELS_H \n " ;
2019-07-18 21:35:00 +01:00
outputText + = " #define GUARD_METATILE_LABELS_H \n " ;
for ( int i = 0 ; i < defines . size ( ) ; ) {
QString defineName = defines . keys ( ) [ i ] ;
QString currentTileset = getTilesetFromLabel ( defineName ) ;
outputText + = QString ( " \n // gTileset_%1 \n " ) . arg ( currentTileset ) ;
int j = 0 , longestLength = 0 ;
QMap < QString , int > definesOut ;
// Setup for pretty formatting.
while ( i + j < defines . size ( ) & & getTilesetFromLabel ( defines . keys ( ) [ i + j ] ) = = currentTileset ) {
defineName = defines . keys ( ) [ i + j ] ;
if ( defineName . size ( ) > longestLength )
longestLength = defineName . size ( ) ;
definesOut . insert ( defineName , defines [ defineName ] ) ;
j + + ;
}
for ( QString defineName : definesOut . keys ( ) ) {
int value = defines [ defineName ] ;
QString line = QString ( " #define %1 0x%2 \n " )
. arg ( defineName , - 1 * longestLength )
. arg ( QString ( " %1 " ) . arg ( value , 3 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ) ;
outputText + = line ;
}
i + = j ;
2019-04-04 06:44:31 +01:00
}
outputText + = " \n #endif // GUARD_METATILE_LABELS_H \n " ;
2020-04-12 01:39:50 +01:00
2020-05-22 21:02:11 +01:00
ignoreWatchedFileTemporarily ( root + " / " + metatileLabelsFilename ) ;
saveTextFile ( root + " / " + metatileLabelsFilename , outputText ) ;
2019-04-04 06:44:31 +01:00
}
2018-10-03 01:01:09 +01:00
void Project : : saveTilesetMetatileAttributes ( Tileset * tileset ) {
QFile attrs_file ( tileset - > metatile_attrs_path ) ;
if ( attrs_file . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) ) {
QByteArray data ;
2020-03-16 20:31:08 +00:00
if ( projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ) {
2021-02-17 02:45:54 +00:00
for ( Metatile * metatile : tileset - > metatiles ) {
2020-03-16 20:31:08 +00:00
data . append ( static_cast < char > ( metatile - > behavior ) ) ;
data . append ( static_cast < char > ( metatile - > behavior > > 8 ) |
static_cast < char > ( metatile - > terrainType < < 1 ) ) ;
data . append ( static_cast < char > ( 0 ) ) ;
data . append ( static_cast < char > ( metatile - > encounterType ) |
static_cast < char > ( metatile - > layerType < < 5 ) ) ;
}
} else {
2021-02-17 02:45:54 +00:00
for ( Metatile * metatile : tileset - > metatiles ) {
2020-03-16 20:31:08 +00:00
data . append ( static_cast < char > ( metatile - > behavior ) ) ;
data . append ( static_cast < char > ( ( metatile - > layerType < < 4 ) & 0xF0 ) ) ;
}
2018-10-03 01:01:09 +01:00
}
attrs_file . write ( data ) ;
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not save tileset metatile attributes file '%1' " ) . arg ( tileset - > metatile_attrs_path ) ) ;
2018-10-03 01:01:09 +01:00
}
}
void Project : : saveTilesetMetatiles ( Tileset * tileset ) {
QFile metatiles_file ( tileset - > metatiles_path ) ;
if ( metatiles_file . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) ) {
QByteArray data ;
2021-02-17 02:45:54 +00:00
for ( Metatile * metatile : tileset - > metatiles ) {
2020-06-25 06:32:42 +01:00
int numTiles = projectConfig . getTripleLayerMetatilesEnabled ( ) ? 12 : 8 ;
for ( int i = 0 ; i < numTiles ; i + + ) {
2021-02-16 17:14:27 +00:00
Tile tile = metatile - > tiles . at ( i ) ;
2018-10-03 01:01:09 +01:00
uint16_t value = static_cast < uint16_t > ( ( tile . tile & 0x3ff )
| ( ( tile . xflip & 1 ) < < 10 )
| ( ( tile . yflip & 1 ) < < 11 )
| ( ( tile . palette & 0xf ) < < 12 ) ) ;
data . append ( static_cast < char > ( value & 0xff ) ) ;
data . append ( static_cast < char > ( ( value > > 8 ) & 0xff ) ) ;
}
}
metatiles_file . write ( data ) ;
} else {
2021-02-17 02:45:54 +00:00
tileset - > metatiles . clear ( ) ;
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open tileset metatiles file '%1' " ) . arg ( tileset - > metatiles_path ) ) ;
2018-10-03 01:01:09 +01:00
}
}
2018-10-03 01:01:24 +01:00
void Project : : saveTilesetTilesImage ( Tileset * tileset ) {
2020-03-04 14:15:00 +00:00
exportIndexed4BPPPng ( tileset - > tilesImage , tileset - > tilesImagePath ) ;
2018-10-03 01:01:24 +01:00
}
2020-05-03 16:00:56 +01:00
void Project : : saveTilesetPalettes ( Tileset * tileset ) {
2019-03-22 00:56:00 +00:00
for ( int i = 0 ; i < Project : : getNumPalettesTotal ( ) ; i + + ) {
2018-10-03 01:01:41 +01:00
QString filepath = tileset - > palettePaths . at ( i ) ;
2021-02-18 08:25:26 +00:00
PaletteUtil : : writeJASC ( filepath , tileset - > palettes . at ( i ) . toVector ( ) , 0 , 16 ) ;
2018-10-03 01:01:41 +01:00
}
}
2020-02-12 16:22:40 +00:00
bool Project : : loadMapTilesets ( Map * map ) {
2020-07-31 22:12:08 +01:00
if ( map - > hasUnsavedChanges ( ) ) {
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
map - > layout - > tileset_primary = getTileset ( map - > layout - > tileset_primary_label ) ;
2020-02-12 16:22:40 +00:00
if ( ! map - > layout - > tileset_primary ) {
2020-04-19 15:16:49 +01:00
QString defaultTileset = tilesetLabels [ " primary " ] . value ( 0 , " gTileset_General " ) ;
logWarn ( QString ( " Map layout %1 has invalid primary tileset '%2'. Using default '%3' " ) . arg ( map - > layout - > id ) . arg ( map - > layout - > tileset_primary_label ) . arg ( defaultTileset ) ) ;
map - > layout - > tileset_primary_label = defaultTileset ;
2020-04-04 19:14:16 +01:00
map - > layout - > tileset_primary = getTileset ( map - > layout - > tileset_primary_label ) ;
if ( ! map - > layout - > tileset_primary ) {
logError ( QString ( " Failed to set default primary tileset. " ) ) ;
return false ;
}
2020-02-12 16:22:40 +00:00
}
2018-09-27 00:33:08 +01:00
map - > layout - > tileset_secondary = getTileset ( map - > layout - > tileset_secondary_label ) ;
2020-02-12 16:22:40 +00:00
if ( ! map - > layout - > tileset_secondary ) {
2020-04-19 15:16:49 +01:00
QString defaultTileset = tilesetLabels [ " secondary " ] . value ( 0 , projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ? " gTileset_PalletTown " : " gTileset_Petalburg " ) ;
logWarn ( QString ( " Map layout %1 has invalid secondary tileset '%2'. Using default '%3' " ) . arg ( map - > layout - > id ) . arg ( map - > layout - > tileset_secondary_label ) . arg ( defaultTileset ) ) ;
map - > layout - > tileset_secondary_label = defaultTileset ;
2020-04-04 19:14:16 +01:00
map - > layout - > tileset_secondary = getTileset ( map - > layout - > tileset_secondary_label ) ;
if ( ! map - > layout - > tileset_secondary ) {
logError ( QString ( " Failed to set default secondary tileset. " ) ) ;
return false ;
}
2020-02-12 16:22:40 +00:00
}
return true ;
2018-09-27 00:33:08 +01:00
}
2018-10-03 01:01:15 +01:00
Tileset * Project : : loadTileset ( QString label , Tileset * tileset ) {
2021-02-16 11:15:54 +00:00
const QStringList values = parser . getLabelValues ( parser . parseAsm ( " data/tilesets/headers.inc " ) , label ) ;
if ( values . isEmpty ( ) ) {
2018-12-20 23:30:35 +00:00
return nullptr ;
}
2018-10-03 01:01:15 +01:00
if ( tileset = = nullptr ) {
tileset = new Tileset ;
}
2018-09-27 00:33:08 +01:00
tileset - > name = label ;
2021-02-16 11:15:54 +00:00
tileset - > is_compressed = values . value ( 0 ) ;
tileset - > is_secondary = values . value ( 1 ) ;
tileset - > padding = values . value ( 2 ) ;
tileset - > tiles_label = values . value ( 3 ) ;
tileset - > palettes_label = values . value ( 4 ) ;
tileset - > metatiles_label = values . value ( 5 ) ;
2020-04-19 23:11:45 +01:00
if ( projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ) {
2021-02-16 11:15:54 +00:00
tileset - > callback_label = values . value ( 6 ) ;
tileset - > metatile_attrs_label = values . value ( 7 ) ;
2020-04-19 23:11:45 +01:00
} else {
2021-02-16 11:15:54 +00:00
tileset - > metatile_attrs_label = values . value ( 6 ) ;
tileset - > callback_label = values . value ( 7 ) ;
2020-04-19 23:11:45 +01:00
}
2018-09-27 00:33:08 +01:00
loadTilesetAssets ( tileset ) ;
2021-02-15 16:33:30 +00:00
tilesetCache . insert ( label , tileset ) ;
2018-09-27 00:33:08 +01:00
return tileset ;
}
2020-07-31 22:12:08 +01:00
bool Project : : loadBlockdata ( Map * map ) {
if ( map - > hasUnsavedChanges ( ) ) {
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
QString path = QString ( " %1/%2 " ) . arg ( root ) . arg ( map - > layout - > blockdata_path ) ;
map - > layout - > blockdata = readBlockdata ( path ) ;
2021-02-14 21:34:17 +00:00
map - > layout - > lastCommitMapBlocks . blocks = map - > layout - > blockdata ;
2020-08-04 01:49:00 +01:00
map - > layout - > lastCommitMapBlocks . dimensions = QSize ( map - > getWidth ( ) , map - > getHeight ( ) ) ;
2019-01-07 18:06:25 +00:00
2021-02-14 21:34:17 +00:00
if ( map - > layout - > blockdata . count ( ) ! = map - > getWidth ( ) * map - > getHeight ( ) ) {
2019-01-07 18:06:25 +00:00
logWarn ( QString ( " Layout blockdata length %1 does not match dimensions %2x%3 (should be %4). Resizing blockdata. " )
2021-02-14 21:34:17 +00:00
. arg ( map - > layout - > blockdata . count ( ) )
2019-01-07 18:06:25 +00:00
. arg ( map - > getWidth ( ) )
. arg ( map - > getHeight ( ) )
. arg ( map - > getWidth ( ) * map - > getHeight ( ) ) ) ;
2021-02-14 21:34:17 +00:00
map - > layout - > blockdata . resize ( map - > getWidth ( ) * map - > getHeight ( ) ) ;
2019-01-07 18:06:25 +00:00
}
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2020-07-31 22:12:08 +01:00
void Project : : setNewMapBlockdata ( Map * map ) {
2021-02-14 21:34:17 +00:00
map - > layout - > blockdata . clear ( ) ;
2018-09-27 00:33:08 +01:00
for ( int i = 0 ; i < map - > getWidth ( ) * map - > getHeight ( ) ; i + + ) {
2021-02-14 21:34:17 +00:00
map - > layout - > blockdata . append ( qint16 ( 0x3001 ) ) ;
2018-09-27 00:33:08 +01:00
}
2021-02-14 21:34:17 +00:00
map - > layout - > lastCommitMapBlocks . blocks = map - > layout - > blockdata ;
2020-08-04 01:49:00 +01:00
map - > layout - > lastCommitMapBlocks . dimensions = QSize ( map - > getWidth ( ) , map - > getHeight ( ) ) ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 16:22:40 +00:00
bool Project : : loadMapBorder ( Map * map ) {
2020-07-31 22:12:08 +01:00
if ( map - > hasUnsavedChanges ( ) ) {
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
QString path = QString ( " %1/%2 " ) . arg ( root ) . arg ( map - > layout - > border_path ) ;
map - > layout - > border = readBlockdata ( path ) ;
2020-03-13 06:23:47 +00:00
int borderLength = map - > getBorderWidth ( ) * map - > getBorderHeight ( ) ;
2021-02-14 21:34:17 +00:00
if ( map - > layout - > border . count ( ) ! = borderLength ) {
2020-02-12 16:22:40 +00:00
logWarn ( QString ( " Layout border blockdata length %1 must be %2. Resizing border blockdata. " )
2021-02-14 21:34:17 +00:00
. arg ( map - > layout - > border . count ( ) )
2020-02-12 16:22:40 +00:00
. arg ( borderLength ) ) ;
2021-02-14 21:34:17 +00:00
map - > layout - > border . resize ( borderLength ) ;
2020-02-12 16:22:40 +00:00
}
return true ;
2018-09-27 00:33:08 +01:00
}
void Project : : setNewMapBorder ( Map * map ) {
2021-02-14 21:34:17 +00:00
map - > layout - > border . clear ( ) ;
2020-03-14 07:44:55 +00:00
if ( map - > getBorderWidth ( ) ! = DEFAULT_BORDER_WIDTH | | map - > getBorderHeight ( ) ! = DEFAULT_BORDER_HEIGHT ) {
for ( int i = 0 ; i < map - > getBorderWidth ( ) * map - > getBorderHeight ( ) ; i + + ) {
2021-02-14 21:34:17 +00:00
map - > layout - > border . append ( 0 ) ;
2020-03-14 07:44:55 +00:00
}
} else if ( projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ) {
2021-02-14 21:34:17 +00:00
map - > layout - > border . append ( qint16 ( 0x0014 ) ) ;
map - > layout - > border . append ( qint16 ( 0x0015 ) ) ;
map - > layout - > border . append ( qint16 ( 0x001C ) ) ;
map - > layout - > border . append ( qint16 ( 0x001D ) ) ;
2020-03-13 06:23:47 +00:00
} else {
2021-02-14 21:34:17 +00:00
map - > layout - > border . append ( qint16 ( 0x01D4 ) ) ;
map - > layout - > border . append ( qint16 ( 0x01D5 ) ) ;
map - > layout - > border . append ( qint16 ( 0x01DC ) ) ;
map - > layout - > border . append ( qint16 ( 0x01DD ) ) ;
2020-03-13 06:23:47 +00:00
}
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
void Project : : saveLayoutBorder ( Map * map ) {
2018-09-27 00:33:08 +01:00
QString path = QString ( " %1/%2 " ) . arg ( root ) . arg ( map - > layout - > border_path ) ;
writeBlockdata ( path , map - > layout - > border ) ;
}
2019-02-01 17:43:25 +00:00
void Project : : saveLayoutBlockdata ( Map * map ) {
2018-09-27 00:33:08 +01:00
QString path = QString ( " %1/%2 " ) . arg ( root ) . arg ( map - > layout - > blockdata_path ) ;
writeBlockdata ( path , map - > layout - > blockdata ) ;
}
2021-02-14 21:34:17 +00:00
void Project : : writeBlockdata ( QString path , const Blockdata & blockdata ) {
2018-09-27 00:33:08 +01:00
QFile file ( path ) ;
if ( file . open ( QIODevice : : WriteOnly ) ) {
2021-02-14 21:34:17 +00:00
QByteArray data = blockdata . serialize ( ) ;
2018-09-27 00:33:08 +01:00
file . write ( data ) ;
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Failed to open blockdata file for writing: '%1' " ) . arg ( path ) ) ;
2018-09-27 00:33:08 +01:00
}
}
void Project : : saveAllMaps ( ) {
2021-02-18 22:41:36 +00:00
for ( auto * map : mapCache . values ( ) )
2018-09-27 00:33:08 +01:00
saveMap ( map ) ;
}
void Project : : saveMap ( Map * map ) {
// Create/Modify a few collateral files for brand new maps.
2019-02-01 17:43:25 +00:00
QString mapDataDir = QString ( root + " /data/maps/%1 " ) . arg ( map - > name ) ;
2018-09-27 00:33:08 +01:00
if ( ! map - > isPersistedToFile ) {
2019-02-01 17:43:25 +00:00
if ( ! QDir : : root ( ) . mkdir ( mapDataDir ) ) {
logError ( QString ( " Error: failed to create directory for new map: '%1' " ) . arg ( mapDataDir ) ) ;
2018-09-27 00:33:08 +01:00
}
// Create file data/maps/<map_name>/scripts.inc
2019-10-22 13:48:41 +01:00
QString text = this - > getScriptDefaultString ( projectConfig . getUsePoryScript ( ) , map - > name ) ;
saveTextFile ( root + " /data/maps/ " + map - > name + " /scripts " + this - > getScriptFileExtension ( projectConfig . getUsePoryScript ( ) ) , text ) ;
2018-09-27 00:33:08 +01:00
2021-02-19 09:04:14 +00:00
bool usesTextFile = projectConfig . getCreateMapTextFileEnabled ( ) ;
if ( usesTextFile ) {
2019-01-08 14:59:43 +00:00
// Create file data/maps/<map_name>/text.inc
2019-10-22 13:48:41 +01:00
saveTextFile ( root + " /data/maps/ " + map - > name + " /text " + this - > getScriptFileExtension ( projectConfig . getUsePoryScript ( ) ) , " \n " ) ;
2019-01-08 14:59:43 +00:00
}
2018-09-27 00:33:08 +01:00
// Simply append to data/event_scripts.s.
text = QString ( " \n \t .include \" data/maps/%1/scripts.inc \" \n " ) . arg ( map - > name ) ;
2021-02-19 09:04:14 +00:00
if ( usesTextFile ) {
2019-01-08 14:59:43 +00:00
text + = QString ( " \t .include \" data/maps/%1/text.inc \" \n " ) . arg ( map - > name ) ;
}
2018-09-27 00:33:08 +01:00
appendTextFile ( root + " /data/event_scripts.s " , text ) ;
2019-02-01 17:43:25 +00:00
if ( map - > needsLayoutDir ) {
QString newLayoutDir = QString ( root + " /data/layouts/%1 " ) . arg ( map - > name ) ;
if ( ! QDir : : root ( ) . mkdir ( newLayoutDir ) ) {
logError ( QString ( " Error: failed to create directory for new layout: '%1' " ) . arg ( newLayoutDir ) ) ;
}
}
}
2018-09-27 00:33:08 +01:00
2019-02-01 17:43:25 +00:00
QString layoutsFilepath = QString ( " %1/data/layouts/layouts.json " ) . arg ( root ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument layoutsDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & layoutsDoc , layoutsFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map layouts from %1 " ) . arg ( layoutsFilepath ) ) ;
return ;
}
2019-02-01 17:43:25 +00:00
QFile layoutsFile ( layoutsFilepath ) ;
if ( ! layoutsFile . open ( QIODevice : : ReadWrite ) ) {
logError ( QString ( " Error: Could not open %1 for read/write " ) . arg ( layoutsFilepath ) ) ;
return ;
}
2019-04-20 15:06:59 +01:00
// Append to "layouts" array in data/layouts/layouts.json.
2019-02-01 17:43:25 +00:00
QJsonObject layoutsObj = layoutsDoc . object ( ) ;
QJsonArray layoutsArr = layoutsObj [ " layouts " ] . toArray ( ) ;
QJsonObject newLayoutObj ;
newLayoutObj [ " id " ] = map - > layout - > id ;
newLayoutObj [ " name " ] = map - > layout - > name ;
newLayoutObj [ " width " ] = map - > layout - > width . toInt ( ) ;
newLayoutObj [ " height " ] = map - > layout - > height . toInt ( ) ;
2020-03-13 06:23:47 +00:00
if ( projectConfig . getUseCustomBorderSize ( ) ) {
newLayoutObj [ " border_width " ] = map - > layout - > border_width . toInt ( ) ;
newLayoutObj [ " border_height " ] = map - > layout - > border_height . toInt ( ) ;
}
2019-02-01 17:43:25 +00:00
newLayoutObj [ " primary_tileset " ] = map - > layout - > tileset_primary_label ;
newLayoutObj [ " secondary_tileset " ] = map - > layout - > tileset_secondary_label ;
newLayoutObj [ " border_filepath " ] = map - > layout - > border_path ;
newLayoutObj [ " blockdata_filepath " ] = map - > layout - > blockdata_path ;
layoutsArr . append ( newLayoutObj ) ;
layoutsFile . write ( layoutsDoc . toJson ( ) ) ;
2019-04-20 15:06:59 +01:00
layoutsFile . close ( ) ;
2019-02-01 17:43:25 +00:00
// Create map.json for map data.
QString mapFilepath = QString ( " %1/map.json " ) . arg ( mapDataDir ) ;
QFile mapFile ( mapFilepath ) ;
if ( ! mapFile . open ( QIODevice : : WriteOnly ) ) {
logError ( QString ( " Error: Could not open %1 for writing " ) . arg ( mapFilepath ) ) ;
return ;
2018-09-27 00:33:08 +01:00
}
2020-03-06 03:46:25 +00:00
OrderedJson : : object mapObj ;
2019-02-01 17:43:25 +00:00
// Header values.
mapObj [ " id " ] = map - > constantName ;
mapObj [ " name " ] = map - > name ;
mapObj [ " layout " ] = map - > layout - > id ;
mapObj [ " music " ] = map - > song ;
mapObj [ " region_map_section " ] = map - > location ;
mapObj [ " requires_flash " ] = map - > requiresFlash . toInt ( ) > 0 | | map - > requiresFlash = = " TRUE " ;
mapObj [ " weather " ] = map - > weather ;
mapObj [ " map_type " ] = map - > type ;
2020-10-25 21:46:04 +00:00
if ( projectConfig . getBaseGameVersion ( ) ! = BaseGameVersion : : pokeruby ) {
mapObj [ " allow_cycling " ] = map - > allowBiking . toInt ( ) > 0 | | map - > allowBiking = = " TRUE " ;
mapObj [ " allow_escaping " ] = map - > allowEscapeRope . toInt ( ) > 0 | | map - > allowEscapeRope = = " TRUE " ;
mapObj [ " allow_running " ] = map - > allowRunning . toInt ( ) > 0 | | map - > allowRunning = = " TRUE " ;
}
2019-02-01 17:43:25 +00:00
mapObj [ " show_map_name " ] = map - > show_location . toInt ( ) > 0 | | map - > show_location = = " TRUE " ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getFloorNumberEnabled ( ) ) {
2020-03-18 07:12:43 +00:00
mapObj [ " floor_number " ] = map - > floorNumber ;
}
2019-02-01 17:43:25 +00:00
mapObj [ " battle_scene " ] = map - > battle_scene ;
// Connections
if ( map - > connections . length ( ) > 0 ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : array connectionsArr ;
2019-02-01 17:43:25 +00:00
for ( MapConnection * connection : map - > connections ) {
2021-02-15 09:21:41 +00:00
if ( mapNamesToMapConstants . contains ( connection - > map_name ) ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object connectionObj ;
2019-02-01 17:43:25 +00:00
connectionObj [ " direction " ] = connection - > direction ;
connectionObj [ " offset " ] = connection - > offset . toInt ( ) ;
2021-02-15 09:21:41 +00:00
connectionObj [ " map " ] = this - > mapNamesToMapConstants . value ( connection - > map_name ) ;
2019-02-01 17:43:25 +00:00
connectionsArr . append ( connectionObj ) ;
} else {
logError ( QString ( " Failed to write map connection. '%1' is not a valid map name " ) . arg ( connection - > map_name ) ) ;
}
}
mapObj [ " connections " ] = connectionsArr ;
} else {
mapObj [ " connections " ] = QJsonValue : : Null ;
}
2019-02-02 20:56:05 +00:00
if ( map - > sharedEventsMap . isEmpty ( ) ) {
// Object events
2020-03-06 03:46:25 +00:00
OrderedJson : : array objectEventsArr ;
2019-02-02 20:56:05 +00:00
for ( int i = 0 ; i < map - > events [ " object_event_group " ] . length ( ) ; i + + ) {
Event * object_event = map - > events [ " object_event_group " ] . value ( i ) ;
2020-03-06 03:46:25 +00:00
OrderedJson : : object eventObj = object_event - > buildObjectEventJSON ( ) ;
objectEventsArr . push_back ( eventObj ) ;
2019-02-01 17:43:25 +00:00
}
2019-02-02 20:56:05 +00:00
mapObj [ " object_events " ] = objectEventsArr ;
// Warp events
2020-03-06 03:46:25 +00:00
OrderedJson : : array warpEventsArr ;
2019-02-02 20:56:05 +00:00
for ( int i = 0 ; i < map - > events [ " warp_event_group " ] . length ( ) ; i + + ) {
Event * warp_event = map - > events [ " warp_event_group " ] . value ( i ) ;
2020-03-06 03:46:25 +00:00
OrderedJson : : object warpObj = warp_event - > buildWarpEventJSON ( mapNamesToMapConstants ) ;
2019-02-02 20:56:05 +00:00
warpEventsArr . append ( warpObj ) ;
}
mapObj [ " warp_events " ] = warpEventsArr ;
// Coord events
2020-03-06 03:46:25 +00:00
OrderedJson : : array coordEventsArr ;
2019-02-02 20:56:05 +00:00
for ( int i = 0 ; i < map - > events [ " coord_event_group " ] . length ( ) ; i + + ) {
Event * event = map - > events [ " coord_event_group " ] . value ( i ) ;
QString event_type = event - > get ( " event_type " ) ;
if ( event_type = = EventType : : Trigger ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object triggerObj = event - > buildTriggerEventJSON ( ) ;
2019-02-02 20:56:05 +00:00
coordEventsArr . append ( triggerObj ) ;
} else if ( event_type = = EventType : : WeatherTrigger ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object weatherObj = event - > buildWeatherTriggerEventJSON ( ) ;
2019-02-02 20:56:05 +00:00
coordEventsArr . append ( weatherObj ) ;
}
}
mapObj [ " coord_events " ] = coordEventsArr ;
// Bg Events
2020-03-06 03:46:25 +00:00
OrderedJson : : array bgEventsArr ;
2019-02-02 20:56:05 +00:00
for ( int i = 0 ; i < map - > events [ " bg_event_group " ] . length ( ) ; i + + ) {
Event * event = map - > events [ " bg_event_group " ] . value ( i ) ;
QString event_type = event - > get ( " event_type " ) ;
if ( event_type = = EventType : : Sign ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object signObj = event - > buildSignEventJSON ( ) ;
2019-02-02 20:56:05 +00:00
bgEventsArr . append ( signObj ) ;
} else if ( event_type = = EventType : : HiddenItem ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object hiddenItemObj = event - > buildHiddenItemEventJSON ( ) ;
2019-02-02 20:56:05 +00:00
bgEventsArr . append ( hiddenItemObj ) ;
} else if ( event_type = = EventType : : SecretBase ) {
2020-03-06 03:46:25 +00:00
OrderedJson : : object secretBaseObj = event - > buildSecretBaseEventJSON ( ) ;
2019-02-02 20:56:05 +00:00
bgEventsArr . append ( secretBaseObj ) ;
}
2019-02-01 17:43:25 +00:00
}
2019-02-02 20:56:05 +00:00
mapObj [ " bg_events " ] = bgEventsArr ;
} else {
mapObj [ " shared_events_map " ] = map - > sharedEventsMap ;
}
if ( ! map - > sharedScriptsMap . isEmpty ( ) ) {
mapObj [ " shared_scripts_map " ] = map - > sharedScriptsMap ;
2019-02-01 17:43:25 +00:00
}
2019-02-03 16:38:45 +00:00
// Custom header fields.
for ( QString key : map - > customHeaders . keys ( ) ) {
mapObj [ key ] = map - > customHeaders [ key ] ;
}
2020-03-06 03:46:25 +00:00
OrderedJson mapJson ( mapObj ) ;
OrderedJsonDoc jsonDoc ( & mapJson ) ;
jsonDoc . dump ( & mapFile ) ;
2019-04-20 15:06:59 +01:00
mapFile . close ( ) ;
2019-02-01 17:43:25 +00:00
saveLayoutBorder ( map ) ;
saveLayoutBlockdata ( map ) ;
saveMapHealEvents ( map ) ;
2018-09-27 00:33:08 +01:00
// Update global data structures with current map data.
updateMapLayout ( map ) ;
map - > isPersistedToFile = true ;
2020-07-31 22:12:08 +01:00
map - > editHistory . setClean ( ) ;
2018-09-27 00:33:08 +01:00
}
void Project : : updateMapLayout ( Map * map ) {
2019-02-01 17:43:25 +00:00
if ( ! mapLayoutsTableMaster . contains ( map - > layoutId ) ) {
mapLayoutsTableMaster . append ( map - > layoutId ) ;
2018-09-27 00:33:08 +01:00
}
// Deep copy
2019-02-01 17:43:25 +00:00
MapLayout * layout = mapLayouts . value ( map - > layoutId ) ;
2018-09-27 00:33:08 +01:00
MapLayout * newLayout = new MapLayout ( ) ;
* newLayout = * layout ;
2019-02-01 17:43:25 +00:00
mapLayoutsMaster . insert ( map - > layoutId , newLayout ) ;
2018-09-27 00:33:08 +01:00
}
void Project : : saveAllDataStructures ( ) {
2019-02-01 17:43:25 +00:00
saveMapLayouts ( ) ;
saveMapGroups ( ) ;
2018-09-27 00:33:08 +01:00
saveMapConstantsHeader ( ) ;
2019-06-15 23:49:30 +01:00
saveWildMonData ( ) ;
2018-09-27 00:33:08 +01:00
}
void Project : : loadTilesetAssets ( Tileset * tileset ) {
QString category = ( tileset - > is_secondary = = " TRUE " ) ? " secondary " : " primary " ;
if ( tileset - > name . isNull ( ) ) {
return ;
}
2020-03-17 04:29:54 +00:00
QRegularExpression re ( " ([a-z]) ( [ A - Z0 - 9 ] ) " ) ;
2018-10-03 01:01:15 +01:00
QString tilesetName = tileset - > name ;
2021-02-16 11:15:54 +00:00
QString dir_path = root + " /data/tilesets/ " + category + ' / ' + tilesetName . replace ( " gTileset_ " , " " ) . replace ( re , " \\ 1_ \\ 2 " ) . toLower ( ) ;
2018-09-27 00:33:08 +01:00
2021-02-16 11:15:54 +00:00
const QList < QStringList > graphics = parser . parseAsm ( " data/tilesets/graphics.inc " ) ;
const QStringList tiles_values = parser . getLabelValues ( graphics , tileset - > tiles_label ) ;
const QStringList palettes_values = parser . getLabelValues ( graphics , tileset - > palettes_label ) ;
2018-09-27 00:33:08 +01:00
QString tiles_path ;
2021-02-16 11:15:54 +00:00
if ( ! tiles_values . isEmpty ( ) ) {
tiles_path = root + ' / ' + tiles_values . value ( 0 ) . section ( ' " ' , 1 , 1 ) ;
2018-09-27 00:33:08 +01:00
} else {
tiles_path = dir_path + " /tiles.4bpp " ;
if ( tileset - > is_compressed = = " TRUE " ) {
tiles_path + = " .lz " ;
}
}
2021-02-16 11:15:54 +00:00
if ( ! palettes_values . isEmpty ( ) ) {
for ( const auto & value : palettes_values ) {
tileset - > palettePaths . append ( this - > fixPalettePath ( root + ' / ' + value . section ( ' " ' , 1 , 1 ) ) ) ;
2018-09-27 00:33:08 +01:00
}
} else {
QString palettes_dir_path = dir_path + " /palettes " ;
for ( int i = 0 ; i < 16 ; i + + ) {
2021-02-16 11:15:54 +00:00
tileset - > palettePaths . append ( palettes_dir_path + ' / ' + QString ( " %1 " ) . arg ( i , 2 , 10 , QLatin1Char ( ' 0 ' ) ) + " .pal " ) ;
2018-09-27 00:33:08 +01:00
}
}
2021-02-16 11:15:54 +00:00
const QList < QStringList > metatiles_macros = parser . parseAsm ( " data/tilesets/metatiles.inc " ) ;
const QStringList metatiles_values = parser . getLabelValues ( metatiles_macros , tileset - > metatiles_label ) ;
if ( ! metatiles_values . isEmpty ( ) ) {
tileset - > metatiles_path = root + ' / ' + metatiles_values . value ( 0 ) . section ( ' " ' , 1 , 1 ) ;
2018-09-27 00:33:08 +01:00
} else {
2018-10-03 01:01:09 +01:00
tileset - > metatiles_path = dir_path + " /metatiles.bin " ;
2018-09-27 00:33:08 +01:00
}
2021-02-16 11:15:54 +00:00
const QStringList metatile_attrs_values = parser . getLabelValues ( metatiles_macros , tileset - > metatile_attrs_label ) ;
if ( ! metatile_attrs_values . isEmpty ( ) ) {
tileset - > metatile_attrs_path = root + ' / ' + metatile_attrs_values . value ( 0 ) . section ( ' " ' , 1 , 1 ) ;
2018-09-27 00:33:08 +01:00
} else {
2018-10-03 01:01:09 +01:00
tileset - > metatile_attrs_path = dir_path + " /metatile_attributes.bin " ;
2018-09-27 00:33:08 +01:00
}
tiles_path = fixGraphicPath ( tiles_path ) ;
2018-10-03 01:01:24 +01:00
tileset - > tilesImagePath = tiles_path ;
2020-02-12 16:22:40 +00:00
QImage image ;
if ( QFile : : exists ( tileset - > tilesImagePath ) ) {
image = QImage ( tileset - > tilesImagePath ) ;
} else {
image = QImage ( 8 , 8 , QImage : : Format_Indexed8 ) ;
}
2018-10-03 01:01:24 +01:00
this - > loadTilesetTiles ( tileset , image ) ;
this - > loadTilesetMetatiles ( tileset ) ;
2019-04-04 06:44:31 +01:00
this - > loadTilesetMetatileLabels ( tileset ) ;
2018-10-03 01:01:24 +01:00
// palettes
2021-02-17 02:45:54 +00:00
QList < QList < QRgb > > palettes ;
QList < QList < QRgb > > palettePreviews ;
2018-10-03 01:01:41 +01:00
for ( int i = 0 ; i < tileset - > palettePaths . length ( ) ; i + + ) {
2018-10-03 01:01:24 +01:00
QList < QRgb > palette ;
2018-10-03 01:01:41 +01:00
QString path = tileset - > palettePaths . value ( i ) ;
2019-05-06 17:42:09 +01:00
QString text = parser . readTextFile ( path ) ;
2018-10-03 01:01:41 +01:00
if ( ! text . isNull ( ) ) {
2020-08-27 01:42:42 +01:00
QStringList lines = text . split ( QRegExp ( " [ \r \n ] " ) , Qt : : SkipEmptyParts ) ;
2018-10-03 01:01:41 +01:00
if ( lines . length ( ) = = 19 & & lines [ 0 ] = = " JASC-PAL " & & lines [ 1 ] = = " 0100 " & & lines [ 2 ] = = " 16 " ) {
for ( int j = 0 ; j < 16 ; j + + ) {
2020-08-27 01:42:42 +01:00
QStringList rgb = lines [ j + 3 ] . split ( QRegExp ( " " ) , Qt : : SkipEmptyParts ) ;
2018-10-03 01:01:41 +01:00
if ( rgb . length ( ) ! = 3 ) {
2018-12-20 23:30:35 +00:00
logWarn ( QString ( " Invalid tileset palette RGB value: '%1' " ) . arg ( lines [ j + 3 ] ) ) ;
2018-10-03 01:01:41 +01:00
palette . append ( qRgb ( ( j - 3 ) * 16 , ( j - 3 ) * 16 , ( j - 3 ) * 16 ) ) ;
} else {
int red = rgb [ 0 ] . toInt ( ) ;
int green = rgb [ 1 ] . toInt ( ) ;
int blue = rgb [ 2 ] . toInt ( ) ;
QRgb color = qRgb ( red , green , blue ) ;
palette . append ( color ) ;
}
}
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Invalid JASC-PAL palette file for tileset: '%1' " ) . arg ( path ) ) ;
2018-10-03 01:01:41 +01:00
for ( int j = 0 ; j < 16 ; j + + ) {
palette . append ( qRgb ( j * 16 , j * 16 , j * 16 ) ) ;
}
2018-10-03 01:01:24 +01:00
}
} else {
for ( int j = 0 ; j < 16 ; j + + ) {
palette . append ( qRgb ( j * 16 , j * 16 , j * 16 ) ) ;
}
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open tileset palette path '%1' " ) . arg ( path ) ) ;
2018-10-03 01:01:24 +01:00
}
2021-02-17 02:45:54 +00:00
palettes . append ( palette ) ;
palettePreviews . append ( palette ) ;
2018-10-03 01:01:24 +01:00
}
tileset - > palettes = palettes ;
2020-05-03 16:31:44 +01:00
tileset - > palettePreviews = palettePreviews ;
2018-10-03 01:01:24 +01:00
}
2018-09-27 00:33:08 +01:00
2018-10-03 01:01:24 +01:00
void Project : : loadTilesetTiles ( Tileset * tileset , QImage image ) {
2021-02-17 02:45:54 +00:00
QList < QImage > tiles ;
2018-09-27 00:33:08 +01:00
int w = 8 ;
int h = 8 ;
2018-10-03 01:01:24 +01:00
for ( int y = 0 ; y < image . height ( ) ; y + = h )
for ( int x = 0 ; x < image . width ( ) ; x + = w ) {
QImage tile = image . copy ( x , y , w , h ) ;
2021-02-17 02:45:54 +00:00
tiles . append ( tile ) ;
2018-09-27 00:33:08 +01:00
}
2018-10-03 01:01:24 +01:00
tileset - > tilesImage = image ;
2018-09-27 00:33:08 +01:00
tileset - > tiles = tiles ;
2018-10-03 01:01:24 +01:00
}
2018-09-27 00:33:08 +01:00
2018-10-03 01:01:24 +01:00
void Project : : loadTilesetMetatiles ( Tileset * tileset ) {
2018-10-03 01:01:09 +01:00
QFile metatiles_file ( tileset - > metatiles_path ) ;
2018-09-27 00:33:08 +01:00
if ( metatiles_file . open ( QIODevice : : ReadOnly ) ) {
QByteArray data = metatiles_file . readAll ( ) ;
2020-06-25 06:32:42 +01:00
int metatile_data_length = projectConfig . getTripleLayerMetatilesEnabled ( ) ? 24 : 16 ;
int num_metatiles = data . length ( ) / metatile_data_length ;
int num_layers = projectConfig . getTripleLayerMetatilesEnabled ( ) ? 3 : 2 ;
2021-02-17 02:45:54 +00:00
QList < Metatile * > metatiles ;
2018-09-27 00:33:08 +01:00
for ( int i = 0 ; i < num_metatiles ; i + + ) {
Metatile * metatile = new Metatile ;
int index = i * ( 2 * 4 * num_layers ) ;
for ( int j = 0 ; j < 4 * num_layers ; j + + ) {
uint16_t word = data [ index + + ] & 0xff ;
word + = ( data [ index + + ] & 0xff ) < < 8 ;
Tile tile ;
tile . tile = word & 0x3ff ;
tile . xflip = ( word > > 10 ) & 1 ;
tile . yflip = ( word > > 11 ) & 1 ;
tile . palette = ( word > > 12 ) & 0xf ;
2021-02-16 17:14:27 +00:00
metatile - > tiles . append ( tile ) ;
2018-09-27 00:33:08 +01:00
}
2021-02-17 02:45:54 +00:00
metatiles . append ( metatile ) ;
2018-09-27 00:33:08 +01:00
}
tileset - > metatiles = metatiles ;
} else {
2021-02-17 02:45:54 +00:00
tileset - > metatiles . clear ( ) ;
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open tileset metatiles file '%1' " ) . arg ( tileset - > metatiles_path ) ) ;
2018-09-27 00:33:08 +01:00
}
2018-10-03 01:01:09 +01:00
QFile attrs_file ( tileset - > metatile_attrs_path ) ;
2018-09-27 00:33:08 +01:00
if ( attrs_file . open ( QIODevice : : ReadOnly ) ) {
QByteArray data = attrs_file . readAll ( ) ;
2021-02-17 02:45:54 +00:00
int num_metatiles = tileset - > metatiles . count ( ) ;
2020-03-15 07:03:12 +00:00
if ( projectConfig . getBaseGameVersion ( ) = = BaseGameVersion : : pokefirered ) {
int num_metatileAttrs = data . length ( ) / 4 ;
if ( num_metatiles ! = num_metatileAttrs ) {
logWarn ( QString ( " Metatile count %1 does not match metatile attribute count %2 in %3 " ) . arg ( num_metatiles ) . arg ( num_metatileAttrs ) . arg ( tileset - > name ) ) ;
if ( num_metatileAttrs > num_metatiles )
num_metatileAttrs = num_metatiles ;
}
2020-03-16 20:31:08 +00:00
bool unusedAttribute = false ;
2020-03-15 07:03:12 +00:00
for ( int i = 0 ; i < num_metatileAttrs ; i + + ) {
int value = ( static_cast < unsigned char > ( data . at ( i * 4 + 3 ) ) < < 24 ) |
( static_cast < unsigned char > ( data . at ( i * 4 + 2 ) ) < < 16 ) |
( static_cast < unsigned char > ( data . at ( i * 4 + 1 ) ) < < 8 ) |
( static_cast < unsigned char > ( data . at ( i * 4 + 0 ) ) ) ;
2021-02-17 02:45:54 +00:00
tileset - > metatiles . at ( i ) - > behavior = value & 0x1FF ;
tileset - > metatiles . at ( i ) - > terrainType = ( value & 0x3E00 ) > > 9 ;
tileset - > metatiles . at ( i ) - > encounterType = ( value & 0x7000000 ) > > 24 ;
tileset - > metatiles . at ( i ) - > layerType = ( value & 0x60000000 ) > > 29 ;
2020-03-16 20:31:08 +00:00
if ( value & ~ ( 0x67003FFF ) )
unusedAttribute = true ;
2020-03-15 07:03:12 +00:00
}
2020-03-16 20:31:08 +00:00
if ( unusedAttribute )
logWarn ( QString ( " Unrecognized metatile attributes in %1 will not be saved. " ) . arg ( tileset - > metatile_attrs_path ) ) ;
2020-03-15 07:03:12 +00:00
} else {
int num_metatileAttrs = data . length ( ) / 2 ;
if ( num_metatiles ! = num_metatileAttrs ) {
logWarn ( QString ( " Metatile count %1 does not match metatile attribute count %2 in %3 " ) . arg ( num_metatiles ) . arg ( num_metatileAttrs ) . arg ( tileset - > name ) ) ;
if ( num_metatileAttrs > num_metatiles )
num_metatileAttrs = num_metatiles ;
}
for ( int i = 0 ; i < num_metatileAttrs ; i + + ) {
int value = ( static_cast < unsigned char > ( data . at ( i * 2 + 1 ) ) < < 8 ) | static_cast < unsigned char > ( data . at ( i * 2 ) ) ;
2021-02-17 02:45:54 +00:00
tileset - > metatiles . at ( i ) - > behavior = value & 0xFF ;
tileset - > metatiles . at ( i ) - > layerType = ( value & 0xF000 ) > > 12 ;
tileset - > metatiles . at ( i ) - > encounterType = 0 ;
tileset - > metatiles . at ( i ) - > terrainType = 0 ;
2020-03-15 07:03:12 +00:00
}
2018-10-03 01:01:09 +01:00
}
2018-09-27 00:33:08 +01:00
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open tileset metatile attributes file '%1' " ) . arg ( tileset - > metatile_attrs_path ) ) ;
2018-09-27 00:33:08 +01:00
}
}
2019-04-04 06:44:31 +01:00
void Project : : loadTilesetMetatileLabels ( Tileset * tileset ) {
2019-09-09 23:24:30 +01:00
QString tilesetPrefix = QString ( " METATILE_%1_ " ) . arg ( QString ( tileset - > name ) . replace ( " gTileset_ " , " " ) ) ;
2020-05-22 20:52:34 +01:00
QString metatileLabelsFilename = " include/constants/metatile_labels.h " ;
fileWatcher . addPath ( root + " / " + metatileLabelsFilename ) ;
QMap < QString , int > labels = parser . readCDefines ( metatileLabelsFilename , QStringList ( ) < < tilesetPrefix ) ;
2019-09-09 23:24:30 +01:00
for ( QString labelName : labels . keys ( ) ) {
int metatileId = labels [ labelName ] ;
// subtract Project::num_tiles_primary from secondary metatiles
Metatile * metatile = Tileset : : getMetatile ( metatileId - ( tileset - > is_secondary = = " TRUE " ? Project : : num_tiles_primary : 0 ) , tileset , nullptr ) ;
if ( metatile ) {
metatile - > label = labelName . replace ( tilesetPrefix , " " ) ;
} else {
QString hexString = QString ( " %1 " ) . arg ( metatileId , 3 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ;
logError ( QString ( " Metatile 0x%1 cannot be found in tileset '%2' " ) . arg ( hexString , tileset - > name ) ) ;
2019-04-04 06:44:31 +01:00
}
}
}
2021-02-14 21:34:17 +00:00
Blockdata Project : : readBlockdata ( QString path ) {
Blockdata blockdata ;
2018-09-27 00:33:08 +01:00
QFile file ( path ) ;
if ( file . open ( QIODevice : : ReadOnly ) ) {
QByteArray data = file . readAll ( ) ;
for ( int i = 0 ; ( i + 1 ) < data . length ( ) ; i + = 2 ) {
uint16_t word = static_cast < uint16_t > ( ( data [ i ] & 0xff ) + ( ( data [ i + 1 ] & 0xff ) < < 8 ) ) ;
2021-02-14 21:34:17 +00:00
blockdata . append ( word ) ;
2018-09-27 00:33:08 +01:00
}
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Failed to open blockdata path '%1' " ) . arg ( path ) ) ;
2018-09-27 00:33:08 +01:00
}
return blockdata ;
}
Map * Project : : getMap ( QString map_name ) {
2021-02-15 16:33:30 +00:00
if ( mapCache . contains ( map_name ) ) {
return mapCache . value ( map_name ) ;
2018-09-27 00:33:08 +01:00
} else {
Map * map = loadMap ( map_name ) ;
return map ;
}
}
2018-10-03 01:01:15 +01:00
Tileset * Project : : getTileset ( QString label , bool forceLoad ) {
Tileset * existingTileset = nullptr ;
2021-02-15 16:33:30 +00:00
if ( tilesetCache . contains ( label ) ) {
existingTileset = tilesetCache . value ( label ) ;
2018-10-03 01:01:15 +01:00
}
if ( existingTileset & & ! forceLoad ) {
2021-02-17 02:45:54 +00:00
return existingTileset ;
2018-09-27 00:33:08 +01:00
} else {
2018-10-03 01:01:15 +01:00
Tileset * tileset = loadTileset ( label , existingTileset ) ;
2018-09-27 00:33:08 +01:00
return tileset ;
}
}
void Project : : saveTextFile ( QString path , QString text ) {
QFile file ( path ) ;
if ( file . open ( QIODevice : : WriteOnly ) ) {
file . write ( text . toUtf8 ( ) ) ;
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open '%1' for writing: " ) . arg ( path ) + file . errorString ( ) ) ;
2018-09-27 00:33:08 +01:00
}
}
void Project : : appendTextFile ( QString path , QString text ) {
QFile file ( path ) ;
if ( file . open ( QIODevice : : Append ) ) {
file . write ( text . toUtf8 ( ) ) ;
} else {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not open '%1' for appending: " ) . arg ( path ) + file . errorString ( ) ) ;
2018-09-27 00:33:08 +01:00
}
}
void Project : : deleteFile ( QString path ) {
QFile file ( path ) ;
if ( file . exists ( ) & & ! file . remove ( ) ) {
2018-12-20 23:30:35 +00:00
logError ( QString ( " Could not delete file '%1': " ) . arg ( path ) + file . errorString ( ) ) ;
2018-09-27 00:33:08 +01:00
}
}
2020-02-12 15:13:58 +00:00
bool Project : : readWildMonData ( ) {
extraEncounterGroups . clear ( ) ;
wildMonFields . clear ( ) ;
wildMonData . clear ( ) ;
encounterGroupLabels . clear ( ) ;
if ( ! projectConfig . getEncounterJsonActive ( ) ) {
return true ;
}
2019-07-03 21:21:48 +01:00
2019-06-13 03:20:28 +01:00
QString wildMonJsonFilepath = QString ( " %1/src/data/wild_encounters.json " ) . arg ( root ) ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( wildMonJsonFilepath ) ;
2019-06-13 03:20:28 +01:00
QJsonDocument wildMonsJsonDoc ;
if ( ! parser . tryParseJsonFile ( & wildMonsJsonDoc , wildMonJsonFilepath ) ) {
logError ( QString ( " Failed to read wild encounters from %1 " ) . arg ( wildMonJsonFilepath ) ) ;
2020-02-12 15:13:58 +00:00
return false ;
2019-06-13 03:20:28 +01:00
}
QJsonObject wildMonObj = wildMonsJsonDoc . object ( ) ;
for ( auto subObjectRef : wildMonObj [ " wild_encounter_groups " ] . toArray ( ) ) {
QJsonObject subObject = subObjectRef . toObject ( ) ;
2019-06-15 23:49:30 +01:00
if ( ! subObject [ " for_maps " ] . toBool ( ) ) {
2020-03-06 03:46:25 +00:00
QString err ;
QString subObjson = QJsonDocument ( subObject ) . toJson ( ) ;
OrderedJson : : object orderedSubObject = OrderedJson : : parse ( subObjson , err ) . object_items ( ) ;
extraEncounterGroups . push_back ( orderedSubObject ) ;
if ( ! err . isEmpty ( ) ) {
logWarn ( QString ( " Encountered a problem while parsing extra encounter groups: %1 " ) . arg ( err ) ) ;
}
2019-06-15 23:49:30 +01:00
continue ;
}
2019-06-13 03:20:28 +01:00
2019-06-13 17:14:49 +01:00
for ( auto field : subObject [ " fields " ] . toArray ( ) ) {
2019-09-30 00:07:34 +01:00
EncounterField encounterField ;
encounterField . name = field . toObject ( ) [ " type " ] . toString ( ) ;
for ( auto val : field . toObject ( ) [ " encounter_rates " ] . toArray ( ) ) {
encounterField . encounterRates . append ( val . toInt ( ) ) ;
}
for ( QString group : field . toObject ( ) [ " groups " ] . toObject ( ) . keys ( ) ) {
for ( auto slotNum : field . toObject ( ) [ " groups " ] . toObject ( ) [ group ] . toArray ( ) ) {
encounterField . groups [ group ] . append ( slotNum . toInt ( ) ) ;
}
}
2019-06-15 23:49:30 +01:00
wildMonFields . append ( encounterField ) ;
2019-06-13 17:14:49 +01:00
}
2019-06-13 03:20:28 +01:00
QJsonArray encounters = subObject [ " encounters " ] . toArray ( ) ;
for ( QJsonValue encounter : encounters ) {
2019-09-21 23:22:51 +01:00
QString mapConstant = encounter . toObject ( ) . value ( " map " ) . toString ( ) ;
2019-06-15 23:49:30 +01:00
2019-06-13 03:20:28 +01:00
WildPokemonHeader header ;
2019-09-30 00:07:34 +01:00
for ( EncounterField monField : wildMonFields ) {
QString field = monField . name ;
2019-09-21 23:22:51 +01:00
if ( encounter . toObject ( ) . value ( field ) ! = QJsonValue : : Undefined ) {
2019-06-13 17:14:49 +01:00
header . wildMons [ field ] . active = true ;
2019-09-21 23:22:51 +01:00
header . wildMons [ field ] . encounterRate = encounter . toObject ( ) . value ( field ) . toObject ( ) . value ( " encounter_rate " ) . toInt ( ) ;
for ( QJsonValue mon : encounter . toObject ( ) . value ( field ) . toObject ( ) . value ( " mons " ) . toArray ( ) ) {
2019-09-22 01:48:53 +01:00
WildPokemon newMon ;
newMon . minLevel = mon . toObject ( ) . value ( " min_level " ) . toInt ( ) ;
newMon . maxLevel = mon . toObject ( ) . value ( " max_level " ) . toInt ( ) ;
newMon . species = mon . toObject ( ) . value ( " species " ) . toString ( ) ;
header . wildMons [ field ] . wildPokemon . append ( newMon ) ;
2019-06-13 17:14:49 +01:00
}
}
}
2020-04-20 15:18:03 +01:00
wildMonData [ mapConstant ] . insert ( { encounter . toObject ( ) . value ( " base_label " ) . toString ( ) , header } ) ;
2019-09-21 23:22:51 +01:00
encounterGroupLabels . append ( encounter . toObject ( ) . value ( " base_label " ) . toString ( ) ) ;
2019-06-13 03:20:28 +01:00
}
}
2020-02-12 15:13:58 +00:00
return true ;
2019-06-13 03:20:28 +01:00
}
2020-02-12 16:22:40 +00:00
bool Project : : readMapGroups ( ) {
2021-02-15 09:21:41 +00:00
mapConstantsToMapNames . clear ( ) ;
mapNamesToMapConstants . clear ( ) ;
mapGroups . clear ( ) ;
2020-02-12 16:22:40 +00:00
2019-02-01 17:43:25 +00:00
QString mapGroupsFilepath = QString ( " %1/data/maps/map_groups.json " ) . arg ( root ) ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( mapGroupsFilepath ) ;
2019-04-20 15:06:59 +01:00
QJsonDocument mapGroupsDoc ;
2019-05-06 17:42:09 +01:00
if ( ! parser . tryParseJsonFile ( & mapGroupsDoc , mapGroupsFilepath ) ) {
2019-04-20 15:06:59 +01:00
logError ( QString ( " Failed to read map groups from %1 " ) . arg ( mapGroupsFilepath ) ) ;
2020-02-12 16:22:40 +00:00
return false ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
QJsonObject mapGroupsObj = mapGroupsDoc . object ( ) ;
QJsonArray mapGroupOrder = mapGroupsObj [ " group_order " ] . toArray ( ) ;
2018-09-27 00:33:08 +01:00
QList < QStringList > groupedMaps ;
2021-02-15 09:21:41 +00:00
QStringList maps ;
QStringList groups ;
2019-02-01 17:43:25 +00:00
for ( int groupIndex = 0 ; groupIndex < mapGroupOrder . size ( ) ; groupIndex + + ) {
QString groupName = mapGroupOrder . at ( groupIndex ) . toString ( ) ;
QJsonArray mapNames = mapGroupsObj . value ( groupName ) . toArray ( ) ;
2018-09-27 00:33:08 +01:00
groupedMaps . append ( QStringList ( ) ) ;
2021-02-15 09:21:41 +00:00
groups . append ( groupName ) ;
2019-02-01 17:43:25 +00:00
for ( int j = 0 ; j < mapNames . size ( ) ; j + + ) {
QString mapName = mapNames . at ( j ) . toString ( ) ;
2021-02-15 09:21:41 +00:00
mapGroups . insert ( mapName , groupIndex ) ;
2019-02-01 17:43:25 +00:00
groupedMaps [ groupIndex ] . append ( mapName ) ;
2021-02-15 09:21:41 +00:00
maps . append ( mapName ) ;
2018-09-27 00:33:08 +01:00
2019-02-01 17:43:25 +00:00
// Build the mapping and reverse mapping between map constants and map names.
QString mapConstant = Map : : mapConstantFromName ( mapName ) ;
2021-02-15 09:21:41 +00:00
mapConstantsToMapNames . insert ( mapConstant , mapName ) ;
mapNamesToMapConstants . insert ( mapName , mapConstant ) ;
2018-09-27 00:33:08 +01:00
}
}
2021-02-15 09:21:41 +00:00
mapConstantsToMapNames . insert ( NONE_MAP_CONSTANT , NONE_MAP_NAME ) ;
mapNamesToMapConstants . insert ( NONE_MAP_NAME , NONE_MAP_CONSTANT ) ;
maps . append ( NONE_MAP_NAME ) ;
2018-09-27 00:33:08 +01:00
groupNames = groups ;
groupedMapNames = groupedMaps ;
mapNames = maps ;
2020-02-12 16:22:40 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2019-02-01 17:43:25 +00:00
Map * Project : : addNewMapToGroup ( QString mapName , int groupNum , Map * newMap , bool existingLayout ) {
2021-02-15 09:21:41 +00:00
mapNames . append ( mapName ) ;
mapGroups . insert ( mapName , groupNum ) ;
2019-01-07 23:14:44 +00:00
groupedMapNames [ groupNum ] . append ( mapName ) ;
2021-02-18 22:41:36 +00:00
newMap - > isPersistedToFile = false ;
newMap - > setName ( mapName ) ;
2019-01-07 23:14:44 +00:00
2021-02-18 22:41:36 +00:00
mapConstantsToMapNames . insert ( newMap - > constantName , newMap - > name ) ;
mapNamesToMapConstants . insert ( newMap - > name , newMap - > constantName ) ;
2019-02-01 17:43:25 +00:00
if ( ! existingLayout ) {
2021-02-18 22:41:36 +00:00
mapLayouts . insert ( newMap - > layoutId , newMap - > layout ) ;
mapLayoutsTable . append ( newMap - > layoutId ) ;
setNewMapBlockdata ( newMap ) ;
setNewMapBorder ( newMap ) ;
2019-01-07 23:14:44 +00:00
}
2021-02-18 22:41:36 +00:00
loadMapTilesets ( newMap ) ;
setNewMapEvents ( newMap ) ;
setNewMapConnections ( newMap ) ;
2019-01-07 23:14:44 +00:00
2021-02-18 22:41:36 +00:00
return newMap ;
2019-01-07 23:14:44 +00:00
}
2018-09-27 00:33:08 +01:00
QString Project : : getNewMapName ( ) {
// Ensure default name doesn't already exist.
int i = 0 ;
QString newMapName ;
do {
newMapName = QString ( " NewMap%1 " ) . arg ( + + i ) ;
2021-02-15 09:21:41 +00:00
} while ( mapNames . contains ( newMapName ) ) ;
2018-09-27 00:33:08 +01:00
return newMapName ;
}
QStringList Project : : getVisibilities ( ) {
// TODO
QStringList names ;
for ( int i = 0 ; i < 16 ; i + + ) {
names . append ( QString ( " %1 " ) . arg ( i ) ) ;
}
return names ;
}
2019-04-29 01:20:48 +01:00
Project : : DataQualifiers Project : : getDataQualifiers ( QString text , QString label ) {
Project : : DataQualifiers qualifiers ;
QRegularExpression regex ( QString ( " \\ s*(?<static>static \\ s*) ? ( ? < const > const \ \ s * ) ? [ A - Za - z0 - 9 _ \ \ s ] * \ \ b % 1 \ \ b " ).arg(label)) ;
QRegularExpressionMatch match = regex . match ( text ) ;
qualifiers . isStatic = match . captured ( " static " ) . isNull ( ) ? false : true ;
qualifiers . isConst = match . captured ( " const " ) . isNull ( ) ? false : true ;
return qualifiers ;
}
2019-03-23 14:49:30 +00:00
QMap < QString , QStringList > Project : : getTilesetLabels ( ) {
2018-09-27 00:33:08 +01:00
QMap < QString , QStringList > allTilesets ;
QStringList primaryTilesets ;
QStringList secondaryTilesets ;
allTilesets . insert ( " primary " , primaryTilesets ) ;
allTilesets . insert ( " secondary " , secondaryTilesets ) ;
2019-05-06 17:42:09 +01:00
2020-02-12 15:13:58 +00:00
QString filename = " data/tilesets/headers.inc " ;
QString headers_text = parser . readTextFile ( root + " / " + filename ) ;
if ( headers_text . isEmpty ( ) ) {
logError ( QString ( " Failed to read tileset labels from %1. " ) . arg ( filename ) ) ;
return QMap < QString , QStringList > ( ) ;
}
2018-09-27 00:33:08 +01:00
2019-05-05 16:45:03 +01:00
QRegularExpression re ( " (?<label>[A-Za-z0-9_]*) : { 1 , 2 } [ A - Za - z0 - 9 _ @ ] * \ \ s + . + \ \ s + \ \ . byte \ \ s + ( ? < isSecondary > [ A - Za - z0 - 9 _ ] + ) " );
QRegularExpressionMatchIterator iter = re . globalMatch ( headers_text ) ;
2018-09-27 00:33:08 +01:00
2019-05-05 16:45:03 +01:00
while ( iter . hasNext ( ) ) {
QRegularExpressionMatch match = iter . next ( ) ;
QString tilesetLabel = match . captured ( " label " ) ;
QString secondaryTilesetValue = match . captured ( " isSecondary " ) ;
2018-09-27 00:33:08 +01:00
2019-05-05 16:45:03 +01:00
if ( secondaryTilesetValue ! = " 1 " & & secondaryTilesetValue ! = " TRUE "
& & secondaryTilesetValue ! = " 0 " & & secondaryTilesetValue ! = " FALSE " ) {
2019-05-05 21:11:00 +01:00
logWarn ( QString ( " Unexpected secondary tileset flag found in %1. Expected 'TRUE', 'FALSE', '1', or '0', but found '%2' " )
. arg ( tilesetLabel ) . arg ( secondaryTilesetValue ) ) ;
2019-05-05 16:45:03 +01:00
continue ;
2018-09-27 00:33:08 +01:00
}
2019-05-05 16:45:03 +01:00
bool isSecondaryTileset = ( secondaryTilesetValue = = " TRUE " | | secondaryTilesetValue = = " 1 " ) ;
if ( isSecondaryTileset )
allTilesets [ " secondary " ] . append ( tilesetLabel ) ;
else
allTilesets [ " primary " ] . append ( tilesetLabel ) ;
2018-09-27 00:33:08 +01:00
}
2019-09-21 21:59:14 +01:00
this - > tilesetLabels = allTilesets ;
2018-09-27 00:33:08 +01:00
return allTilesets ;
}
2020-02-12 15:13:58 +00:00
bool Project : : readTilesetProperties ( ) {
2019-05-05 21:11:00 +01:00
QStringList definePrefixes ;
2021-01-25 16:04:07 +00:00
definePrefixes < < " \\ bNUM_ " ;
2020-04-08 05:42:38 +01:00
QString filename = " include/fieldmap.h " ;
fileWatcher . addPath ( root + " / " + filename ) ;
QMap < QString , int > defines = parser . readCDefines ( filename , definePrefixes ) ;
2018-09-27 00:33:08 +01:00
2019-05-05 21:11:00 +01:00
auto it = defines . find ( " NUM_TILES_IN_PRIMARY " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_tiles_primary = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_TILES_IN_PRIMARY' not found. Using default (%1) instead. " )
. arg ( Project : : num_tiles_primary ) ) ;
2019-05-05 21:11:00 +01:00
}
it = defines . find ( " NUM_TILES_TOTAL " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_tiles_total = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_TILES_TOTAL' not found. Using default (%1) instead. " )
. arg ( Project : : num_tiles_total ) ) ;
2019-05-05 21:11:00 +01:00
}
it = defines . find ( " NUM_METATILES_IN_PRIMARY " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_metatiles_primary = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_METATILES_IN_PRIMARY' not found. Using default (%1) instead. " )
. arg ( Project : : num_metatiles_primary ) ) ;
2019-05-05 21:11:00 +01:00
}
it = defines . find ( " NUM_METATILES_TOTAL " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_metatiles_total = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_METATILES_TOTAL' not found. Using default (%1) instead. " )
. arg ( Project : : num_metatiles_total ) ) ;
2019-05-05 21:11:00 +01:00
}
it = defines . find ( " NUM_PALS_IN_PRIMARY " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_pals_primary = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_PALS_IN_PRIMARY' not found. Using default (%1) instead. " )
. arg ( Project : : num_pals_primary ) ) ;
2019-05-05 21:11:00 +01:00
}
it = defines . find ( " NUM_PALS_TOTAL " ) ;
if ( it ! = defines . end ( ) ) {
Project : : num_pals_total = it . value ( ) ;
}
else {
2019-05-06 17:42:09 +01:00
logWarn ( QString ( " Value for tileset property 'NUM_PALS_TOTAL' not found. Using default (%1) instead. " )
. arg ( Project : : num_pals_total ) ) ;
2018-09-27 00:33:08 +01:00
}
2020-05-16 23:52:46 +01:00
return true ;
}
bool Project : : readMaxMapDataSize ( ) {
QStringList definePrefixes ;
2021-01-25 16:04:07 +00:00
definePrefixes < < " \\ bMAX_ " ;
2020-05-16 23:52:46 +01:00
QString filename = " include/fieldmap.h " ; // already in fileWatcher from readTilesetProperties
QMap < QString , int > defines = parser . readCDefines ( filename , definePrefixes ) ;
auto it = defines . find ( " MAX_MAP_DATA_SIZE " ) ;
2020-05-16 21:57:03 +01:00
if ( it ! = defines . end ( ) ) {
int min = getMapDataSize ( 1 , 1 ) ;
if ( it . value ( ) > = min ) {
Project : : max_map_data_size = it . value ( ) ;
calculateDefaultMapSize ( ) ;
} else {
// must be large enough to support a 1x1 map
logWarn ( QString ( " Value for map property 'MAX_MAP_DATA_SIZE' is %1, must be at least %2. Using default (%3) instead. " )
. arg ( it . value ( ) )
. arg ( min )
. arg ( Project : : max_map_data_size ) ) ;
}
}
else {
logWarn ( QString ( " Value for map property 'MAX_MAP_DATA_SIZE' not found. Using default (%1) instead. " )
. arg ( Project : : max_map_data_size ) ) ;
}
2020-02-12 15:13:58 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 14:12:12 +00:00
bool Project : : readRegionMapSections ( ) {
2019-02-25 18:31:34 +00:00
this - > mapSectionNameToValue . clear ( ) ;
this - > mapSectionValueToName . clear ( ) ;
2019-05-05 21:11:00 +01:00
2021-01-25 16:04:07 +00:00
QStringList prefixes = ( QStringList ( ) < < " \\ bMAPSEC_ " ) ;
2020-02-12 14:12:12 +00:00
QString filename = " include/constants/region_map_sections.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2020-02-12 14:12:12 +00:00
this - > mapSectionNameToValue = parser . readCDefines ( filename , prefixes ) ;
if ( this - > mapSectionNameToValue . isEmpty ( ) ) {
logError ( QString ( " Failed to read region map sections from %1. " ) . arg ( filename ) ) ;
return false ;
}
2019-05-05 21:11:00 +01:00
for ( QString defineName : this - > mapSectionNameToValue . keys ( ) ) {
this - > mapSectionValueToName . insert ( this - > mapSectionNameToValue [ defineName ] , defineName ) ;
2019-02-25 18:31:34 +00:00
}
2020-02-12 14:12:12 +00:00
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readHealLocations ( ) {
dataQualifiers . clear ( ) ;
2020-04-29 17:34:49 +01:00
healLocations . clear ( ) ;
2020-02-12 15:13:58 +00:00
QString filename = " src/data/heal_locations.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2020-02-12 15:13:58 +00:00
QString text = parser . readTextFile ( root + " / " + filename ) ;
2019-04-28 14:05:37 +01:00
text . replace ( QRegularExpression ( " //.*?( \r \n ?| \n )|/ \\ *.*? \\ */ " , QRegularExpression : : DotMatchesEverythingOption ) , " " ) ;
2020-05-22 22:51:56 +01:00
if ( projectConfig . getHealLocationRespawnDataEnabled ( ) ) {
2020-03-20 07:09:48 +00:00
dataQualifiers . insert ( " heal_locations " , getDataQualifiers ( text , " sSpawnPoints " ) ) ;
QRegularExpression spawnRegex ( " SPAWN_(?<id>[A-Za-z0-9_]+) \ \ s * - 1 \ \ ] \ \ s * = \ \ { MAP_GROUP [ \ \ ( \ \ s ] + ( ? < map > [ A - Za - z0 - 9 _ ] + ) [ \ \ s \ \ ) ] + , \ \ s * MAP_NUM [ \ \ ( \ \ s ] + ( \ \ 2 ) [ \ \ s \ \ ) ] + , \ \ s * ( ? < x > [ 0 - 9 A - Fa - fx ] + ) , \ \ s * ( ? < y > [ 0 - 9 A - Fa - fx ] + ) " );
QRegularExpression respawnMapRegex ( " SPAWN_(?<id>[A-Za-z0-9_]+) \ \ s * - 1 \ \ ] \ \ s * = \ \ { MAP_GROUP [ \ \ ( \ \ s ] + ( ? < map > [ A - Za - z0 - 9 _ ] + ) [ \ \ s \ \ ) ] + , \ \ s * MAP_NUM [ \ \ ( \ \ s ] + ( \ \ 2 ) [ \ \ s \ \ ) ] + } " );
QRegularExpression respawnNPCRegex ( " SPAWN_(?<id>[A-Za-z0-9_]+) \ \ s * - 1 \ \ ] \ \ s * = ( ? < npc > [ 0 - 9 ] + ) " ) ;
QRegularExpressionMatchIterator spawns = spawnRegex . globalMatch ( text ) ;
QRegularExpressionMatchIterator respawnMaps = respawnMapRegex . globalMatch ( text ) ;
QRegularExpressionMatchIterator respawnNPCs = respawnNPCRegex . globalMatch ( text ) ;
// This would be better if idName was used to look up data from the other two arrays
// As it is, element total and order needs to be the same in the 3 arrays to work. This should always be true though
for ( int i = 1 ; spawns . hasNext ( ) ; i + + ) {
QRegularExpressionMatch spawn = spawns . next ( ) ;
QRegularExpressionMatch respawnMap = respawnMaps . next ( ) ;
QRegularExpressionMatch respawnNPC = respawnNPCs . next ( ) ;
QString idName = spawn . captured ( " id " ) ;
QString mapName = spawn . captured ( " map " ) ;
QString respawnMapName = respawnMap . captured ( " map " ) ;
unsigned x = spawn . captured ( " x " ) . toUShort ( ) ;
unsigned y = spawn . captured ( " y " ) . toUShort ( ) ;
unsigned npc = respawnNPC . captured ( " npc " ) . toUShort ( ) ;
2020-04-29 17:34:49 +01:00
healLocations . append ( HealLocation ( idName , mapName , i , x , y , respawnMapName , npc ) ) ;
2020-03-20 07:09:48 +00:00
}
} else {
dataQualifiers . insert ( " heal_locations " , getDataQualifiers ( text , " sHealLocations " ) ) ;
QRegularExpression regex ( " HEAL_LOCATION_(?<id>[A-Za-z0-9_]+) \ \ s * - 1 \ \ ] \ \ s * = \ \ { MAP_GROUP [ \ \ ( \ \ s ] + ( ? < map > [ A - Za - z0 - 9 _ ] + ) [ \ \ s \ \ ) ] + , \ \ s * MAP_NUM [ \ \ ( \ \ s ] + ( \ \ 2 ) [ \ \ s \ \ ) ] + , \ \ s * ( ? < x > [ 0 - 9 A - Fa - fx ] + ) , \ \ s * ( ? < y > [ 0 - 9 A - Fa - fx ] + ) " );
QRegularExpressionMatchIterator iter = regex . globalMatch ( text ) ;
for ( int i = 1 ; iter . hasNext ( ) ; i + + ) {
QRegularExpressionMatch match = iter . next ( ) ;
QString idName = match . captured ( " id " ) ;
QString mapName = match . captured ( " map " ) ;
unsigned x = match . captured ( " x " ) . toUShort ( ) ;
unsigned y = match . captured ( " y " ) . toUShort ( ) ;
2020-04-29 17:34:49 +01:00
healLocations . append ( HealLocation ( idName , mapName , i , x , y ) ) ;
2020-03-20 07:09:48 +00:00
}
2019-04-28 14:05:37 +01:00
}
2020-02-12 15:13:58 +00:00
return true ;
2019-04-28 14:05:37 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readItemNames ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bITEM_(?!(B_) ? USE_ ) " ) ; // Exclude ITEM_USE_ and ITEM_B_USE_ constants
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/items.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
itemNames = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( itemNames . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read item constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readFlagNames ( ) {
2020-07-13 21:36:43 +01:00
// First read MAX_TRAINERS_COUNT, used to skip over trainer flags
// If this fails flags may simply be out of order, no need to check for success
QString opponentsFilename = " include/constants/opponents.h " ;
fileWatcher . addPath ( root + " / " + opponentsFilename ) ;
2021-01-25 16:04:07 +00:00
QMap < QString , int > maxTrainers = parser . readCDefines ( opponentsFilename , QStringList ( ) < < " \\ bMAX_ " ) ;
2020-07-13 21:36:43 +01:00
// Parse flags
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bFLAG_ " ) ;
2020-07-13 21:36:43 +01:00
QString flagsFilename = " include/constants/flags.h " ;
fileWatcher . addPath ( root + " / " + flagsFilename ) ;
2021-02-16 12:15:47 +00:00
flagNames = parser . readCDefinesSorted ( flagsFilename , prefixes , maxTrainers ) ;
2021-02-15 16:33:30 +00:00
if ( flagNames . isEmpty ( ) ) {
2020-07-13 21:36:43 +01:00
logError ( QString ( " Failed to read flag constants from %1 " ) . arg ( flagsFilename ) ) ;
2020-02-12 15:13:58 +00:00
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readVarNames ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bVAR_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/vars.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
varNames = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( varNames . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read var constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readMovementTypes ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bMOVEMENT_TYPE_ " ) ;
2019-11-22 16:06:11 +00:00
QString filename = " include/constants/event_object_movement.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
movementTypes = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( movementTypes . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read movement type constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readInitialFacingDirections ( ) {
QString filename = " src/event_object_movement.c " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2020-02-12 15:13:58 +00:00
facingDirections = parser . readNamedIndexCArray ( filename , " gInitialMovementTypeFacingDirections " ) ;
if ( facingDirections . isEmpty ( ) ) {
logError ( QString ( " Failed to read initial movement type facing directions from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2019-04-03 00:51:33 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readMapTypes ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bMAP_TYPE_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/map_types.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
mapTypes = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( mapTypes . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read map type constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readMapBattleScenes ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bMAP_BATTLE_SCENE_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/map_types.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
mapBattleScenes = parser . readCDefinesSorted ( " include/constants/map_types.h " , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( mapBattleScenes . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read map battle scene constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readWeatherNames ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bWEATHER_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/weather.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
weatherNames = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( weatherNames . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read weather constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readCoordEventWeatherNames ( ) {
2021-02-16 12:15:47 +00:00
if ( ! projectConfig . getEventWeatherTriggerEnabled ( ) )
return true ;
2020-05-22 22:51:56 +01:00
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bCOORD_EVENT_WEATHER_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/weather.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
coordEventWeatherNames = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( coordEventWeatherNames . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read coord event weather constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readSecretBaseIds ( ) {
2021-02-16 12:15:47 +00:00
if ( ! projectConfig . getEventSecretBaseEnabled ( ) )
return true ;
2020-05-22 22:51:56 +01:00
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bSECRET_BASE_[A-Za-z0-9_]*_[0-9]+ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/secret_bases.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
secretBaseIds = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( secretBaseIds . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read secret base id constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-02-12 15:13:58 +00:00
bool Project : : readBgEventFacingDirections ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bBG_EVENT_PLAYER_FACING_ " ) ;
2019-11-22 16:06:11 +00:00
QString filename = " include/constants/event_bg.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
bgEventFacingDirections = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( bgEventFacingDirections . isEmpty ( ) ) {
2020-02-12 15:13:58 +00:00
logError ( QString ( " Failed to read bg event facing direction constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
2018-09-27 00:33:08 +01:00
}
2020-03-27 14:51:57 +00:00
bool Project : : readTrainerTypes ( ) {
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bTRAINER_TYPE_ " ) ;
2020-03-27 14:51:57 +00:00
QString filename = " include/constants/trainer_types.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-16 12:15:47 +00:00
trainerTypes = parser . readCDefinesSorted ( filename , prefixes ) ;
2021-02-15 16:33:30 +00:00
if ( trainerTypes . isEmpty ( ) ) {
2020-03-27 14:51:57 +00:00
logError ( QString ( " Failed to read trainer type constants from %1 " ) . arg ( filename ) ) ;
return false ;
}
return true ;
}
2020-02-12 15:13:58 +00:00
bool Project : : readMetatileBehaviors ( ) {
2018-10-03 01:01:09 +01:00
this - > metatileBehaviorMap . clear ( ) ;
this - > metatileBehaviorMapInverse . clear ( ) ;
2019-05-05 21:11:00 +01:00
2021-02-15 16:33:30 +00:00
QStringList prefixes ( " \\ bMB_ " ) ;
2020-02-12 15:13:58 +00:00
QString filename = " include/constants/metatile_behaviors.h " ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPath ( root + " / " + filename ) ;
2020-02-12 15:13:58 +00:00
this - > metatileBehaviorMap = parser . readCDefines ( filename , prefixes ) ;
if ( this - > metatileBehaviorMap . isEmpty ( ) ) {
2020-03-27 14:51:57 +00:00
logError ( QString ( " Failed to read metatile behaviors from %1. " ) . arg ( filename ) ) ;
2020-02-12 15:13:58 +00:00
return false ;
}
2019-05-05 21:11:00 +01:00
for ( QString defineName : this - > metatileBehaviorMap . keys ( ) ) {
this - > metatileBehaviorMapInverse . insert ( this - > metatileBehaviorMap [ defineName ] , defineName ) ;
2018-10-03 01:01:09 +01:00
}
2020-02-12 15:13:58 +00:00
return true ;
2018-10-03 01:01:09 +01:00
}
2018-09-27 00:33:08 +01:00
QStringList Project : : getSongNames ( ) {
2021-02-15 16:33:30 +00:00
QStringList songDefinePrefixes { " \\ bSE_ " , " \\ bMUS_ " } ;
2020-04-08 05:42:38 +01:00
QString filename = " include/constants/songs.h " ;
fileWatcher . addPath ( root + " / " + filename ) ;
QMap < QString , int > songDefines = parser . readCDefines ( filename , songDefinePrefixes ) ;
2019-05-06 17:42:09 +01:00
QStringList names = songDefines . keys ( ) ;
2020-04-19 15:16:49 +01:00
this - > defaultSong = names . value ( 0 , " MUS_DUMMY " ) ;
2019-05-05 21:11:00 +01:00
2018-09-27 00:33:08 +01:00
return names ;
}
QMap < QString , int > Project : : getEventObjGfxConstants ( ) {
2021-02-15 16:33:30 +00:00
QStringList eventObjGfxPrefixes ( " \\ bOBJ_EVENT_GFX_ " ) ;
2019-05-06 17:42:09 +01:00
2020-04-08 05:42:38 +01:00
QString filename = " include/constants/event_objects.h " ;
fileWatcher . addPath ( root + " / " + filename ) ;
QMap < QString , int > constants = parser . readCDefines ( filename , eventObjGfxPrefixes ) ;
2019-05-05 21:11:00 +01:00
2018-09-27 00:33:08 +01:00
return constants ;
}
2020-02-12 15:13:58 +00:00
bool Project : : readMiscellaneousConstants ( ) {
miscConstants . clear ( ) ;
if ( projectConfig . getEncounterJsonActive ( ) ) {
2020-04-08 05:42:38 +01:00
QString filename = " include/constants/pokemon.h " ;
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-15 16:33:30 +00:00
QMap < QString , int > pokemonDefines = parser . readCDefines ( filename , { " MIN_ " , " MAX_ " } ) ;
2020-02-12 15:13:58 +00:00
miscConstants . insert ( " max_level_define " , pokemonDefines . value ( " MAX_LEVEL " ) > pokemonDefines . value ( " MIN_LEVEL " ) ? pokemonDefines . value ( " MAX_LEVEL " ) : 100 ) ;
miscConstants . insert ( " min_level_define " , pokemonDefines . value ( " MIN_LEVEL " ) < pokemonDefines . value ( " MAX_LEVEL " ) ? pokemonDefines . value ( " MIN_LEVEL " ) : 1 ) ;
}
2020-07-10 21:34:42 +01:00
QString filename = " include/constants/global.h " ;
fileWatcher . addPath ( root + " / " + filename ) ;
2021-02-15 16:33:30 +00:00
QStringList definePrefixes ( " \\ bOBJECT_ " ) ;
2020-07-10 21:34:42 +01:00
QMap < QString , int > defines = parser . readCDefines ( filename , definePrefixes ) ;
auto it = defines . find ( " OBJECT_EVENT_TEMPLATES_COUNT " ) ;
if ( it ! = defines . end ( ) ) {
if ( it . value ( ) > 0 ) {
Project : : max_object_events = it . value ( ) ;
} else {
logWarn ( QString ( " Value for 'OBJECT_EVENT_TEMPLATES_COUNT' is %1, must be greater than 0. Using default (%2) instead. " )
. arg ( it . value ( ) )
. arg ( Project : : max_object_events ) ) ;
}
}
else {
logWarn ( QString ( " Value for 'OBJECT_EVENT_TEMPLATES_COUNT' not found. Using default (%1) instead. " )
. arg ( Project : : max_object_events ) ) ;
}
2020-02-12 15:13:58 +00:00
return true ;
2019-07-03 03:08:58 +01:00
}
2021-01-30 03:05:40 +00:00
bool Project : : readEventScriptLabels ( ) {
for ( const auto & filePath : getEventScriptsFilePaths ( ) )
2021-04-16 14:04:38 +01:00
globalScriptLabels < < ParseUtil : : getGlobalScriptLabels ( filePath ) ;
2021-01-30 03:05:40 +00:00
2021-04-16 14:04:38 +01:00
eventScriptLabelModel . setStringList ( globalScriptLabels ) ;
eventScriptLabelCompleter . setModel ( & eventScriptLabelModel ) ;
eventScriptLabelCompleter . setCaseSensitivity ( Qt : : CaseInsensitive ) ;
eventScriptLabelCompleter . setFilterMode ( Qt : : MatchContains ) ;
2021-01-30 03:05:40 +00:00
return true ;
}
2018-10-03 01:01:41 +01:00
QString Project : : fixPalettePath ( QString path ) {
path = path . replace ( QRegExp ( " \\ .gbapal$ " ) , " .pal " ) ;
return path ;
}
2018-09-27 00:33:08 +01:00
QString Project : : fixGraphicPath ( QString path ) {
path = path . replace ( QRegExp ( " \\ .lz$ " ) , " " ) ;
path = path . replace ( QRegExp ( " \\ .[1248]bpp$ " ) , " .png " ) ;
return path ;
}
2020-11-21 22:33:16 +00:00
QString Project : : getScriptFileExtension ( bool usePoryScript ) const {
2019-10-22 13:48:41 +01:00
if ( usePoryScript ) {
return " .pory " ;
} else {
return " .inc " ;
}
}
2020-11-21 22:33:16 +00:00
QString Project : : getScriptDefaultString ( bool usePoryScript , QString mapName ) const {
2019-10-22 13:48:41 +01:00
if ( usePoryScript )
2021-03-07 15:16:54 +00:00
return QString ( " mapscripts %1_MapScripts {} \n " ) . arg ( mapName ) ;
2019-10-22 13:48:41 +01:00
else
return QString ( " %1_MapScripts:: \n \t .byte 0 \n " ) . arg ( mapName ) ;
}
2020-11-21 22:33:16 +00:00
QString Project : : getMapScriptsFilePath ( const QString & mapName ) const {
const bool usePoryscript = projectConfig . getUsePoryScript ( ) ;
auto path = QDir : : cleanPath ( root + " /data/maps/ " + mapName + " /scripts " ) ;
auto extension = getScriptFileExtension ( usePoryscript ) ;
if ( usePoryscript & & ! QFile : : exists ( path + extension ) )
extension = getScriptFileExtension ( false ) ;
path + = extension ;
return path ;
}
2020-12-04 14:29:38 +00:00
QStringList Project : : getEventScriptsFilePaths ( ) const {
QStringList filePaths ( QDir : : cleanPath ( root + " /data/event_scripts.s " ) ) ;
const QString scriptsDir = QDir : : cleanPath ( root + " /data/scripts " ) ;
const QString mapsDir = QDir : : cleanPath ( root + " /data/maps " ) ;
const bool usePoryscript = projectConfig . getUsePoryScript ( ) ;
if ( usePoryscript ) {
QDirIterator it_pory_shared ( scriptsDir , { " *.pory " } , QDir : : Files ) ;
while ( it_pory_shared . hasNext ( ) )
filePaths < < it_pory_shared . next ( ) ;
QDirIterator it_pory_maps ( mapsDir , { " scripts.pory " } , QDir : : Files , QDirIterator : : Subdirectories ) ;
while ( it_pory_maps . hasNext ( ) )
filePaths < < it_pory_maps . next ( ) ;
}
QDirIterator it_inc_shared ( scriptsDir , { " *.inc " } , QDir : : Files ) ;
while ( it_inc_shared . hasNext ( ) )
filePaths < < it_inc_shared . next ( ) ;
QDirIterator it_inc_maps ( mapsDir , { " scripts.inc " } , QDir : : Files , QDirIterator : : Subdirectories ) ;
while ( it_inc_maps . hasNext ( ) )
filePaths < < it_inc_maps . next ( ) ;
return filePaths ;
}
2021-04-16 14:04:38 +01:00
QCompleter * Project : : getEventScriptLabelCompleter ( QStringList additionalScriptLabels ) {
additionalScriptLabels < < globalScriptLabels ;
additionalScriptLabels . removeDuplicates ( ) ;
eventScriptLabelModel . setStringList ( additionalScriptLabels ) ;
return & eventScriptLabelCompleter ;
2021-01-30 03:05:40 +00:00
}
2018-09-27 00:33:08 +01:00
void Project : : loadEventPixmaps ( QList < Event * > objects ) {
bool needs_update = false ;
for ( Event * object : objects ) {
if ( object - > pixmap . isNull ( ) ) {
needs_update = true ;
break ;
}
}
if ( ! needs_update ) {
return ;
}
QMap < QString , int > constants = getEventObjGfxConstants ( ) ;
2020-04-08 05:42:38 +01:00
fileWatcher . addPaths ( QStringList ( ) < < root + " / " + " src/data/object_events/object_event_graphics_info_pointers.h "
< < root + " / " + " src/data/object_events/object_event_graphics_info.h "
< < root + " / " + " src/data/object_events/object_event_pic_tables.h "
< < root + " / " + " src/data/object_events/object_event_graphics.h " ) ;
2020-02-13 00:13:33 +00:00
QMap < QString , QString > pointerHash = parser . readNamedIndexCArray ( " src/data/object_events/object_event_graphics_info_pointers.h " , " gObjectEventGraphicsInfoPointers " ) ;
2019-08-27 16:30:35 +01:00
2018-09-27 00:33:08 +01:00
for ( Event * object : objects ) {
if ( ! object - > pixmap . isNull ( ) ) {
continue ;
}
object - > spriteWidth = 16 ;
object - > spriteHeight = 16 ;
2019-01-11 01:59:41 +00:00
object - > usingSprite = false ;
2018-09-27 00:33:08 +01:00
QString event_type = object - > get ( " event_type " ) ;
if ( event_type = = EventType : : Object ) {
object - > pixmap = QPixmap ( " :/images/Entities_16x16.png " ) . copy ( 0 , 0 , 16 , 16 ) ;
} else if ( event_type = = EventType : : Warp ) {
object - > pixmap = QPixmap ( " :/images/Entities_16x16.png " ) . copy ( 16 , 0 , 16 , 16 ) ;
2018-12-26 20:15:35 +00:00
} else if ( event_type = = EventType : : Trigger | | event_type = = EventType : : WeatherTrigger ) {
2018-09-27 00:33:08 +01:00
object - > pixmap = QPixmap ( " :/images/Entities_16x16.png " ) . copy ( 32 , 0 , 16 , 16 ) ;
} else if ( event_type = = EventType : : Sign | | event_type = = EventType : : HiddenItem | | event_type = = EventType : : SecretBase ) {
object - > pixmap = QPixmap ( " :/images/Entities_16x16.png " ) . copy ( 48 , 0 , 16 , 16 ) ;
} else if ( event_type = = EventType : : HealLocation ) {
object - > pixmap = QPixmap ( " :/images/Entities_16x16.png " ) . copy ( 64 , 0 , 16 , 16 ) ;
}
if ( event_type = = EventType : : Object ) {
2019-06-08 19:05:01 +01:00
QString info_label = pointerHash [ object - > get ( " sprite " ) ] . replace ( " & " , " " ) ;
2020-02-13 00:13:33 +00:00
QStringList gfx_info = parser . readCArray ( " src/data/object_events/object_event_graphics_info.h " , info_label ) ;
2018-09-27 00:33:08 +01:00
QString pic_label = gfx_info . value ( 14 ) ;
QString dimensions_label = gfx_info . value ( 11 ) ;
QString subsprites_label = gfx_info . value ( 12 ) ;
2020-02-13 00:13:33 +00:00
QString gfx_label = parser . readCArray ( " src/data/object_events/object_event_pic_tables.h " , pic_label ) . value ( 0 ) ;
2018-09-27 00:33:08 +01:00
gfx_label = gfx_label . section ( QRegExp ( " [ \\ ( \\ )] " ) , 1 , 1 ) ;
2020-02-13 00:13:33 +00:00
QString path = parser . readCIncbin ( " src/data/object_events/object_event_graphics.h " , gfx_label ) ;
2018-09-27 00:33:08 +01:00
if ( ! path . isNull ( ) ) {
path = fixGraphicPath ( path ) ;
QImage spritesheet ( root + " / " + path ) ;
if ( ! spritesheet . isNull ( ) ) {
// Infer the sprite dimensions from the OAM labels.
2020-03-27 13:22:54 +00:00
int spriteWidth , spriteHeight ;
2018-09-27 00:33:08 +01:00
QRegularExpression re ( " \\ S+_( \\ d+) x ( \ \ d + ) " ) ;
QRegularExpressionMatch dimensionMatch = re . match ( dimensions_label ) ;
2020-03-27 13:22:54 +00:00
QRegularExpressionMatch oamTablesMatch = re . match ( subsprites_label ) ;
if ( oamTablesMatch . hasMatch ( ) ) {
spriteWidth = oamTablesMatch . captured ( 1 ) . toInt ( ) ;
spriteHeight = oamTablesMatch . captured ( 2 ) . toInt ( ) ;
} else if ( dimensionMatch . hasMatch ( ) ) {
spriteWidth = dimensionMatch . captured ( 1 ) . toInt ( ) ;
spriteHeight = dimensionMatch . captured ( 2 ) . toInt ( ) ;
} else {
spriteWidth = spritesheet . width ( ) ;
spriteHeight = spritesheet . height ( ) ;
2018-09-27 00:33:08 +01:00
}
2019-04-03 00:51:33 +01:00
object - > setPixmapFromSpritesheet ( spritesheet , spriteWidth , spriteHeight , object - > frame , object - > hFlip ) ;
2018-09-27 00:33:08 +01:00
}
}
}
}
}
2020-02-12 15:13:58 +00:00
bool Project : : readSpeciesIconPaths ( ) {
speciesToIconPath . clear ( ) ;
2020-04-08 05:42:38 +01:00
QString srcfilename = " src/pokemon_icon.c " ;
QString incfilename = " src/data/graphics/pokemon.h " ;
fileWatcher . addPath ( root + " / " + srcfilename ) ;
fileWatcher . addPath ( root + " / " + incfilename ) ;
QMap < QString , QString > monIconNames = parser . readNamedIndexCArray ( srcfilename , " gMonIconTable " ) ;
2019-06-13 03:20:28 +01:00
for ( QString species : monIconNames . keys ( ) ) {
2020-04-08 05:42:38 +01:00
QString path = parser . readCIncbin ( incfilename , monIconNames . value ( species ) ) ;
2019-06-13 03:20:28 +01:00
speciesToIconPath . insert ( species , root + " / " + path . replace ( " 4bpp " , " png " ) ) ;
}
2020-02-12 15:13:58 +00:00
return true ;
2019-06-13 03:20:28 +01:00
}
2019-02-01 17:43:25 +00:00
void Project : : saveMapHealEvents ( Map * map ) {
2018-09-27 00:33:08 +01:00
// save heal event changes
if ( map - > events [ " heal_event_group " ] . length ( ) > 0 ) {
for ( Event * healEvent : map - > events [ " heal_event_group " ] ) {
HealLocation hl = HealLocation : : fromEvent ( healEvent ) ;
2020-04-29 17:34:49 +01:00
healLocations [ hl . index - 1 ] = hl ;
2018-09-27 00:33:08 +01:00
}
}
saveHealLocationStruct ( map ) ;
}
void Project : : setNewMapEvents ( Map * map ) {
map - > events [ " object_event_group " ] . clear ( ) ;
map - > events [ " warp_event_group " ] . clear ( ) ;
map - > events [ " heal_event_group " ] . clear ( ) ;
map - > events [ " coord_event_group " ] . clear ( ) ;
map - > events [ " bg_event_group " ] . clear ( ) ;
}
int Project : : getNumTilesPrimary ( )
{
return Project : : num_tiles_primary ;
}
int Project : : getNumTilesTotal ( )
{
return Project : : num_tiles_total ;
}
int Project : : getNumMetatilesPrimary ( )
{
return Project : : num_metatiles_primary ;
}
int Project : : getNumMetatilesTotal ( )
{
return Project : : num_metatiles_total ;
}
int Project : : getNumPalettesPrimary ( )
{
return Project : : num_pals_primary ;
}
int Project : : getNumPalettesTotal ( )
{
return Project : : num_pals_total ;
}
2020-05-16 21:57:03 +01:00
int Project : : getMaxMapDataSize ( )
{
return Project : : max_map_data_size ;
}
int Project : : getMapDataSize ( int width , int height )
{
// + 15 and + 14 come from fieldmap.c in pokeruby/pokeemerald/pokefirered.
return ( width + 15 ) * ( height + 14 ) ;
}
int Project : : getDefaultMapSize ( )
{
return Project : : default_map_size ;
}
int Project : : getMaxMapWidth ( )
{
return ( getMaxMapDataSize ( ) / ( 1 + 14 ) ) - 15 ;
}
int Project : : getMaxMapHeight ( )
{
return ( getMaxMapDataSize ( ) / ( 1 + 15 ) ) - 14 ;
}
2020-05-16 23:52:46 +01:00
bool Project : : mapDimensionsValid ( int width , int height ) {
return getMapDataSize ( width , height ) < = getMaxMapDataSize ( ) ;
}
2020-05-16 21:57:03 +01:00
// Get largest possible square dimensions for a map up to maximum of 20x20 (arbitrary)
bool Project : : calculateDefaultMapSize ( ) {
int max = getMaxMapDataSize ( ) ;
if ( max > = getMapDataSize ( 20 , 20 ) ) {
default_map_size = 20 ;
} else if ( max > = getMapDataSize ( 1 , 1 ) ) {
// Below equation derived from max >= (x + 15) * (x + 14)
2020-05-17 00:06:52 +01:00
// x^2 + 29x + (210 - max), then complete the square and simplify
2020-05-16 21:57:03 +01:00
default_map_size = qFloor ( ( qSqrt ( 4 * getMaxMapDataSize ( ) + 1 ) - 29 ) / 2 ) ;
} else {
logError ( QString ( " 'MAX_MAP_DATA_SIZE' of %1 is too small to support a 1x1 map. Must be at least %2. " )
. arg ( max )
. arg ( getMapDataSize ( 1 , 1 ) ) ) ;
return false ;
}
return true ;
}
2020-07-10 21:34:42 +01:00
int Project : : getMaxObjectEvents ( )
{
return Project : : max_object_events ;
}