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

tilesets: add support for looks_like field #24747

Merged
merged 7 commits into from Aug 10, 2018

Conversation

Projects
None yet
6 participants
@mlangsdorf
Copy link
Contributor

commented Aug 9, 2018

This adds support for a looks_like field in most things that have tileset images.
If the tileset loader fails to load a tile for a terrain, furniture, monster, vehicle part, or item, it will check to
see if the object has a looks_like field. If it does, it will attempt to load that tile. If that fails, it will check
if the looks_like object has a field, repeating the process up to 10 times.

the looks_like defaults to the value of copy-from if the object has a copy-from field, or it can be set
explicitly.

Fixes #19932

mlangsdorf added some commits Aug 8, 2018

default to look-alike tiles: functionalize finding tiles by season
as part of an effort to find a look-alike tile when an object doesn't
have a tile in a tileset, it's going to be necessary to retrieve
the seasonal variation of a tile in a lot of different places in the
code. Move the code to get the seasonal variation into its own function.
tileset: implement looks_like for furniture
add a looks_like field to map_data_common_t, that is filled by the value
of copy-from if present or looks_like if that is present, preferring the
second.

when a tileset loads, if the loader can't find a tile for a piece of
furniture, it will see if it has one for the looks_like furniture. If
it doesn't, it will check if the looks_like furniture has a looks_like
field, and if it does, try to find a tile for that. It will continue the
process of looking for looks_like tiles until it finds one, or the
looks_like field is empty, or it has gone through 10 attempts (to avoid
looks_like loops). If a looks_like tile is found, that will be used;
if not, the fallback to ASCII symbol will proceed normally.
tileset: implement looks_like for terrain
use the existing looks_like field in map_common_t to implement look_alike
for terrain.

when a tileset loads, if the loader can't find a tile for a piece of
terrain, it will see if it has one for the looks_like terrain. If
it doesn't, it will check if the looks_like terrain has a looks_like
field, and if it does, try to find a tile for that. It will continue the
process of looking for looks_like tiles until it finds one, or the
looks_like field is empty, or it has gone through 10 attempts (to avoid
looks_like loops). If a looks_like tile is found, that will be used;
if not, the fallback to ASCII symbol will proceed normally.
tileset: implement looks_like for monster
add a looks_like field to mtype, that is filled by the value of copy-from
if present or looks_like if that is present, preferring the second.

when a tileset loads, if the loader can't find a tile for a monster, it
will see if it has one for the looks_like monster. If it doesn't, it will
check if the looks_like monster has a looks_like field, and if it does,
try to find a tile for that. It will continue the process of looking for
looks_like tiles until it finds one, or the looks_like field is empty, or
it has gone through 10 attempts (to avoid looks_like loops). If a
looks_like tile is found, that will be used; if not, the fallback to
ASCII symbol will proceed normally.
tileset: implement looks_like for vehicle_parts
add a looks_like to vpart_info, that is filled by the value of copy-from if
present or looks_like if that is present, preferring the second.

when a tileset loads, if the loader can't find a tile for a vehicle part,
it will see if it has one for the looks_like terrain. If it doesn't, it
will check if the looks_like vehicle part has a looks_like field, and if
it does, try to find a tile for that. It will continue the process of
looking for looks_like tiles until it finds one, or the looks_like field
is empty, or it has gone through 10 attempts (to avoid looks_like loops).
If a looks_like tile is found, that will be used; if not, the fallback to
ASCII symbol will proceed normally.
@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 9, 2018

oldandbusted
Mike the Mechanic, in a game without looks_like support.

thenewhotness
Mike the Mechanic, in the same savefile with looks_like support.

I'll need to add a separate PR to add some looks_like fields, but hopefully this PR will make it easier for
people to add new content and keep their tileset games looking pretty.

tileset: implement looks_like for items
add a looks_like field to itype, that is filled by the value of copy-from
if present or looks_like if that is present, preferring the second.

when a tileset loads, if the loader can't find a tile for an item, it will
see if it has one for the looks_like item. If it doesn't, it will check if
the looks_like item has a looks_like field, and if it does, try to find a
tile for that. It will continue the process of looking for looks_like
tiles until it finds one, or the looks_like field is empty, or it has gone
through 10 attempts (to avoid looks_like loops). If a looks_like tile is
found, that will be used; if not, the fallback to ASCII symbol will
proceed normally.

