Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Furniture and terrain can have custom field sprites #55416

Merged
merged 3 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions doc/TILESET.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
##### Tileset
A package of images for the game.

##### Sprite
##### Sprite
A single image that represents either an individual game entity or a common background

##### Root name
Expand Down Expand Up @@ -177,10 +177,10 @@ An optional file called layering.json can be provided. this file defines layerin

```c++
{
"item_variants": [
"variants": [
{
"context": "f_desk",
"variants": [
"item_variants": [
{
"item": "laptop",
"sprite": [{"id": "desk_laptop", "weight": 1}],
Expand All @@ -191,6 +191,12 @@ An optional file called layering.json can be provided. this file defines layerin
"sprite": [{"id": "desk_pen_1", "weight": 2}, {"id": "desk_pen_2", "weight": 2}],
"layer": 100
}
],
"field_variants": [
{
"field": "fd_fire",
"sprite": [{"id": "desk_fd_fire", "weight": 1}]
}
]
}
]
Expand All @@ -201,13 +207,25 @@ This entry sets it so that the f_desk furniture if it contains either a pen or a

`"context": "f_desk"` the furniture or terrain that this should apply to.

`"variants":` the definitions for what will have a variant sprite.
##### Items

`"item_variants":` the definitions for what items will have a variant sprite.

`"item": "laptop"` the item id. (only supported in item_variants)

`"layer": 100` this defines the order the sprites will draw in. 1 drawing first 100 drawing last (so 100 ends up on top). This only works for items, Fields are instead drawn in the order they are stacked on the tile.

`"sprite": [{"id": "desk_pen_1", "weight": 2}, {"id": "desk_pen_2", "weight": 2}]` an array of the possible sprites that can display. For items multiple sprites can be provided with specific weights and will be selected at random.

##### Fields

`"field_variants":` the definitions for what fields will have a variant sprite.

`"field": "fd_fire"` the field id. (only supported in field_variants)

`"item": "laptop"` the item id.
`"sprite": [{"id": "desk_fd_fire", "weight": 1}]` A field can have at most one sprite.

`"sprite": [{"id": "desk_pen_1", "weight": 2}, {"id": "desk_pen_2", "weight": 2}]` an array of the possible sprites that can display. For variation multiple sprites can be provided with specific weights.

`"layer": 100` this defines the order the sprites will draw in. 1 drawing first 100 drawing last (so 100 ends up on top)

## `compose.py`

Expand Down
16 changes: 8 additions & 8 deletions gfx/UltimateCataclysm/layering.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"item_variants": [
"variants": [
{
"context": "f_desk",
"variants": [
"item_variants": [
{
"item": "laptop",
"sprite": [{"id": "desk_laptop", "weight": 1}],
Expand All @@ -22,7 +22,7 @@
},
{
"context": "f_oven",
"variants": [
"item_variants": [
{
"item": "pan",
"sprite": [{"id": "oven_pan_1", "weight": 1}, {"id": "oven_pan_2", "weight": 1}],
Expand All @@ -32,7 +32,7 @@
},
{
"context": "f_brazier",
"variants": [
"item_variants": [
{
"item": "2x4",
"sprite": [{"id": "brazier_2x4", "weight": 1}],
Expand All @@ -47,7 +47,7 @@
},
{
"context": "f_toilet",
"variants": [
"item_variants": [
{
"item": "water",
"sprite": [{"id": "toilet_water", "weight": 1}],
Expand All @@ -62,7 +62,7 @@
},
{
"context": "f_fireplace",
"variants": [
"item_variants": [
{
"item": "log",
"sprite": [{"id": "fireplace_log", "weight": 1}],
Expand All @@ -72,7 +72,7 @@
},
{
"context": "f_cupboard",
"variants": [
"item_variants": [
{
"item": "battery_charger",
"sprite": [{"id": "cupboard_battery_charger", "weight": 1}],
Expand All @@ -97,7 +97,7 @@
},
{
"context": "f_sink",
"variants": [
"item_variants": [
{
"item": "box_small",
"sprite": [{"id": "sink_box_small", "weight": 1}],
Expand Down
2 changes: 1 addition & 1 deletion gfx/layering.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"item_variants": [
"variants": [

]
}
174 changes: 135 additions & 39 deletions src/cata_tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ void tileset::clear()
for( int i = 0; i < season_type::NUM_SEASONS; ++i ) {
tile_ids_by_season[i].clear();
}
layer_data.clear();
item_layer_data.clear();
field_layer_data.clear();
}

const tile_type *tileset::find_tile_type( const std::string &id ) const
Expand Down Expand Up @@ -749,9 +750,9 @@ void tileset_cache::loader::load( const std::string &tileset_id, const bool prec
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" );
// "variants" section must exist.
if( !layer_config.has_member( "variants" ) ) {
layer_config.throw_error( "\"variants\" missing" );
}

load_layers( layer_config );
Expand Down Expand Up @@ -825,38 +826,64 @@ void tileset_cache::loader::load_internal( const JsonObject &config,

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" ) ) {
for( const JsonObject item : config.get_array( "variants" ) ) {
if( item.has_member( "context" ) && ( item.has_array( "item_variants" ) ||
item.has_array( "field_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;
std::vector<layer_variant> item_variants;
std::vector<layer_variant> field_variants;
if( item.has_array( "item_variants" ) ) {
for( const JsonObject vars : item.get_array( "item_variants" ) ) {
if( vars.has_member( "item" ) && vars.has_array( "sprite" ) && vars.has_member( "layer" ) ) {
layer_variant v;
v.id = 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;
item_variants.push_back( v );
} else {
config.throw_error( "item_variants configured incorrectly" );
}
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( item_variants.begin(), item_variants.end(), []( const layer_variant & a,
const layer_variant & b ) {
return a.layer < b.layer;
} );
ts.item_layer_data.emplace( context, item_variants );
}
if( item.has_array( "field_variants" ) ) {
for( const JsonObject vars : item.get_array( "field_variants" ) ) {
if( vars.has_member( "field" ) && vars.has_array( "sprite" ) ) {
layer_variant v;
v.id = vars.get_string( "field" );

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;
field_variants.push_back( v );
} else {
config.throw_error( "field_variants configured incorrectly" );
}
}
ts.field_layer_data.emplace( context, field_variants );
}
// 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" );
}
Expand Down Expand Up @@ -2995,6 +3022,8 @@ bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int
bool ret_draw_items = false;
// go through each field and draw it
if( !fld_overridden ) {
const maptile &tile = here.maptile_at( p );

for( std::map<field_type_id, field_entry>::iterator fd_it = here.field_at( p ).begin();
fd_it != here.field_at( p ).end(); ++fd_it ) {
const field_type_id &fld = fd_it->first;
Expand Down Expand Up @@ -3031,8 +3060,75 @@ bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int
//get field intensity
int intensity = fd_it->second.get_field_intensity();
int nullint = 0;
ret_draw_field = draw_from_id_string( fld.id().str(), TILE_CATEGORY::FIELD, empty_string,
p, subtile, rotation, lit, nv, nullint, intensity );

bool has_drawn = false;

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

// the furniture has layer info
// go through all the layer variants
for( const layer_variant &layer_var : itt->second ) {
if( fld.id().str() == layer_var.id ) {

// get the sprite to draw
// roll should be based on the maptile seed to keep visuals consistent
int roll = 1;
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
ret_draw_field = draw_from_id_string( sprite_to_draw, TILE_CATEGORY::FIELD, empty_string, p,
subtile, rotation, lit, nv, nullint, intensity, "" );
has_drawn = true;
break;
}
}
} else {
// check if the terrain has data
auto itt = tileset_ptr->item_layer_data.find( tile.get_ter_t().id.str() );
if( itt != tileset_ptr->item_layer_data.end() ) {
// the furniture has layer info
// go through all the layer variants
for( const layer_variant &layer_var : itt->second ) {
if( fld.id().str() == layer_var.id ) {

// get the sprite to draw
// roll should be based on the maptile seed to keep visuals consistent
int roll = 1;
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
ret_draw_field = draw_from_id_string( sprite_to_draw, TILE_CATEGORY::FIELD, empty_string, p,
subtile, rotation, lit, nv, nullint, intensity, "" );
has_drawn = true;
break;
}
}
}
}

// draw the default sprite
if( !has_drawn ) {
ret_draw_field = draw_from_id_string( fld.id().str(), TILE_CATEGORY::FIELD, empty_string,
p, subtile, rotation, lit, nv, nullint, intensity );
}

}
}
} else {
Expand Down Expand Up @@ -3082,14 +3178,14 @@ bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int
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() ) {
auto itt = tileset_ptr->item_layer_data.find( tile.get_furn_t().id.str() );
if( itt != tileset_ptr->item_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( i.typeId().str() == layer_var.id ) {
// 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;
Expand Down Expand Up @@ -3125,14 +3221,14 @@ bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int

} 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() ) {
auto itt = tileset_ptr->item_layer_data.find( tile.get_ter_t().id.str() );
if( itt != tileset_ptr->item_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( i.typeId().str() == layer_var.id ) {
// 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;
Expand Down
5 changes: 3 additions & 2 deletions src/cata_tiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class texture
class layer_variant
{
public:
std::string item;
std::string id;
std::map<std::string, int> sprite;
int layer;
int total_weight;
Expand Down Expand Up @@ -155,7 +155,8 @@ class tileset

public:

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

void clear();

Expand Down