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

Chapters are Character-mapped for unique books #79388

Merged
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
8 changes: 8 additions & 0 deletions data/json/items/book/abstract.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@
"copy-from": "book_fict_tpl",
"melee_damage": { "bash": 1 }
},
{
"abstract": "book_fict_soft_collection_tpl",
"type": "BOOK",
"copy-from": "book_fict_soft_tpl",
"name": { "str": "paperback novel", "str_pl": "paperbacks" },
"description": "Paperback fiction novel generic collection",
"generic": true
},
{
"abstract": "book_nonf_hard_tpl",
"type": "BOOK",
Expand Down
40 changes: 25 additions & 15 deletions data/json/items/book/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"intelligence": 10,
"time": "26 m",
"chapters": 40,
"generic": true,
"fun": 3
},
{
Expand Down Expand Up @@ -170,6 +171,7 @@
"color": "pink",
"time": "10 m",
"chapters": 4,
"generic": true,
"fun": 1
},
{
Expand All @@ -194,7 +196,7 @@
"type": "BOOK",
"name": { "str": "adventure novel" },
"description": "The stirring tale of a race against time, in search of a lost city located in the heart of the African continent.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"price": "8 USD 50 cent",
"price_postapoc": "50 cent",
"time": "20 m",
Expand All @@ -205,7 +207,7 @@
"type": "BOOK",
"name": { "str": "buddy novel" },
"description": "A gripping tale of two friends struggling to survive on the streets of New York City.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "244 g",
"volume": "500 ml",
"price": "6 USD 50 cent",
Expand All @@ -215,7 +217,7 @@
"id": "novel_crime",
"type": "BOOK",
"name": { "str": "crime novel" },
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"description": "After their diamond heist goes wrong, the surviving criminals begin to suspect that one of them is a police informant.",
"intelligence": 6,
"time": "20 m",
Expand Down Expand Up @@ -252,7 +254,7 @@
"type": "BOOK",
"name": { "str": "drama novel" },
"description": "A real book for real adults.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"intelligence": 7,
"time": "25 m",
"chapters": 28,
Expand All @@ -263,7 +265,7 @@
"type": "BOOK",
"name": { "str": "erotic novel" },
"description": "A hackneyed fictional narrative concealing low-grade literary smut.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "200 g",
"volume": "500 ml",
"time": "18 m",
Expand All @@ -274,7 +276,7 @@
"type": "BOOK",
"name": { "str": "experimental novel" },
"description": "A bizarre play about the philosophy of existential absurdity. Or maybe it's about two guys waiting for their friend to show up. It's confusing.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "142 g",
"volume": "500 ml",
"intelligence": 7,
Expand All @@ -286,7 +288,7 @@
"type": "BOOK",
"name": { "str": "fantasy novel" },
"description": "Basic sword & sorcery.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "227 g",
"volume": "1 L",
"intelligence": 7,
Expand All @@ -300,7 +302,7 @@
"type": "BOOK",
"name": { "str": "horror novel" },
"description": "Maybe not the best reading material considering the situation.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "227 g",
"intelligence": 7,
"time": "18 m",
Expand All @@ -312,7 +314,7 @@
"type": "BOOK",
"name": { "str": "mystery novel" },
"description": "A detective investigates an unusual murder in a secluded location.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"intelligence": 7,
"time": "18 m",
"chapters": 28,
Expand All @@ -323,7 +325,7 @@
"type": "BOOK",
"name": { "str": "road novel" },
"description": "A tale about a group of friends who wander the USA in the 1960s against a backdrop of jazz, poetry and drug use.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "244 g",
"volume": "500 ml",
"time": "20 m",
Expand All @@ -345,7 +347,7 @@
"type": "BOOK",
"name": { "str": "romance novel" },
"description": "Drama and mild smut.",
"copy-from": "book_fict_soft_tpl"
"copy-from": "book_fict_soft_collection_tpl"
},
{
"id": "paperback_romance_circuses",
Expand Down Expand Up @@ -497,7 +499,7 @@
"type": "BOOK",
"name": { "str": "spy novel" },
"description": "A tale of intrigue and espionage among Nazis, no, Commies, no, Iraqis!",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"intelligence": 5,
"time": "18 m",
"chapters": 20,
Expand All @@ -524,7 +526,7 @@
"description": "An exciting seventeenth century tale of how an enslaved Irish doctor and his comrades-in-chains escape and become heroic pirates of the Robin Hood variety.",
"weight": "582 g",
"volume": "750 ml",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"intelligence": 7,
"time": "20 m",
"chapters": 28,
Expand Down Expand Up @@ -582,7 +584,7 @@
"type": "BOOK",
"name": { "str": "war novel" },
"description": "A thrilling narrative of survival in a prisoner of war camp during the Second World War, filled with riveting subplots about rat farming and dysentery.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"weight": "686 g",
"price_postapoc": "50 cent",
"intelligence": 7,
Expand All @@ -604,7 +606,7 @@
"type": "BOOK",
"name": { "str": "western novel" },
"description": "The classic tale of a gunfighting stranger who comes to a small settlement and is hired to help the townsfolk defend themselves from a band of marauding outlaws.",
"copy-from": "book_fict_soft_tpl",
"copy-from": "book_fict_soft_collection_tpl",
"intelligence": 5,
"time": "20 m",
"chapters": 28,
Expand Down Expand Up @@ -680,6 +682,7 @@
"intelligence": 4,
"time": "1 m",
"chapters": 200,
"generic": true,
"fun": -5,
"melee_damage": { "bash": 2 }
},
Expand All @@ -698,6 +701,7 @@
"color": "light_gray",
"time": "10 m",
"chapters": 4,
"generic": true,
"fun": 1,
"flags": [ "INSPIRATIONAL" ]
},
Expand All @@ -717,6 +721,7 @@
"intelligence": 9,
"time": "18 m",
"chapters": 36,
"generic": true,
"fun": 2
},
{
Expand All @@ -735,6 +740,7 @@
"intelligence": 9,
"time": "18 m",
"chapters": 36,
"generic": true,
"fun": 2
},
{
Expand Down Expand Up @@ -825,6 +831,7 @@
"intelligence": 7,
"time": "48 m",
"chapters": 28,
"generic": true,
"fun": 5
},
{
Expand All @@ -843,6 +850,7 @@
"intelligence": 6,
"time": "18 m",
"chapters": 24,
"generic": true,
"fun": 3
},
{
Expand Down Expand Up @@ -978,6 +986,7 @@
"intelligence": 7,
"time": "28 m",
"chapters": 40,
"generic": true,
"fun": 3
},
{
Expand Down Expand Up @@ -1073,6 +1082,7 @@
"intelligence": 7,
"time": "28 m",
"chapters": 40,
"generic": true,
"fun": 4
},
{
Expand Down
1 change: 1 addition & 0 deletions doc/JSON/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3905,6 +3905,7 @@ Books can be defined like this:
"fun" : -2, // Morale bonus/penalty for reading
"skill" : "computer", // Skill raised
"chapters" : 4, // Number of chapters (for fun only books), each reading "consumes" a chapter. Books with no chapters left are less fun (because the content is already known to the character).
"generic": false, // This book counts chapters by item instance instead of by type (this book represents a generic variety of books, like "book of essays")
"required_level" : 2, // Minimum skill level required to learn
"martial_art": "style_mma", // Martial art learned from this book; incompatible with `skill`
"proficiencies": [ // Having this book mitigate lack of proficiency, required for crafting
Expand Down
2 changes: 2 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,8 @@ class Character : public Creature, public visitable
/** Calculates the total fun bonus relative to this character's traits and chapter progress */
bool fun_to_read( const item &book ) const;
int book_fun_for( const item &book, const Character &p ) const;
/** The number of chapters remaining for each book itype */
std::map<itype_id, int> book_chapters;
ShnitzelX2 marked this conversation as resolved.
Show resolved Hide resolved

bool can_pickVolume( const item &it, bool safe = false, const item *avoid = nullptr,
bool ignore_pkt_settings = true ) const;
Expand Down
36 changes: 24 additions & 12 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8851,7 +8851,7 @@ void item::set_browsed( bool browsed )

bool item::is_ecopiable() const
{
return has_flag( flag_E_COPIABLE ) || ( is_book() && get_chapters() == 0 );
return has_flag( flag_E_COPIABLE ) || ( is_book() && !type->book->generic );
}

bool item::efiles_all_browsed() const
Expand Down Expand Up @@ -10770,22 +10770,34 @@ int item::get_chapters() const

int item::get_remaining_chapters( const Character &u ) const
{
// NOLINTNEXTLINE(cata-translate-string-literal)
const std::string var = string_format( "remaining-chapters-%d", u.getID().get_value() );
return get_var( var, get_chapters() );
const itype_id &type = typeId();
if( is_book() && type->book->generic ) {
// NOLINTNEXTLINE(cata-translate-string-literal)
const std::string var = string_format( "remaining-chapters-%d", u.getID().get_value() );
return get_var( var, get_chapters() );
}
const std::map<itype_id, int> &book_chapters = u.book_chapters;
auto find_chapters = book_chapters.find( type );
if( find_chapters == book_chapters.end() ) {
return get_chapters();
}
return find_chapters->second;
}

void item::mark_chapter_as_read( const Character &u )
void item::mark_chapter_as_read( Character &u )
{
// NOLINTNEXTLINE(cata-translate-string-literal)
const std::string var = string_format( "remaining-chapters-%d", u.getID().get_value() );
if( type->book && type->book->chapters == 0 ) {
// books without chapters will always have remaining chapters == 0, so we don't need to store them
erase_var( var );
// books without chapters will always have remaining chapters == 0, so we don't need to store them
if( is_book() && get_chapters() == 0 ) {
return;
}
if( is_book() && type->book->generic ) {
// NOLINTNEXTLINE(cata-translate-string-literal)
const std::string var = string_format( "remaining-chapters-%d", u.getID().get_value() );
const int remain = std::max( 0, get_remaining_chapters( u ) - 1 );
set_var( var, remain );
return;
}
const int remain = std::max( 0, get_remaining_chapters( u ) - 1 );
set_var( var, remain );
u.book_chapters[typeId()] = std::max( 0, get_remaining_chapters( u ) - 1 );
}

std::set<recipe_id> item::get_saved_recipes() const
Expand Down
2 changes: 1 addition & 1 deletion src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2407,7 +2407,7 @@ class item : public visitable
* Mark one chapter of the book as read by the given player. May do nothing if the book has
* no unread chapters. This is a per-character setting, see @ref get_remaining_chapters.
*/
void mark_chapter_as_read( const Character &u );
void mark_chapter_as_read( Character &u );
/**
* Returns recipes stored on the item (laptops, smartphones, sd cards etc)
* Filters out !is_valid() recipes
Expand Down
1 change: 1 addition & 0 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3264,6 +3264,7 @@ void islot_book::load( const JsonObject &jo )
optional( jo, was_loaded, "skill", skill, skill_id::NULL_ID() );
optional( jo, was_loaded, "martial_art", martial_art, matype_id::NULL_ID() );
optional( jo, was_loaded, "chapters", chapters, 0 );
optional( jo, was_loaded, "generic", generic, false );
optional( jo, was_loaded, "proficiencies", proficiencies );
optional( jo, was_loaded, "scannable", is_scannable, true );
}
Expand Down
5 changes: 5 additions & 0 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ struct islot_book {
* "To read" means getting 1 skill point, not all of them.
*/
time_duration time = 0_turns;
/**
* This book counts chapters by item instance instead of by type
* (i.e. this book represents a generic variety of books, like "book of essays")
*/
bool generic = false;
/**
* Fun books have chapters; after all are read, the book is less fun.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,7 @@ void Character::load( const JsonObject &data )
data.read( "male", male );
data.read( "cash", cash );
data.read( "recoil", recoil );
data.read( "book_chapters", book_chapters );
data.read( "in_vehicle", in_vehicle );
data.read( "last_sleep_check", last_sleep_check );
if( data.read( "id", tmpid ) && tmpid.is_valid() ) {
Expand Down Expand Up @@ -1489,6 +1490,7 @@ void Character::store( JsonOut &json ) const

json.member( "cash", cash );
json.member( "recoil", recoil );
json.member( "book_chapters", book_chapters );
json.member( "in_vehicle", in_vehicle );
json.member( "id", getID() );

Expand Down
Loading