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

feat(port): Add book binder with many QoL #2170

Closed
wants to merge 17 commits into from
Closed
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
5 changes: 5 additions & 0 deletions data/json/item_actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,11 @@
"id": "COIN_FLIP",
"name": { "str": "Flip" }
},
{
"type": "item_action",
"id": "BINDER_ADD_RECIPE",
"name": { "str": "Copy a recipe" }
},
{
"type": "item_action",
"id": "COMBATSAW_OFF",
Expand Down
2 changes: 2 additions & 0 deletions data/json/itemgroups/SUS/office.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
{ "item": "file", "count": [ 1, 8 ], "prob": 50 },
{ "item": "paper", "count": [ 2, 9 ], "prob": 60 },
{ "item": "pen", "count": [ 1, 10 ], "prob": 95 },
{ "item": "book_binder", "prob": 90 },
{ "item": "stapler", "prob": 60 },
{ "item": "scissors", "prob": 35 },
{ "item": "phonebook", "prob": 10 },
Expand All @@ -32,6 +33,7 @@
"entries": [
{ "item": "file", "count": [ 1, 15 ], "prob": 95 },
{ "item": "paper", "count": [ 2, 30 ], "prob": 80 },
{ "item": "book_binder", "prob": 90 },
{ "item": "mobile_memory_card_used", "count": [ 1, 4 ], "prob": 15 },
{ "item": "mobile_memory_card_encrypted", "count": [ 1, 2 ], "prob": 15 }
]
Expand Down
6 changes: 6 additions & 0 deletions data/json/items/ammo_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,12 @@
"name": "compressed air",
"default": "nitrox"
},
{
"type": "ammunition_type",
"id": "paper",
"name": "paper",
"default": "paper"
},
{
"id": "tinder",
"name": "tinder",
Expand Down
6 changes: 3 additions & 3 deletions data/json/items/comestibles/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,14 @@
},
{
"type": "COMESTIBLE",
"id": "paper",
"name": "paper",
"id": "napkin",
"name": { "str": "napkin" },
"category": "spare_parts",
"weight": "3 g",
"color": "white",
"comestible_type": "FOOD",
"symbol": "`",
"description": "A piece of paper. Can be used for fires.",
"description": "A napkin. Can be used for fires.",
Comment on lines +236 to +243
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is adding this new item intentional? It looks like a merge conflict.
There are no references to napkin anywhere, it won't spawn in game world.

"price": 0,
"price_postapoc": 0,
"material": "paper",
Expand Down
37 changes: 37 additions & 0 deletions data/json/items/generic.json
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,43 @@
"to_hit": -3,
"flags": [ "TRADER_AVOID", "NO_REPAIR" ]
},
{
"type": "AMMO",
"id": "paper",
"name": { "str": "paper" },
"category": "spare_parts",
"weight": "3 g",
"color": "white",
"symbol": "`",
"description": "A piece of paper. Can be used for fires.",
"price": 0,
"price_postapoc": 0,
"material": [ "paper" ],
"volume": "6 ml",
"ammo_type": "paper"
},
{
"id": "book_binder",
"type": "TOOL",
"category": "books",
"use_action": "BINDER_ADD_RECIPE",
"symbol": "/",
"looks_like": "story_book",
"color": "light_gray",
"name": { "str": "book binder" },
"description": "A book binder. With a pen and some paper you could copy some recipes from books.",
"weight": "200 g",
"volume": "640 ml",
"material": [ "plastic" ],
"bashing": 1,
"to_hit": -1,
"price": 500,
"ammo": "paper",
"initial_charges": 1,
"max_charges": 500,
"price_postapoc": 50,
"flags": [ "TRADER_AVOID", "NO_UNLOAD", "NO_RELOAD" ]
},
{
"type": "GENERIC",
"id": "broken_dispatch_military",
Expand Down
8 changes: 6 additions & 2 deletions data/json/items/tool/stationary.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
{
"id": "pen",
"type": "GENERIC",
"type": "TOOL",
"category": "tools",
"name": { "str": "pen" },
"description": "A plastic ball point pen.",
Expand All @@ -25,7 +25,11 @@
"price_postapoc": 10,
"material": "plastic",
"symbol": ";",
"color": "light_gray"
"color": "light_gray",
"initial_charges": 200,
"max_charges": 200,
"charges_per_use": 1,
"flags": [ "WRITE_MESSAGE" ]
},
{
"id": "scissors",
Expand Down
7 changes: 7 additions & 0 deletions data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,13 @@
"based_on": "time",
"no_resume": true
},
{
"id": "ACT_BINDER_COPY_RECIPE",
"type": "activity_type",
"verb": "writing",
"rooted": true,
"based_on": "speed"
},
{
"id": "ACT_MIGRATION_CANCEL",
"type": "activity_type",
Expand Down
1 change: 1 addition & 0 deletions data/json/recipes/other/materials.json
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@
"subcategory": "CSC_OTHER_MATERIALS",
"skill_used": "fabrication",
"skills_required": [ "survival", 4 ],
"charges": 50,
"difficulty": 5,
"time": "2 h",
"autolearn": true,
Expand Down
11 changes: 11 additions & 0 deletions data/json/recipes/other/tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,17 @@
"autolearn": true,
"using": [ [ "blacksmithing_standard", 4 ], [ "steel_standard", 1 ] ]
},
{
"type": "recipe",
"result": "book_binder",
"category": "CC_OTHER",
"subcategory": "CSC_OTHER_OTHER",
"skill_used": "fabrication",
"difficulty": 2,
"time": "45 m",
"autolearn": true,
"components": [ [ [ "duct_tape", 20 ] ], [ [ "plastic_chunk", 2 ] ], [ [ "string_6", 2 ] ] ]
},
{
"type": "recipe",
"result": "teapot",
Expand Down
5 changes: 5 additions & 0 deletions data/json/tool_qualities.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@
"id": "PRY",
"name": { "str": "prying" }
},
{
"type": "tool_quality",
"id": "WRITE",
"name": { "str": "writing" }
},
{
"type": "tool_quality",
"id": "LIFT",
Expand Down
3 changes: 2 additions & 1 deletion doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ These are handled through `ammo_types.json`. You can tag a weapon with these to
- ```thrown``` Thrown
- ```unfinished_char``` Semi-charred fuel
- ```water``` Water
- ```paper``` Paper

### Effects

Expand Down Expand Up @@ -403,7 +404,7 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite
- ```WASH_HARD_ITEMS``` Wash hard items with FILTHY flag.
- ```WASH_SOFT_ITEMS``` Wash soft items with FILTHY flag.
- ```WATER_PURIFIER``` Purify water.

- ```BINDER_ADD_RECIPE``` Add recipe to a book binder.

## Comestibles

Expand Down
72 changes: 72 additions & 0 deletions src/activity_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "avatar.h"
#include "calendar.h"
#include "character.h"
#include "character_functions.h"
#include "crafting.h"
#include "debug.h"
#include "enums.h"
Expand Down Expand Up @@ -50,6 +51,7 @@

static const itype_id itype_bone_human( "bone_human" );
static const itype_id itype_electrohack( "electrohack" );
static const itype_id itype_paper( "paper" );

static const skill_id skill_computer( "computer" );

Expand All @@ -60,6 +62,7 @@ static const mtype_id mon_skeleton( "mon_skeleton" );
static const mtype_id mon_zombie_crawler( "mon_zombie_crawler" );

static const std::string flag_RELOAD_AND_SHOOT( "RELOAD_AND_SHOOT" );
static const std::string flag_WRITE_MESSAGE( "WRITE_MESSAGE" );

static const std::string has_thievery_witness( "has_thievery_witness" );

Expand Down Expand Up @@ -879,6 +882,74 @@ void hacking_activity_actor::finish( player_activity &act, Character &who )
act.set_to_null();
}

void bookbinder_copy_activity_actor::start( player_activity &act, Character & )
{
pages = 1 + rec_id->difficulty / 2;
act.moves_total = to_moves<int>( pages * 1_minutes );
act.moves_left = to_moves<int>( pages * 1_minutes );
}

void bookbinder_copy_activity_actor::do_turn( player_activity &, Character &p )
{
if( character_funcs::fine_detail_vision_mod( p ) > 4.0f ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A function has been added for this, no need for magic numbers anymore

Suggested change
if( character_funcs::fine_detail_vision_mod( p ) > 4.0f ) {
if( !character_funcs::can_see_fine_details( p ) ) {

p.cancel_activity();
p.add_msg_if_player( m_info, _( "It's too dark to write!" ) );
return;
}
}

void bookbinder_copy_activity_actor::finish( player_activity &act, Character &p )
{
const bool rec_added = book_binder->eipc_recipe_add( rec_id );
if( rec_added ) {
p.add_msg_if_player( m_good, _( "You copy the recipe for %1$s into your recipe book." ),
rec_id->result_name() );

p.use_charges( itype_paper, pages );
book_binder.get_item()->charges += pages;

const std::vector<const item *> writing_tools_filter =
p.crafting_inventory().items_with( [&]( const item & it ) {
return it.has_flag( flag_WRITE_MESSAGE ) && it.ammo_remaining() >= it.ammo_required();
} );

std::vector<tool_comp> writing_tools;
writing_tools.reserve( writing_tools_filter.size() );
for( const item *tool : writing_tools_filter ) {
writing_tools.emplace_back( tool_comp( tool->typeId(), 1 ) );
}

player *player = p.as_player();

player->consume_tools( writing_tools, pages );
} else {
debugmsg( "Recipe book already has '%s' recipe when it should not.", rec_id.str() );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast here is done automatically

Suggested change
debugmsg( "Recipe book already has '%s' recipe when it should not.", rec_id.str() );
debugmsg( "Recipe book already has '%s' recipe when it should not.", rec_id );

}

act.set_to_null();
}

void bookbinder_copy_activity_actor::serialize( JsonOut &jsout ) const
{
jsout.start_object();
jsout.member( "book_binder", book_binder );
jsout.member( "rec_id", rec_id );
jsout.member( "pages", pages );
jsout.end_object();
}

std::unique_ptr<activity_actor> bookbinder_copy_activity_actor::deserialize( JsonIn &jsin )
{
bookbinder_copy_activity_actor actor;

JsonObject jsobj = jsin.get_object();
jsobj.read( "book_binder", actor.book_binder );
jsobj.read( "rec_id", actor.rec_id );
jsobj.read( "pages", actor.pages );

return actor.clone();
}

void hacking_activity_actor::serialize( JsonOut &jsout ) const
{
jsout.start_object();
Expand Down Expand Up @@ -1270,6 +1341,7 @@ const std::unordered_map<activity_id, std::unique_ptr<activity_actor>( * )( Json
deserialize_functions = {
{ activity_id( "ACT_AIM" ), &aim_activity_actor::deserialize },
{ activity_id( "ACT_AUTODRIVE" ), &autodrive_activity_actor::deserialize },
{ activity_id( "ACT_BINDER_COPY_RECIPE" ), &bookbinder_copy_activity_actor::deserialize },
{ activity_id( "ACT_DIG" ), &dig_activity_actor::deserialize },
{ activity_id( "ACT_DIG_CHANNEL" ), &dig_channel_activity_actor::deserialize },
{ activity_id( "ACT_DROP" ), &drop_activity_actor::deserialize },
Expand Down
37 changes: 37 additions & 0 deletions src/activity_actor_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,43 @@ class autodrive_activity_actor : public activity_actor
static std::unique_ptr<activity_actor> deserialize( JsonIn &jsin );
};

class bookbinder_copy_activity_actor : public activity_actor
{
private:
item_location book_binder;
recipe_id rec_id;
int pages = 0;

public:

bookbinder_copy_activity_actor() = default;
bookbinder_copy_activity_actor(
const item_location &book_binder,
const recipe_id &rec_id
) : book_binder( book_binder ), rec_id( rec_id ) {};

activity_id get_type() const override {
return activity_id( "ACT_BINDER_COPY_RECIPE" );
}

bool can_resume_with_internal( const activity_actor &other, const Character & ) const override {
const bookbinder_copy_activity_actor &act = static_cast<const bookbinder_copy_activity_actor &>
( other );
return rec_id == act.rec_id && book_binder == act.book_binder;
}

void start( player_activity &act, Character & ) override;
void do_turn( player_activity &, Character &p ) override;
void finish( player_activity &act, Character &p ) override;

std::unique_ptr<activity_actor> clone() const override {
return std::make_unique<bookbinder_copy_activity_actor>( *this );
}

void serialize( JsonOut &jsout ) const override;
static std::unique_ptr<activity_actor> deserialize( JsonIn &jsin );
};

class dig_activity_actor : public activity_actor
{
private:
Expand Down
2 changes: 1 addition & 1 deletion src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10598,4 +10598,4 @@ bool has_psy_protection( const Character &c, int partial_chance )
{
return c.has_artifact_with( AEP_PSYSHIELD ) ||
( c.worn_with_flag( "PSYSHIELD_PARTIAL" ) && one_in( partial_chance ) );
}
}
Loading