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

Implement integral magazines as magazine items #32331

Draft
wants to merge 68 commits into
base: master
from
Draft
Changes from 12 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
525aa50
give mosin 91/30 loadables
ymber Jul 12, 2019
61ce8ae
allow nameless items
ymber Jul 12, 2019
eb32e8c
load integral magazines from item definitions
ymber Jul 12, 2019
b2fff1f
create integrals in item constructor
ymber Jul 12, 2019
00377e5
show integral magazines in item info
ymber Jul 12, 2019
67a0d96
dont show integral mags in item contents
ymber Jul 12, 2019
60ac847
give integral mags default names if none given
ymber Jul 12, 2019
bf198ba
implement item::integral_magazines
ymber Jul 12, 2019
f9cf2f8
check for unassigned names in check_definitions
ymber Jul 12, 2019
26b8bb5
Update is_reloadable helper for integral mags
ymber Jul 12, 2019
dbd0a01
update find_ammo_helper
ymber Jul 12, 2019
402921d
load clips data for magazines
ymber Jul 12, 2019
a2f9c06
throw json error from loadable if type is wrong
ymber Jul 13, 2019
9823371
simplify check in is_reloadable_helper
ymber Jul 13, 2019
962e538
update mosin loadables
ymber Jul 13, 2019
33b413f
return to mandatory item names
ymber Jul 13, 2019
faf3858
Update reload_finish handling of RELOAD_ONE flag
ymber Jul 13, 2019
66441f1
update item::info for integral mag reload time
ymber Jul 13, 2019
757f08f
Update item::is_reloadable_helper for speedloaders
ymber Jul 13, 2019
4d59c78
update find_ammo_helper for speedloaders
ymber Jul 13, 2019
055d1ca
remove unneeded special case from list_ammo
ymber Jul 13, 2019
ca495fc
simplify activity_handlers::reload_finish
ymber Jul 13, 2019
9449479
Remove obsolete definition check
ymber Jul 13, 2019
0a8f0a6
update item::reload to handle speedloaders right
ymber Jul 13, 2019
8565d00
Translate new strings
ymber Jul 13, 2019
27973b4
Revert "simplify activity_handlers::reload_finish"
ymber Jul 13, 2019
3a43e9a
update unloading for integral mag items
ymber Jul 13, 2019
0c31bf1
Update specific reloading tests
ymber Jul 14, 2019
56e3410
Allow integral mag names to be set in JSON
ymber Jul 14, 2019
79b9d74
Test item::is_reloadable_with in reload tests
ymber Jul 14, 2019
b856754
Update automatic reloading test
ymber Jul 14, 2019
bad253b
Update value condition for magazine_integral
ymber Jul 15, 2019
af7a45b
update ammo_set and ammo_unset
ymber Jul 15, 2019
1e61262
update Item_modifier::modify
ymber Jul 15, 2019
a6c2bcd
Update tool magazine display in item::info
ymber Jul 16, 2019
759c01c
Remove integral magazine check from item::price
ymber Jul 16, 2019
c890b83
Remove code for old integral mags in item::weight
ymber Jul 16, 2019
4495b2b
Remove redundant mag check in map::place_items
ymber Jul 16, 2019
6aeb6e0
Remove redundant mag check in player:select_ammo
ymber Jul 16, 2019
0e7a8b6
Remove redundant check in has_magazine_for_ammo
ymber Jul 16, 2019
b5096a5
Remove redundant mag check in place_spawn_items
ymber Jul 16, 2019
2d3be91
Update Character::find_reloadables
ymber Jul 16, 2019
6818698
Reload correct item for RELOAD_AND_SHOOT
ymber Jul 16, 2019
fddcd1c
Block magazine display if RELOAD_AND_SHOOT
ymber Jul 16, 2019
b2a3c5b
Update item::is_reloadable conditions
ymber Jul 16, 2019
22eb605
Update bow item definitions
ymber Jul 16, 2019
e540ec5
Update misc item loadables
ymber Jul 16, 2019
6264e16
Update crossbow defs with loadables
ymber Jul 16, 2019
1c4fa33
Update more weapon definitions
ymber Jul 16, 2019
2e6f798
Update more weapon definitions with loadables
ymber Jul 16, 2019
c1f3e72
More item def updates
ymber Jul 17, 2019
9490d29
Update weapon definitions for loadables
ymber Jul 17, 2019
5d6aca2
Fix definitions
ymber Jul 18, 2019
7299370
Update last loadables defs
ymber Jul 18, 2019
b25a252
Use integral magazine name in item::info
ymber Jul 18, 2019
970c612
Style JSON
ymber Jul 18, 2019
064d9c9
Update GASMASK items with loadables
ymber Jul 18, 2019
f5b3d45
Update toolweapons with loadables
ymber Jul 18, 2019
4671385
Update tool armors with loadables
ymber Jul 19, 2019
5d3a0d8
Update scuba gear
ymber Jul 19, 2019
8b616d6
Add loadable converter script
ymber Jul 19, 2019
46df244
Update loadables in tools
ymber Jul 19, 2019
a7690a7
Update fish trap for loadables
ymber Jul 19, 2019
1746e79
Update hotplate type items for loadables
ymber Jul 19, 2019
486d96d
Update static tools
ymber Jul 19, 2019
afd2746
Fix reloading ALLOWS_REMOTE_USE items
ymber Jul 19, 2019
7f68816
Update sewing tools with loadables
ymber Jul 19, 2019
9d0d7c3
Update remaining loadable tools
ymber Jul 19, 2019
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -51,7 +51,18 @@
"range": 3,
"dispersion": 90,
"durability": 10,
"clip_size": 5,
"loadables": {
"integrals": [
{
"id": "mosin_integral_mag",
"type": "MAGAZINE",
"ammo_type": [ "762R" ],
"capacity": 5,
"reliability": 9,
"clips": [ "762R_clip" ]
}
]
},
"barrel_length": 4,
"valid_mod_locations": [
[ "accessories", 4 ],
@@ -62,7 +73,6 @@
[ "sights", 1 ],
[ "sling", 1 ]
],
"magazines": [ [ "762R", [ "762R_clip" ] ] ],
"flags": [ "RELOAD_ONE" ]
},
{
@@ -1084,12 +1084,12 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes
} );
}
}
if( obj.magazine_integral() ) {
if( obj.is_magazine() ) {
// find suitable ammo excluding that already loaded in magazines
std::set<ammotype> ammo = obj.ammo_types();
const auto mags = obj.magazine_compatible();

src.visit_items( [&src, &nested, &out, &mags, ammo]( item * node ) {
src.visit_items( [&src, &nested, &out, ammo]( item * node ) {
if( node->is_gun() || node->is_tool() ) {
// guns/tools never contain usable ammo so most efficient to skip them now
return VisitResponse::SKIP;
@@ -1108,15 +1108,8 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes
return VisitResponse::SKIP;
}

for( const ammotype &at : ammo ) {
if( node->ammo_type() == at ) {
out = item_location( src, node );
}
}
if( node->is_magazine() && node->has_flag( "SPEEDLOADER" ) ) {
if( mags.count( node->typeId() ) && node->ammo_remaining() ) {
out = item_location( src, node );
}
if( ammo.count( node->ammo_type() ) ) {
out = item_location( src, node );
}
return nested ? VisitResponse::NEXT : VisitResponse::SKIP;
} );
@@ -212,6 +212,10 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), bday(
set_var( "NANOFAB_ITEM_ID", nanofab_recipe );
}

for( const itype &it : type->integral_loadables ) {
emplace_back( &it, turn, qty ).item_tags.insert( "IRREMOVABLE" );
}

if( type->gun ) {
for( const auto &mod : type->gun->built_in_mods ) {
emplace_back( mod, turn, qty ).item_tags.insert( "IRREMOVABLE" );
@@ -1464,11 +1468,15 @@ std::string item::info( std::vector<iteminfo> &info, const iteminfo_query *parts
"<info>" + skill.name() + "</info>" ) );
}

if( mod->magazine_integral() || mod->magazine_current() ) {
if( mod->magazine_current() && parts->test( iteminfo_parts::GUN_MAGAZINE ) ) {
info.emplace_back( "GUN", _( "Magazine: " ),
string_format( "<stat>%s</stat>",
mod->magazine_current()->tname() ) );
if( mod->magazine_current() ) {
if( mod->magazine_current() && parts->test( iteminfo_parts::GUN_MAGAZINE ) &&
!mod->magazine_current()->has_flag( "IRREMOVABLE" ) ) {
info.emplace_back( "GUN", _( "Magazine: " ), string_format( "<stat>%s</stat>",
mod->magazine_current()->tname() ) );
} else if( mod->magazine_current() && parts->test( iteminfo_parts::GUN_MAGAZINE ) &&
mod->magazine_current()->has_flag( "IRREMOVABLE" ) ) {
info.emplace_back( "GUN", _( "Magazine: " ), string_format( "<stat>%s integral magazine</stat>",
This conversation was marked as resolved by ymber

This comment has been minimized.

Copy link
@BevapDin

BevapDin Jul 12, 2019

Contributor

Needs translation.

mod->tname() ) );
}
if( mod->ammo_capacity() && parts->test( iteminfo_parts::GUN_CAPACITY ) ) {
for( const ammotype &at : mod->ammo_types() ) {
@@ -2715,8 +2723,18 @@ std::string item::info( std::vector<iteminfo> &info, const iteminfo_query *parts
info.push_back( iteminfo( "DESCRIPTION", ntext + item_note->second ) );
}

// We don't necessarily want to show the player all of an item's contents.
// Create a list of only the things we want to show and print from that.
std::list<item> print_contents;
for( const item &it : contents ) {
if( it.is_magazine() && it.is_irremovable() ) {
continue;
} else {
print_contents.emplace_back( it );
}
}
// describe contents
if( !contents.empty() && parts->test( iteminfo_parts::DESCRIPTION_CONTENTS ) ) {
if( !print_contents.empty() && parts->test( iteminfo_parts::DESCRIPTION_CONTENTS ) ) {
for( const auto mod : is_gun() ? gunmods() : toolmods() ) {
if( mod->type->gunmod ) {
temp1.str( "" );
@@ -2732,7 +2750,7 @@ std::string item::info( std::vector<iteminfo> &info, const iteminfo_query *parts
info.emplace_back( "DESCRIPTION", _( mod->type->description ) );
}
bool contents_header = false;
for( const auto &contents_item : contents ) {
for( const auto &contents_item : print_contents ) {
if( !contents_item.type->mod ) {
if( !contents_header ) {
insert_separation_line();
@@ -5334,24 +5352,22 @@ bool item::is_reloadable_helper( const itype_id &ammo, bool now ) const
return ( ( now ? !is_container_full() : true ) && ( ammo.empty()
|| ( find_type( ammo )->phase == LIQUID && ( is_container_empty()
|| contents.front().typeId() == ammo ) ) ) );
} else if( magazine_integral() ) {
} else if( is_magazine() ) {
if( !ammo.empty() ) {
if( ammo_data() ) {
if( ammo_current() != ammo ) {
return false;
}
} else {
auto at = find_type( ammo );
if( ( !at->ammo || !ammo_types().count( at->ammo->type ) ) &&
!magazine_compatible().count( ammo ) ) {
return false;
}
if( ammo_data() && ammo_current() != ammo ) {
return false;
}
const itype *at = find_type( ammo );
if( !at->ammo || !ammo_types().count( at->ammo->type ) ) {
return false;
}
}
return now ? ( ammo_remaining() < ammo_capacity() ) : true;
} else {
return ammo.empty() ? true : magazine_compatible().count( ammo );
if( now && !( ammo_remaining() < ammo_capacity() ) ) {
This conversation was marked as resolved by ymber

This comment has been minimized.

Copy link
@BevapDin

BevapDin Jul 12, 2019

Contributor
Suggested change
if( now && !( ammo_remaining() < ammo_capacity() ) ) {
if( now && ammo_remaining() >= ammo_capacity() ) {
return false;
}
return true;
}
return ammo.empty() ? true : magazine_compatible().count( ammo );
}

bool item::is_salvageable() const
@@ -6126,6 +6142,17 @@ const item *item::magazine_current() const
return const_cast<item *>( this )->magazine_current();
}

std::vector<const item *> item::integral_magazines() const
{
std::vector<const item *> mags;
for( const item &it : contents ) {
if( it.is_magazine() && it.is_irremovable() ) {
mags.push_back( &it );
}
}
return mags;
}

std::vector<item *> item::gunmods()
{
std::vector<item *> res;
@@ -1648,6 +1648,11 @@ class item : public visitable<item>
item *magazine_current();
const item *magazine_current() const;

/** All integral magazines in an item
* @return vector of pointers to integral magazine items
*/
std::vector<const item *> integral_magazines() const;

/** Normalizes an item to use the new magazine system. Idempotent if item already converted.
* @return items that were created as a result of the conversion (excess ammo or magazines) */
std::vector<item> magazine_convert();
@@ -863,6 +863,9 @@ void Item_factory::check_definitions() const
if( type->description.empty() ) {
msg << "empty description" << "\n";
}
if( type->name == "none" ) {
msg << "no name given" << "\n";
}

for( const material_id &mat_id : type->materials ) {
if( mat_id.str() == "null" || !mat_id.is_valid() ) {
@@ -1877,6 +1880,7 @@ void Item_factory::load( islot_magazine &slot, JsonObject &jo, const std::string
assign( jo, "reliability", slot.reliability, strict, 0, 10 );
assign( jo, "reload_time", slot.reload_time, strict, 0 );
assign( jo, "linkage", slot.linkage, strict );
assign( jo, "clips", slot.clips, strict );
}

void Item_factory::load_magazine( JsonObject &jo, const std::string &src )
@@ -2047,11 +2051,13 @@ void Item_factory::load_basic_info( JsonObject &jo, itype &def, const std::strin
def.damage_max = arr.get_int( 1 ) * itype::damage_scale;
}

def.name = jo.get_string( "name" );
if( jo.has_member( "name_plural" ) ) {
def.name_plural = jo.get_string( "name_plural" );
} else {
def.name_plural = jo.get_string( "name" ) += "s";
if( jo.has_string( "name" ) ) {
def.name = jo.get_string( "name" );
if( jo.has_member( "name_plural" ) ) {
def.name_plural = jo.get_string( "name_plural" );
} else {
def.name_plural = jo.get_string( "name" ) += "s";
}
}

if( jo.has_string( "description" ) ) {
@@ -2077,6 +2083,24 @@ void Item_factory::load_basic_info( JsonObject &jo, itype &def, const std::strin
def.phase = jo.get_enum_value<phase_id>( "phase" );
}

JsonObject loadables = jo.get_object( "loadables" );
JsonArray integrals = loadables.get_array( "integrals" );
for( size_t i = 0; i < integrals.size(); ++i ) {
JsonObject integral_def = integrals.get_object( i );
itype integral_type;
if( integral_def.get_string( "type" ) == "MAGAZINE" ) {
load_slot( integral_type.magazine, integral_def, src );
load_basic_info( integral_def, integral_type, src );
if( integral_type.name == "none" ) {
integral_type.name = string_format( "%s integral magazine", jo.get_string( "name" ) );
This conversation was marked as resolved by ymber

This comment has been minimized.

Copy link
@BevapDin

BevapDin Jul 12, 2019

Contributor

Needs translation. Why make the name optional anyway?

This comment has been minimized.

Copy link
@ymber

ymber Jul 13, 2019

Author Member

I was looking at using anonymous items that only have a handful of attributes for integral items but with the way the code is already set up it will work better to do them as proper items.

}
} else {
debugmsg( "integral loadable must have MAGAZINE type" );
This conversation was marked as resolved by ymber

This comment has been minimized.

Copy link
@BevapDin

BevapDin Jul 12, 2019

Contributor
Suggested change
debugmsg( "integral loadable must have MAGAZINE type" );
integral_def.throw_error( "integral loadable must have MAGAZINE type", "type" );

So the user gets a better error message along with the JSON context.

continue;
}
def.integral_loadables.push_back( integral_type );
}

if( jo.has_array( "magazines" ) ) {
def.magazine_default.clear();
def.magazines.clear();
@@ -593,6 +593,9 @@ struct islot_magazine {
/** What type of ammo this magazine can be loaded with */
std::set<ammotype> type;

/** What clips can be used to load this magazine */
std::set<itype_id> clips;

/** Capacity of magazine (in equivalent units to ammo charges) */
int capacity = 0;

@@ -899,6 +902,9 @@ struct itype {
/** What faults (if any) can occur */
std::set<fault_id> faults;

/** Integral magazines and batteries */
std::list<itype> integral_loadables;

/** Magazine types (if any) for each ammo type that can be used to reload this item */
std::map< ammotype, std::set<itype_id> > magazines;

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.