@mlangsdorf mlangsdorf force-pushed the mlangsdorf:looks_like_tile branch to cde1efc Aug 9, 2018

@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 9, 2018

Will add doc/ changes tomorrow. Sorry I forgot.

@lispcoc

This comment has been minimized.

Copy link
Contributor

commented Aug 10, 2018

It Seems good, but overlay_XXXX tilles is not taken into account. Support them is hard to implement?

@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 10, 2018

As far as I can tell, overlay tiles are handled in draw_entity_with_overlays(), which pulls the overlay string ids from pl.get_overlay_ids() and then looks for overlay_(fe)male_$OVERLAY or overlay_$OVERLAY via find_tile_type(). If one of those two matches, draw_from_string_id() is called, but it will necessarily return a tile because draw_from_string_id doesn't get called unless there's a tile to return.

overlays aren't terrain, furniture, monsters, vehicleparts, or items. They don't have an json object associated with them that can have a looks_like value. I don't think looks_like overlays would necessarily even be a good idea.

But maybe I'm wrong. I think I can code up a find_mutation_looks_like and find_effects_looks_like fairly straightforwardly, and the existing find_item_looks_like could be altered to take an optional prefix string and perform the search with the prefix string. The biggest problem is the number of the potential searches: overlay_male_ultimate_winter_survivor_coat might have to search for overlay_ultimate_winter_survivor_coat, overlay_male_winter_survivor_coat, overlay_winter_survivor_coat, overlay_male_duster_survivor, overlay_duster_survivor, overlay_male_duster_leather, overlay_duster_leather, overlay_male_duster, overlay_duster, overlay_male_trenchcoat, overlay_trenchcoat, overlay_male_coat_winter, and overlay_coat_winter to satisfy the looks_like series of
ultimate_winter_survivor_coat: winter_survivor_coat,
winter_survivor_coat: duster_survivor,
duster_survivor: duster_leather,
duster_leather: duster,
duster: trenchcoat,
trenchcoat: coat_winter

though I guess that's only 6 loops through and 24 searches (because you have to search for seasonal variations each time), not too bad. I suspect the find() operator is O(1), so doing it 40 times (in the absolute worst case scenario) per tile shouldn't be too bad, and I don't expect most tiles to need the full 10 iterations to either find a matching tile or run out of looks_like.

Might take me a couple of days. Thinking about it, I'd rather open a second PR for overlays after this gets merged, because I'd like this to get extensive reality testing before expanding it, if that's okay by you.

@SomeDeadGuy

This comment has been minimized.

Copy link
Contributor

commented Aug 10, 2018

Addition:
How about this - give looks_like to randomly generated artifacts! So now artifacts can look like something, what they actually are!

tileset: add looks_like to doc/JSON_INFO.md
add a description of looks_like to the relevant descriptions in
doc/JSON_INFO.md. Annoyingly, the full description of how it works is at
the bottom in the furniture/terrain stuff.

@mlangsdorf mlangsdorf referenced this pull request Aug 10, 2018

Open

tileset looks_like: further enhancements #24758

1 of 3 tasks complete
@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 10, 2018

Artifact tagging added to the todo list but not for this PR.

@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 10, 2018

minimal documentation added.

Off to go revise JSON_INFO.

@Night-Pryanik

This comment has been minimized.

Copy link
Member

commented Aug 10, 2018

After this PR is merged, you can also update info on json API changes in #19376.

@ZhilkinSerg ZhilkinSerg self-assigned this Aug 10, 2018

@ZhilkinSerg ZhilkinSerg merged commit 1280b85 into CleverRaven:master Aug 10, 2018

2 of 3 checks passed

continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
gorgon-ghprb Build finished.
Details

@ZhilkinSerg ZhilkinSerg removed their assignment Aug 10, 2018

@mlangsdorf mlangsdorf deleted the mlangsdorf:looks_like_tile branch Aug 10, 2018

const std::string &subcategory, tripoint pos,
int subtile, int rota, lit_level ll,
bool apply_night_vision_goggles, int &height_3d )
const tile_type *cata_tiles::find_furniture_looks_like( std::string id )

This comment has been minimized.

Copy link
@codemime

codemime Aug 12, 2018

Member

Immutable strings should be passed by constant references: const std::string& id.

