Skip to content

Commit

Permalink
Layering items and item variants on specific furniture and terrain (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bombasticSlacks authored Nov 12, 2021
1 parent 05fc3af commit 610e7d5
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 30 deletions.
3 changes: 3 additions & 0 deletions gfx/layering.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"item_variants": [

]
245 changes: 215 additions & 30 deletions src/cata_tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,11 @@ void cata_tiles::reinit()
}

static void get_tile_information( const std::string &config_path, std::string &json_path,
std::string &tileset_path )
std::string &tileset_path, std::string &layering_path )
{
const std::string default_json = PATH_INFO::defaulttilejson();
const std::string default_tileset = PATH_INFO::defaulttilepng();
const std::string default_layering = PATH_INFO::defaultlayeringjson();

// Get JSON and TILESET vars from config
const auto reader = [&]( std::istream & fin ) {
Expand All @@ -341,6 +342,10 @@ static void get_tile_information( const std::string &config_path, std::string &j
} else if( string_starts_with( sOption, "TILESET" ) ) {
fin >> tileset_path;
dbg( D_INFO ) << "TILESET path set to [" << tileset_path << "].";
} else if( string_starts_with( sOption, "LAYERING" ) ) {
fin >> layering_path;
dbg( D_INFO ) << "LAYERING path set to [" << layering_path << "].";

} else {
getline( fin, sOption );
}
Expand All @@ -350,6 +355,7 @@ static void get_tile_information( const std::string &config_path, std::string &j
if( !read_from_file( config_path, reader ) ) {
json_path = default_json;
tileset_path = default_tileset;
layering_path = default_layering;
}

if( json_path.empty() ) {
Expand All @@ -360,6 +366,10 @@ static void get_tile_information( const std::string &config_path, std::string &j
tileset_path = default_tileset;
dbg( D_INFO ) << "TILESET set to default [" << tileset_path << "].";
}
if( layering_path.empty() ) {
layering_path = default_layering;
dbg( D_INFO ) << "TILESET set to default [" << layering_path << "].";
}
}

template<typename PixelConverter>
Expand Down Expand Up @@ -576,25 +586,39 @@ void tileset_cache::loader::load( const std::string &tileset_id, const bool prec
const bool pump_events )
{
std::string json_conf;
std::string layering;
std::string tileset_path;
std::string tileset_root;

bool has_layering = true;

const auto tset_iter = TILESETS.find( tileset_id );
if( tset_iter != TILESETS.end() ) {
tileset_root = tset_iter->second;
dbg( D_INFO ) << '"' << tileset_id << '"' << " tileset: found config file path: " <<
tileset_root;
get_tile_information( tileset_root + '/' + PATH_INFO::tileset_conf(),
json_conf, tileset_path );
json_conf, tileset_path, layering );
dbg( D_INFO ) << "Current tileset is: " << tileset_id;
} else {
dbg( D_ERROR ) << "Tileset \"" << tileset_id << "\" from options is invalid";
json_conf = PATH_INFO::defaulttilejson();
tileset_path = PATH_INFO::defaulttilepng();
layering = PATH_INFO::defaultlayeringjson();
}

std::string json_path = tileset_root + '/' + json_conf;
std::string img_path = tileset_root + '/' + tileset_path;
std::string layering_path = tileset_root + '/' + layering;

dbg( D_INFO ) << "Attempting to Load LAYERING file " << layering_path;
cata::ifstream layering_file( fs::u8path( layering_path ),
std::ifstream::in | std::ifstream::binary );

if( !layering_file.good() ) {
has_layering = false;
//throw std::runtime_error(std::string("Failed to open layering info json: ") + layering_path);
}

dbg( D_INFO ) << "Attempting to Load JSON file " << json_path;
cata::ifstream config_file( fs::u8path( json_path ),
Expand Down Expand Up @@ -701,6 +725,22 @@ void tileset_cache::loader::load( const std::string &tileset_id, const bool prec
ensure_default_item_highlight();

ts.tileset_id = tileset_id;

// set up layering data
if( has_layering ) {
JsonIn layering_json( layering_file );
JsonObject layer_config = layering_json.get_object();
layer_config.allow_omitted_members();

// "item_variants" section must exist.
if( !layer_config.has_member( "item_variants" ) ) {
layer_config.throw_error( "\"item_variants\" missing" );
}

load_layers( layer_config );
}


}

void tileset_cache::loader::load_internal( const JsonObject &config,
Expand Down Expand Up @@ -766,6 +806,47 @@ void tileset_cache::loader::load_internal( const JsonObject &config,
// also eliminate negative sprite references
}

void tileset_cache::loader::load_layers( const JsonObject &config )
{
for( const JsonObject item : config.get_array( "item_variants" ) ) {
if( item.has_member( "context" ) && item.has_array( "variants" ) ) {
std::string context;
context = item.get_string( "context" );
std::vector<layer_variant> variants;
for( const JsonObject vars : item.get_array( "variants" ) ) {
if( vars.has_member( "item" ) && vars.has_array( "sprite" ) && vars.has_member( "layer" ) ) {
layer_variant v;
v.item = vars.get_string( "item" );

v.layer = vars.get_int( "layer" );

int total_weight = 0;
for( const JsonObject sprites : vars.get_array( "sprite" ) ) {
std::string id = sprites.get_string( "id" );
int weight = sprites.get_int( "weight", 1 );
v.sprite.emplace( id, weight );

total_weight += weight;
}
v.total_weight = total_weight;
variants.push_back( v );
} else {
config.throw_error( "variants configured incorrectly" );
}
}
// sort them based on layering so we can draw them correctly
std::sort( variants.begin(), variants.end(), []( const layer_variant & a,
const layer_variant & b ) {
return a.layer < b.layer;
} );
ts.layer_data.emplace( context, variants );
} else {
config.throw_error( "layering configured incorrectly" );
}
}

}

void tileset_cache::loader::process_variations_after_loading( weighted_int_list<std::vector<int>>
&vs )
{
Expand Down Expand Up @@ -2976,38 +3057,142 @@ bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int
mtype_id mon_id;
std::string variant;
bool hilite = false;
bool drawtop = true;
const itype *it_type;
if( it_overridden ) {
it_id = std::get<0>( it_override->second );
mon_id = std::get<1>( it_override->second );
hilite = std::get<2>( it_override->second );
it_type = item::find_type( it_id );
} else if( !invisible[0] && here.sees_some_items( p, get_player_character() ) ) {
const maptile &tile = here.maptile_at( p );
const item &itm = tile.get_uppermost_item();
if( itm.has_itype_variant() ) {
variant = itm.itype_variant().id;
const maptile &tile = here.maptile_at( p );

if( !invisible[0] ) {
// start by drawing the layering data if available
// start for checking if layer data is available for the furniture
auto itt = tileset_ptr->layer_data.find( tile.get_furn_t().id.str() );
if( itt != tileset_ptr->layer_data.end() ) {

// the furniture has layer info
// go through all the layer variants
for( const layer_variant &layer_var : itt->second ) {
for( const item &i : tile.get_items() ) {
if( i.typeId().str() == layer_var.item ) {
// if an item matches draw it and break
const std::string layer_it_category = i.typeId()->get_item_type_string();
const lit_level layer_lit = ll;
const bool layer_nv = nv_goggles_activated;

// get the sprite to draw
// roll should be based on the maptile seed to keep visuals consistent
int roll = i.seed % layer_var.total_weight;
std::string sprite_to_draw;
for( const auto &sprite_list : layer_var.sprite ) {
roll = roll - sprite_list.second;
if( roll < 0 ) {
sprite_to_draw = sprite_list.first;
break;
}
}

// if we have found info on the item go through and draw its stuff
draw_from_id_string( sprite_to_draw, TILE_CATEGORY::ITEM, layer_it_category, p, 0,
0, layer_lit, layer_nv, height_3d, 0, "" );


// if the top item is already being layered don't draw it later
if( i.typeId() == tile.get_uppermost_item().typeId() ) {
drawtop = false;
}

break;
}
}

}

} else {
// check if the terrain has data
auto itt = tileset_ptr->layer_data.find( tile.get_ter_t().id.str() );
if( itt != tileset_ptr->layer_data.end() ) {

// the furniture has layer info
// go through all the layer variants
for( const layer_variant &layer_var : itt->second ) {
for( const item &i : tile.get_items() ) {
if( i.typeId().str() == layer_var.item ) {
// if an item matches draw it and break
const std::string layer_it_category = i.typeId()->get_item_type_string();
const lit_level layer_lit = ll;
const bool layer_nv = nv_goggles_activated;

// get the sprite to draw
// roll should be based on the maptile seed to keep visuals consistent
int roll = i.seed % layer_var.total_weight;
std::string sprite_to_draw;
for( const auto &sprite_list : layer_var.sprite ) {
roll = roll - sprite_list.second;
if( roll < 0 ) {
sprite_to_draw = sprite_list.first;
break;
}
}

// if we have found info on the item go through and draw its stuff
draw_from_id_string( sprite_to_draw, TILE_CATEGORY::ITEM, layer_it_category, p, 0,
0, layer_lit, layer_nv, height_3d, 0, "" );


// if the top item is already being layered don't draw it later
if( i.typeId() == tile.get_uppermost_item().typeId() ) {
drawtop = false;
}

break;
}
}

}

}
}
const mtype *const mon = itm.get_mtype();
it_id = itm.typeId();
mon_id = mon ? mon->id : mtype_id::NULL_ID();
hilite = tile.get_item_count() > 1;
it_type = itm.type;
} else {
it_type = nullptr;
}
if( it_type && !it_id.is_null() ) {
const std::string disp_id = it_id == itype_corpse && mon_id ?
"corpse_" + mon_id.str() : it_id.str();
const std::string it_category = it_type->get_item_type_string();
const lit_level lit = it_overridden ? lit_level::LIT : ll;
const bool nv = it_overridden ? false : nv_goggles_activated;

ret_draw_items = draw_from_id_string( disp_id, TILE_CATEGORY::ITEM, it_category, p, 0,
0, lit, nv, height_3d, 0, variant );
if( ret_draw_items && hilite ) {
draw_item_highlight( p );


if( drawtop || it_overridden ) {
if( it_overridden ) {
it_id = std::get<0>( it_override->second );
mon_id = std::get<1>( it_override->second );
hilite = std::get<2>( it_override->second );
it_type = item::find_type( it_id );
} else if( !invisible[0] && here.sees_some_items( p, get_player_character() ) ) {
const item &itm = tile.get_uppermost_item();
if( itm.has_itype_variant() ) {
variant = itm.itype_variant().id;
}
const mtype *const mon = itm.get_mtype();
it_id = itm.typeId();
mon_id = mon ? mon->id : mtype_id::NULL_ID();
hilite = tile.get_item_count() > 1;
it_type = itm.type;
} else {
it_type = nullptr;
}
if( it_type && !it_id.is_null() ) {

const std::string disp_id = it_id == itype_corpse && mon_id ?
"corpse_" + mon_id.str() : it_id.str();
const std::string it_category = it_type->get_item_type_string();
const lit_level lit = it_overridden ? lit_level::LIT : ll;
const bool nv = it_overridden ? false : nv_goggles_activated;




ret_draw_items = draw_from_id_string( disp_id, TILE_CATEGORY::ITEM, it_category, p, 0,
0, lit, nv, height_3d, 0, variant );
if( ret_draw_items && hilite ) {
draw_item_highlight( p );
}
}
}
// we may still need to draw the highlight
else if( tile.get_item_count() > 1 && here.sees_some_items( p, get_player_character() ) ) {
draw_item_highlight( p );
}
}
return ret_draw_field && ret_draw_items;
Expand Down
20 changes: 20 additions & 0 deletions src/cata_tiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ class texture
}
};

class layer_variant
{
public:
std::string item;
std::map<std::string, int> sprite;
int layer;
int total_weight;
};

class tileset
{
private:
Expand Down Expand Up @@ -136,6 +145,7 @@ class tileset
// either variant can be either a `nullptr` or a pointer/reference to the real value (stored inside `tile_ids`)
std::unordered_map<std::string, season_tile_value> tile_ids_by_season[season_type::NUM_SEASONS];


static const texture *get_if_available( const size_t index,
const decltype( shadow_tile_values ) &tiles ) {
return index < tiles.size() ? & tiles[index] : nullptr;
Expand All @@ -144,6 +154,9 @@ class tileset
friend class tileset_cache;

public:

std::unordered_map<std::string, std::vector<layer_variant>> layer_data;

int get_tile_width() const {
return tile_width;
}
Expand Down Expand Up @@ -179,6 +192,7 @@ class tileset

tile_type &create_tile_type( const std::string &id, tile_type &&new_tile_type );
const tile_type *find_tile_type( const std::string &id ) const;

/**
* Looks up tile by id + season suffix AND just raw id
* Example: if id == "t_tree_apple" and season == SPRING
Expand Down Expand Up @@ -284,6 +298,12 @@ class tileset_cache::loader
void load_internal( const JsonObject &config, const std::string &tileset_root,
const std::string &img_path, bool pump_events );

/**
* Helper function to load layering data.
* @throw std::exception On any error.
*/
void load_layers( const JsonObject &config );

public:
loader( tileset &ts, const SDL_Renderer_Ptr &r ) : ts( ts ), renderer( r ) {
}
Expand Down
Loading

0 comments on commit 610e7d5

Please sign in to comment.