{
std::string looks_like = id;
int cnt = 0;
while( !looks_like.empty() && cnt < 10 ) {

This comment has been minimized.

Copy link
@codemime

codemime Aug 12, 2018

Member

You could've stored already visited items using a set or something, thus preventing infinite loops. The counter based approach raises questions like "why 10"? Checking for inconsistencies beforehand would be even better.

std::string looks_like = id;
int cnt = 0;
while( !looks_like.empty() && cnt < 10 ) {
const tile_type *lltt = find_tile_with_season( looks_like );

This comment has been minimized.

Copy link
@codemime

codemime Aug 12, 2018

Member

find_tile_with_season() is invoked twice per draw_from_id_string(). The first invocation is here.

This comment has been minimized.

Copy link
@mlangsdorf

mlangsdorf Aug 12, 2018

Author Contributor

I think the issue you're pointing out is addressed in #24778

This comment has been minimized.

Copy link
@codemime

codemime Aug 12, 2018

Member

I meant something else:

    const tile_type *tt = find_tile_with_season( id );	// <- 1st find_tile_with_season(id).
    
    if( !tt ) {
	...
        if( category == C_FURNITURE ) {
            tt = find_furniture_looks_like( id, effective_id );	// <- 2nd find_tile_with_season(id) with the same id inside the function

Map lookup times are noticeable in tiles, so this may slightly impact the overall performance.

return nullptr;
}

const tile_type *cata_tiles::find_terrain_looks_like( std::string id )

This comment has been minimized.

Copy link
@codemime

codemime Aug 12, 2018

Member

These functions look very alike. They introduce unnecessary duplication which can be avoided with something like:

const tile_type *cata_tiles::find_tile_looks_like( const std::string& id, TILE_CATEGORY category )
{
    switch( category )
    {
	...
	
	case C_FURNITURE: {
	    const furn_str_id fid( id );
	    return fid ? fid->looks_like : nullptr;	
	}
	
	...	
    }
    
    return nullptr;
}

This comment has been minimized.

Copy link
@mlangsdorf

mlangsdorf Aug 12, 2018

Author Contributor

Yeah, I realized that after I submitted it. Refactoring it is on the todo list.

@codemime

This comment has been minimized.

Copy link
Member

commented Aug 12, 2018

The very idea of using copy_from for obtaining missing tiles looks sound. I think it's great, actually.
However, I'd avoid introducing the explicit looks_like filed for two reasons:

  1. It's kinda way too tile related and makes no sense in the absence of tiles. Tiles are supposed to be on top of items, not vice versa.
  2. Most of the time it simply restates copy_from.
@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 12, 2018

Completely disagree on the second point. Most of the time it doesn't restate copy-from at all. None of the hd vehicle door/hatches/trunk doorss are copy-froms of the non-hd versions.
People aren't introducing new terrain or furniture via copy-from, for better or worse. But they can say an autodoc couch looks like a sofa, and fills a gap until the tilesets are updated.

As far as the first point, I don't see any other place to put this information than in the item JSON. Any other location is going to be inherently fragile.

@codemime

This comment has been minimized.

Copy link
Member

commented Aug 12, 2018

People aren't introducing new terrain or furniture via copy-from, for better or worse.

Good point. This looks like an issue to me. You've outlined it and offered a workaround which would work for tiles, but only if people are willing to use it.

I don't see any other place to put this information than in the item JSON

JSON is perfectly fine for the purpose. I just meant that if you store the ancestor's id somewhere (the item copy-from references), it would suffice.

@ZhilkinSerg

This comment has been minimized.

Copy link
Contributor

commented Aug 12, 2018

Using ofcopy-from would make sense if all items utilized this field.

@codemime

This comment has been minimized.

Copy link
Member

commented Aug 12, 2018

Using ofcopy-from would make sense if all items utilized this field.

This PR relies solely on copy-from (doesn't introduce any new looks_likes). Still, it's said that it resolves the corresponding issue.

@mlangsdorf

This comment has been minimized.

Copy link
Contributor Author

commented Aug 12, 2018

See #24768

return false;
}

const tile_type *tt = find_tile_with_season( id );

This comment has been minimized.

Copy link
@lispcoc

lispcoc Aug 13, 2018

Contributor

I think id is not updated to seasonal_id. May this cause break seasonal tile? #24819

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.