@@ -43,7 +43,66 @@ const efftype_id effect_webbed( "webbed" );
const skill_id skill_dodge( "dodge" );
const skill_id skill_throw( "throw" );

const std::string debug_nodmg( "DEBUG_NODMG" );
static const trait_id trait_ACIDBLOOD( "ACIDBLOOD" );
static const trait_id trait_ARACHNID_ARMS( "ARACHNID_ARMS" );
static const trait_id trait_ARM_TENTACLES_4( "ARM_TENTACLES_4" );
static const trait_id trait_ARM_TENTACLES_8( "ARM_TENTACLES_8" );
static const trait_id trait_ARM_TENTACLES( "ARM_TENTACLES" );
static const trait_id trait_BADBACK( "BADBACK" );
static const trait_id trait_BENDY2( "BENDY2" );
static const trait_id trait_BENDY3( "BENDY3" );
static const trait_id trait_BIRD_EYE( "BIRD_EYE" );
static const trait_id trait_CEPH_EYES( "CEPH_EYES" );
static const trait_id trait_CEPH_VISION( "CEPH_VISION" );
static const trait_id trait_CHITIN2( "CHITIN2" );
static const trait_id trait_CHITIN3( "CHITIN3" );
static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" );
static const trait_id trait_DEBUG_NIGHTVISION( "DEBUG_NIGHTVISION" );
static const trait_id trait_DISORGANIZED( "DISORGANIZED" );
static const trait_id trait_ELFA_FNV( "ELFA_FNV" );
static const trait_id trait_ELFA_NV( "ELFA_NV" );
static const trait_id trait_FEL_NV( "FEL_NV" );
static const trait_id trait_FLIMSY2( "FLIMSY2" );
static const trait_id trait_FLIMSY3( "FLIMSY3" );
static const trait_id trait_FLIMSY( "FLIMSY" );
static const trait_id trait_GLASSJAW( "GLASSJAW" );
static const trait_id trait_HOLLOW_BONES( "HOLLOW_BONES" );
static const trait_id trait_HUGE( "HUGE" );
static const trait_id trait_INSECT_ARMS( "INSECT_ARMS" );
static const trait_id trait_LIGHT_BONES( "LIGHT_BONES" );
static const trait_id trait_MEMBRANE( "MEMBRANE" );
static const trait_id trait_MUT_TOUGH2( "MUT_TOUGH2" );
static const trait_id trait_MUT_TOUGH3( "MUT_TOUGH3" );
static const trait_id trait_MUT_TOUGH( "MUT_TOUGH" );
static const trait_id trait_MYOPIC( "MYOPIC" );
static const trait_id trait_NIGHTVISION2( "NIGHTVISION2" );
static const trait_id trait_NIGHTVISION3( "NIGHTVISION3" );
static const trait_id trait_NIGHTVISION( "NIGHTVISION" );
static const trait_id trait_PACKMULE( "PACKMULE" );
static const trait_id trait_PER_SLIME_OK( "PER_SLIME_OK" );
static const trait_id trait_PER_SLIME( "PER_SLIME" );
static const trait_id trait_SHELL2( "SHELL2" );
static const trait_id trait_SHELL( "SHELL" );
static const trait_id trait_STRONGBACK( "STRONGBACK" );
static const trait_id trait_TAIL_CATTLE( "TAIL_CATTLE" );
static const trait_id trait_TAIL_FLUFFY( "TAIL_FLUFFY" );
static const trait_id trait_TAIL_LONG( "TAIL_LONG" );
static const trait_id trait_TAIL_RAPTOR( "TAIL_RAPTOR" );
static const trait_id trait_TAIL_RAT( "TAIL_RAT" );
static const trait_id trait_TAIL_THICK( "TAIL_THICK" );
static const trait_id trait_THICK_SCALES( "THICK_SCALES" );
static const trait_id trait_THRESH_CEPHALOPOD( "THRESH_CEPHALOPOD" );
static const trait_id trait_THRESH_INSECT( "THRESH_INSECT" );
static const trait_id trait_THRESH_PLANT( "THRESH_PLANT" );
static const trait_id trait_THRESH_SPIDER( "THRESH_SPIDER" );
static const trait_id trait_TOUGH2( "TOUGH2" );
static const trait_id trait_TOUGH3( "TOUGH3" );
static const trait_id trait_TOUGH( "TOUGH" );
static const trait_id trait_URSINE_EYE( "URSINE_EYE" );
static const trait_id trait_WEBBED( "WEBBED" );
static const trait_id trait_WINGS_BAT( "WINGS_BAT" );
static const trait_id trait_WINGS_BUTTERFLY( "WINGS_BUTTERFLY" );
static const trait_id debug_nodmg( "DEBUG_NODMG" );

Character::Character() : Creature(), visitable<Character>()
{
@@ -74,13 +133,13 @@ Character::Character() : Creature(), visitable<Character>()

field_id Character::bloodType() const
{
if (has_trait("ACIDBLOOD"))
if (has_trait( trait_ACIDBLOOD ))
return fd_acid;
if (has_trait("THRESH_PLANT"))
if (has_trait( trait_THRESH_PLANT ))
return fd_blood_veggy;
if (has_trait("THRESH_INSECT") || has_trait("THRESH_SPIDER"))
if (has_trait( trait_THRESH_INSECT ) || has_trait( trait_THRESH_SPIDER ))
return fd_blood_insect;
if (has_trait("THRESH_CEPHALOPOD"))
if (has_trait( trait_THRESH_CEPHALOPOD ))
return fd_blood_invertebrate;
return fd_blood;
}
@@ -348,7 +407,7 @@ void Character::recalc_hp()
elem = 60 + str_max * 3 + hp_adjustment;
elem *= hp_mod;
}
if( has_trait( "GLASSJAW" ) ) {
if( has_trait( trait_GLASSJAW ) ) {
new_max_hp[hp_head] *= 0.8;
}
for( int i = 0; i < num_hp_parts; i++ ) {
@@ -376,67 +435,67 @@ void Character::recalc_sight_limits()
// Set sight_max.
if( is_blind() ) {
sight_max = 0;
} else if( has_effect( effect_boomered ) && (!(has_trait("PER_SLIME_OK"))) ) {
} else if( has_effect( effect_boomered ) && (!(has_trait( trait_PER_SLIME_OK ))) ) {
sight_max = 1;
vision_mode_cache.set( BOOMERED );
} else if (has_effect( effect_in_pit ) ||
(underwater && !has_bionic("bio_membrane") &&
!has_trait("MEMBRANE") && !worn_with_flag("SWIM_GOGGLES") &&
!has_trait("CEPH_EYES") && !has_trait("PER_SLIME_OK") ) ) {
!has_trait( trait_MEMBRANE ) && !worn_with_flag("SWIM_GOGGLES") &&
!has_trait( trait_CEPH_EYES ) && !has_trait( trait_PER_SLIME_OK ) ) ) {
sight_max = 1;
} else if (has_active_mutation("SHELL2")) {
} else if (has_active_mutation( trait_SHELL2 )) {
// You can kinda see out a bit.
sight_max = 2;
} else if ( (has_trait("MYOPIC") || has_trait("URSINE_EYE")) &&
} else if ( (has_trait( trait_MYOPIC ) || has_trait( trait_URSINE_EYE )) &&
!is_wearing("glasses_eye") && !is_wearing("glasses_monocle") &&
!is_wearing("glasses_bifocal") && !has_effect( effect_contacts )) {
sight_max = 4;
} else if (has_trait("PER_SLIME")) {
} else if (has_trait( trait_PER_SLIME )) {
sight_max = 6;
} else if( has_effect( effect_darkness ) ) {
vision_mode_cache.set( DARKNESS );
sight_max = 10;
}

// Debug-only NV, by vache's request
if( has_trait("DEBUG_NIGHTVISION") ) {
if( has_trait( trait_DEBUG_NIGHTVISION ) ) {
vision_mode_cache.set( DEBUG_NIGHTVISION );
}
if( has_nv() ) {
vision_mode_cache.set( NV_GOGGLES );
}
if( has_active_mutation("NIGHTVISION3") || is_wearing("rm13_armor_on") ) {
if( has_active_mutation( trait_NIGHTVISION3 ) || is_wearing("rm13_armor_on") ) {
vision_mode_cache.set( NIGHTVISION_3 );
}
if( has_active_mutation("ELFA_FNV") ) {
if( has_active_mutation( trait_ELFA_FNV ) ) {
vision_mode_cache.set( FULL_ELFA_VISION );
}
if( has_active_mutation("CEPH_VISION") ) {
if( has_active_mutation( trait_CEPH_VISION ) ) {
vision_mode_cache.set( CEPH_VISION );
}
if (has_active_mutation("ELFA_NV")) {
if (has_active_mutation( trait_ELFA_NV )) {
vision_mode_cache.set( ELFA_VISION );
}
if( has_active_mutation("NIGHTVISION2") ) {
if( has_active_mutation( trait_NIGHTVISION2 ) ) {
vision_mode_cache.set( NIGHTVISION_2 );
}
if( has_active_mutation("FEL_NV") ) {
if( has_active_mutation( trait_FEL_NV ) ) {
vision_mode_cache.set( FELINE_VISION );
}
if( has_active_mutation("URSINE_EYE") ) {
if( has_active_mutation( trait_URSINE_EYE ) ) {
vision_mode_cache.set( URSINE_VISION );
}
if (has_active_mutation("NIGHTVISION")) {
if (has_active_mutation( trait_NIGHTVISION )) {
vision_mode_cache.set(NIGHTVISION_1);
}
if( has_trait("BIRD_EYE") ) {
if( has_trait( trait_BIRD_EYE ) ) {
vision_mode_cache.set( BIRD_EYE);
}

// Not exactly a sight limit thing, but related enough
if( has_active_bionic( "bio_infrared" ) ||
has_trait( "INFRARED" ) ||
has_trait( "LIZ_IR" ) ||
has_trait( trait_id( "INFRARED" ) ) ||
has_trait( trait_id( "LIZ_IR" ) ) ||
worn_with_flag( "IR_EFFECT" ) ) {
vision_mode_cache.set( IR_VISION );
}
@@ -470,12 +529,12 @@ float Character::get_vision_threshold( float light_level ) const {
float range = get_per() / 3.0f - encumb( bp_eyes ) / 10.0f;
if( vision_mode_cache[NV_GOGGLES] || vision_mode_cache[NIGHTVISION_3] ||
vision_mode_cache[FULL_ELFA_VISION] || vision_mode_cache[CEPH_VISION] ) {
range += 10;
range += 30;
} else if( vision_mode_cache[NIGHTVISION_2] || vision_mode_cache[FELINE_VISION] ||
vision_mode_cache[URSINE_VISION] || vision_mode_cache[ELFA_VISION] ) {
range += 4.5;
range += 13.5;
} else if( vision_mode_cache[NIGHTVISION_1] ) {
range += 2;
range += 6;
}

if( vision_mode_cache[BIRD_EYE] ) {
@@ -795,7 +854,7 @@ units::volume Character::volume_carried() const

int Character::weight_capacity() const
{
if( has_trait( "DEBUG_STORAGE" ) ) {
if( has_trait( trait_id( "DEBUG_STORAGE" ) ) ) {
// Infinite enough
return INT_MAX >> 2;
}
@@ -804,16 +863,16 @@ int Character::weight_capacity() const
int ret = Creature::weight_capacity();
///\EFFECT_STR increases carrying capacity
ret += get_str() * 4000;
if (has_trait("BADBACK")) {
if( has_trait( trait_id( "BADBACK" ) ) ) {
ret = int(ret * .65);
}
if (has_trait("STRONGBACK")) {
if( has_trait( trait_id( "STRONGBACK" ) ) ) {
ret = int(ret * 1.35);
}
if (has_trait("LIGHT_BONES")) {
if( has_trait( trait_id( "LIGHT_BONES" ) ) ) {
ret = int(ret * .80);
}
if (has_trait("HOLLOW_BONES")) {
if( has_trait( trait_id( "HOLLOW_BONES" ) ) ) {
ret = int(ret * .60);
}
if (has_artifact_with(AEP_CARRY_MORE)) {
@@ -832,7 +891,7 @@ units::volume Character::volume_capacity() const

units::volume Character::volume_capacity_reduced_by( units::volume mod ) const
{
if( has_trait( "DEBUG_STORAGE" ) ) {
if( has_trait( trait_id( "DEBUG_STORAGE" ) ) ) {
return units::volume_max;
}

@@ -843,16 +902,16 @@ units::volume Character::volume_capacity_reduced_by( units::volume mod ) const
if (has_bionic("bio_storage")) {
ret += 2000_ml;
}
if (has_trait("SHELL")) {
if( has_trait( trait_SHELL ) ) {
ret += 4000_ml;
}
if (has_trait("SHELL2") && !has_active_mutation("SHELL2")) {
if( has_trait( trait_SHELL2 ) && !has_active_mutation( trait_SHELL2 ) ) {
ret += 6000_ml;
}
if (has_trait("PACKMULE")) {
if( has_trait( trait_PACKMULE ) ) {
ret = ret * 1.4;
}
if (has_trait("DISORGANIZED")) {
if( has_trait( trait_DISORGANIZED ) ) {
ret = ret * 0.6;
}
return std::max( ret, 0_ml );
@@ -1110,52 +1169,52 @@ void Character::reset_stats()
mod_dex_bonus(2);

// Trait / mutation buffs
if (has_trait("THICK_SCALES")) {
if (has_trait( trait_THICK_SCALES )) {
mod_dex_bonus(-2);
}
if (has_trait("CHITIN2") || has_trait("CHITIN3") || has_trait("CHITIN_FUR3")) {
if (has_trait( trait_CHITIN2 ) || has_trait( trait_CHITIN3 ) || has_trait( trait_CHITIN_FUR3 )) {
mod_dex_bonus(-1);
}
if (has_trait("BIRD_EYE")) {
if (has_trait( trait_BIRD_EYE )) {
mod_per_bonus(4);
}
if (has_trait("INSECT_ARMS")) {
if (has_trait( trait_INSECT_ARMS )) {
mod_dex_bonus(-2);
}
if (has_trait("WEBBED")) {
if (has_trait( trait_WEBBED )) {
mod_dex_bonus(-1);
}
if (has_trait("ARACHNID_ARMS")) {
if (has_trait( trait_ARACHNID_ARMS )) {
mod_dex_bonus(-4);
}
if (has_trait("ARM_TENTACLES") || has_trait("ARM_TENTACLES_4") ||
has_trait("ARM_TENTACLES_8")) {
if (has_trait( trait_ARM_TENTACLES ) || has_trait( trait_ARM_TENTACLES_4 ) ||
has_trait( trait_ARM_TENTACLES_8 )) {
mod_dex_bonus(1);
}

// Dodge-related effects
if (has_trait("TAIL_LONG")) {
if (has_trait( trait_TAIL_LONG )) {
mod_dodge_bonus(2);
}
if (has_trait("TAIL_CATTLE")) {
if (has_trait( trait_TAIL_CATTLE )) {
mod_dodge_bonus(1);
}
if (has_trait("TAIL_RAT")) {
if (has_trait( trait_TAIL_RAT )) {
mod_dodge_bonus(2);
}
if (has_trait("TAIL_THICK") && !(has_active_mutation("TAIL_THICK")) ) {
if (has_trait( trait_TAIL_THICK ) && !(has_active_mutation( trait_TAIL_THICK )) ) {
mod_dodge_bonus(1);
}
if (has_trait("TAIL_RAPTOR")) {
if (has_trait( trait_TAIL_RAPTOR )) {
mod_dodge_bonus(3);
}
if (has_trait("TAIL_FLUFFY")) {
if (has_trait( trait_TAIL_FLUFFY )) {
mod_dodge_bonus(4);
}
if (has_trait("WINGS_BAT")) {
if (has_trait( trait_WINGS_BAT )) {
mod_dodge_bonus(-3);
}
if (has_trait("WINGS_BUTTERFLY")) {
if (has_trait( trait_WINGS_BUTTERFLY )) {
mod_dodge_bonus(-4);
}

@@ -1376,8 +1435,7 @@ void Character::mut_cbm_encumb( std::array<encumbrance_data, num_bp> &vals ) con
// Lower penalty for bps covered only by XL armor
const auto oversize = exclusive_flag_coverage( "OVERSIZE" );
for( const auto &mut_pair : my_mutations ) {
const auto &branch = mutation_branch::get( mut_pair.first );
apply_mut_encumbrance( vals, branch, oversize );
apply_mut_encumbrance( vals, *mut_pair.first, oversize );
}
}

@@ -1932,12 +1990,12 @@ bool Character::is_immune_field( const field_id fid ) const
case fd_relax_gas:
return get_env_resist( bp_mouth ) >= 15;
case fd_fungal_haze:
return has_trait("M_IMMUNE") || (get_env_resist( bp_mouth ) >= 15 &&
return has_trait( trait_id( "M_IMMUNE" ) ) || (get_env_resist( bp_mouth ) >= 15 &&
get_env_resist( bp_eyes ) >= 15);
case fd_electricity:
return is_elec_immune();
case fd_acid:
return has_trait("ACIDPROOF") ||
return has_trait( trait_id( "ACIDPROOF" ) ) ||
(!is_on_ground() && get_env_resist( bp_foot_l ) >= 15 &&
get_env_resist( bp_foot_r ) >= 15 &&
get_env_resist( bp_leg_l ) >= 15 &&
@@ -1947,7 +2005,7 @@ bool Character::is_immune_field( const field_id fid ) const
get_armor_type( DT_ACID, bp_leg_l ) >= 5 &&
get_armor_type( DT_ACID, bp_leg_r ) >= 5);
case fd_web:
return has_trait( "WEB_WALKER" );
return has_trait( trait_id( "WEB_WALKER" ) );
default:
// Suppress warning
break;
@@ -2063,8 +2121,7 @@ resistances Character::mutation_armor( body_part bp ) const
{
resistances res;
for( auto &iter : my_mutations ) {
const mutation_branch &mb = mutation_branch::get( iter.first );
res += mb.damage_resistance( bp );
res += iter.first->damage_resistance( bp );
}

return res;
@@ -224,27 +224,27 @@ class Character : public Creature, public visitable<Character>
// --------------- Mutation Stuff ---------------
// In newcharacter.cpp
/** Returns the id of a random starting trait that costs >= 0 points */
std::string random_good_trait();
trait_id random_good_trait();
/** Returns the id of a random starting trait that costs < 0 points */
std::string random_bad_trait();
trait_id random_bad_trait();

// In mutation.cpp
/** Returns true if the player has the entered trait */
bool has_trait(const std::string &flag) const override;
bool has_trait(const trait_id &flag) const override;
/** Returns true if the player has the entered starting trait */
bool has_base_trait(const std::string &flag) const;
bool has_base_trait(const trait_id &flag) const;
/** Returns true if player has a trait with a flag */
bool has_trait_flag( const std::string &flag ) const;
/** Returns true if player has a bionic with a flag */
bool has_bionic_flag( const std::string &flag ) const;
/** Returns the trait id with the given invlet, or an empty string if no trait has that invlet */
std::string trait_by_invlet( long ch ) const;
trait_id trait_by_invlet( long ch ) const;

/** Toggles a trait on the player and in their mutation list */
void toggle_trait(const std::string &flag);
void toggle_trait( const trait_id &flag );
/** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */
void set_mutation( const std::string &flag );
void unset_mutation( const std::string &flag );
void set_mutation( const trait_id &flag );
void unset_mutation( const trait_id &flag );

/** Converts a body_part to an hp_part */
static hp_part bp_to_hp(body_part bp);
@@ -268,10 +268,10 @@ class Character : public Creature, public visitable<Character>

private:
/** Retrieves a stat mod of a mutation. */
int get_mod(std::string mut, std::string arg) const;
int get_mod( const trait_id &mut, std::string arg ) const;
protected:
/** Applies stat mods to character. */
void apply_mods(const std::string &mut, bool add_remove);
void apply_mods(const trait_id &mut, bool add_remove);

/** Recalculate encumbrance for all body parts. */
std::array<encumbrance_data, num_bp> calc_encumbrance() const;
@@ -284,11 +284,11 @@ class Character : public Creature, public visitable<Character>
void item_encumb( std::array<encumbrance_data, num_bp> &vals, const item &new_item ) const;
public:
/** Handles things like destruction of armor, etc. */
void mutation_effect(std::string mut);
void mutation_effect( const trait_id &mut );
/** Handles what happens when you lose a mutation. */
void mutation_loss_effect(std::string mut);
void mutation_loss_effect( const trait_id &mut );

bool has_active_mutation(const std::string &b) const;
bool has_active_mutation( const trait_id &b ) const;

/**
* Returns resistances on a body part provided by mutations
@@ -418,6 +418,10 @@ class Character : public Creature, public visitable<Character>

/** Maximum thrown range with a given item, taking all active effects into account. */
int throw_range( const item & ) const;
/** Dispersion of a thrown item, against a given target. */
int throwing_dispersion( const item &to_throw, Creature *critter = nullptr ) const;
/** How much dispersion does one point of target's dodge add when throwing at said target? */
int throw_dispersion_per_dodge( bool add_encumbrance = true ) const;

int weight_carried() const;
units::volume volume_carried() const;
@@ -529,9 +533,9 @@ class Character : public Creature, public visitable<Character>
/** Returns a random name from NAMES_* */
void pick_name(bool bUseDefault = false);
/** Get the idents of all base traits. */
std::vector<std::string> get_base_traits() const;
std::vector<trait_id> get_base_traits() const;
/** Get the idents of all traits/mutations. */
std::vector<std::string> get_mutations() const;
std::vector<trait_id> get_mutations() const;
const std::bitset<NUM_VISION_MODES> &get_vision_modes() const
{
return vision_mode_cache;
@@ -559,8 +563,8 @@ class Character : public Creature, public visitable<Character>

protected:
void on_stat_change( const std::string &, int ) override {};
virtual void on_mutation_gain( const std::string & ) {};
virtual void on_mutation_loss( const std::string & ) {};
virtual void on_mutation_gain( const trait_id & ) {};
virtual void on_mutation_loss( const trait_id & ) {};

public:
virtual void on_item_wear( const item & ) {};
@@ -608,11 +612,11 @@ class Character : public Creature, public visitable<Character>
* If there is not entry for a mutation, the character does not have it. If the map
* contains the entry, the character has the mutation.
*/
std::unordered_map<std::string, trait_data> my_mutations;
std::unordered_map<trait_id, trait_data> my_mutations;
/**
* Contains mutation ids of the base traits.
*/
std::unordered_set<std::string> my_traits;
std::unordered_set<trait_id> my_traits;
/**
* Pointers to mutation branches in @ref my_mutations.
*/
@@ -37,17 +37,9 @@ const efftype_id effect_stemcell_treatment( "stemcell_treatment" );

int alerts = 0;

computer::computer(): name(DEFAULT_COMPUTER_NAME)
computer::computer( const std::string &new_name, int new_security ): name( new_name )
{
security = 0;
w_terminal = NULL;
w_border = NULL;
mission_id = -1;
}

computer::computer(std::string Name, int Security): name(Name)
{
security = Security;
security = new_security;
w_terminal = NULL;
w_border = NULL;
mission_id = -1;
@@ -772,7 +764,9 @@ of pureed bone & LSD."));
case COMPACT_REPEATER_MOD:
if (g->u.has_amount("radio_repeater_mod", 1)) {
for( auto miss : g->u.get_active_missions() ) {
if (miss->name() == "Install Repeater Mod"){
static const mission_type_id commo_3 = mission_type_id("MISSION_OLD_GUARD_NEC_COMMO_3"),
commo_4 = mission_type_id("MISSION_OLD_GUARD_NEC_COMMO_4");
if (miss->mission_id() == commo_3 || miss->mission_id() == commo_4) {
miss->step_complete( 1 );
print_error(_("Repeater mod installed..."));
print_error(_("Mission Complete!"));
@@ -7,8 +7,6 @@
#include <vector>
#include <string>

#define DEFAULT_COMPUTER_NAME ""

class game;
class player;
class JsonObject;
@@ -87,14 +85,13 @@ struct computer_option {
class computer
{
public:
computer();
computer( std::string Name, int Security );
computer( const std::string &name, int Security );
~computer();

computer &operator=( const computer &rhs );
// Initialization
void set_security( int Security );
void add_option( std::string opt_name, computer_action action, int Security );
void add_option( std::string opt_name, computer_action action, int security );
void add_failure( computer_failure failure );
// Basic usage
/** Shutdown (free w_terminal, etc.) */
@@ -37,6 +37,12 @@ static const skill_id skill_electronics( "electronics" );
static const skill_id skill_unarmed( "unarmed" );
static const skill_id skill_throw( "throw" );

static const trait_id trait_DEBUG_HS( "DEBUG_HS" );
static const trait_id trait_NOPAIN( "NOPAIN" );
static const trait_id trait_PAINRESIST_TROGLO( "PAINRESIST_TROGLO" );
static const trait_id trait_STOCKY_TROGLO( "STOCKY_TROGLO" );
static const trait_id trait_WEB_ROPE( "WEB_ROPE" );

// Construction functions.
namespace construct
{
@@ -136,7 +142,7 @@ void draw_grid( WINDOW *w, const int list_width )
nc_color construction_color( std::string &con_name, bool highlight )
{
nc_color col = c_dkgray;
if( g->u.has_trait( "DEBUG_HS" ) ) {
if( g->u.has_trait( trait_id( "DEBUG_HS" ) ) ) {
col = c_white;
} else if( can_construct( con_name ) ) {
construction *con_first = nullptr;
@@ -649,7 +655,7 @@ bool character_has_skill_for( const Character &c, const construction &con )

bool player_can_build( player &p, const inventory &pinv, const construction &con )
{
if( p.has_trait( "DEBUG_HS" ) ) {
if( p.has_trait( trait_DEBUG_HS ) ) {
return true;
}

@@ -990,7 +996,7 @@ void construct::done_digormine_stair( const tripoint &p, bool dig )
tmpmap.load( pos_sm.x, pos_sm.y, pos_sm.z - 1, false );
tripoint const local_tmp = tmpmap.getlocal( abs_pos );

bool dig_muts = g->u.has_trait( "PAINRESIST_TROGLO" ) || g->u.has_trait( "STOCKY_TROGLO" );
bool dig_muts = g->u.has_trait( trait_PAINRESIST_TROGLO ) || g->u.has_trait( trait_STOCKY_TROGLO );

int no_mut_penalty = dig_muts ? 10 : 0;
int mine_penalty = dig ? 0 : 10;
@@ -1063,7 +1069,7 @@ void construct::done_mine_upstair( const tripoint &p )
return;
}

bool dig_muts = g->u.has_trait( "PAINRESIST_TROGLO" ) || g->u.has_trait( "STOCKY_TROGLO" );
bool dig_muts = g->u.has_trait( trait_PAINRESIST_TROGLO ) || g->u.has_trait( trait_STOCKY_TROGLO );

int no_mut_penalty = dig_muts ? 15 : 0;
g->u.mod_hunger( 20 + no_mut_penalty );

Large diffs are not rendered by default.

@@ -132,7 +132,7 @@ bool craft_command::query_continue( const std::vector<comp_selection<item_comp>>
std::list<item> craft_command::consume_components()
{
std::list<item> used;
if( crafter->has_trait( "DEBUG_HS" ) ) {
if( crafter->has_trait( trait_id( "DEBUG_HS" ) ) ) {
return used;
}

@@ -35,6 +35,10 @@ const efftype_id effect_contacts( "contacts" );
void remove_from_component_lookup( recipe *r );
void drop_or_handle( const item &newit, player &p );

static const trait_id trait_DEBUG_HS( "DEBUG_HS" );
static const trait_id trait_PAWS_LARGE( "PAWS_LARGE" );
static const trait_id trait_PAWS( "PAWS" );

static bool crafting_allowed( const player &p, const recipe &rec )
{
if( !p.has_morale_to_craft() ) {
@@ -386,7 +390,7 @@ void player::complete_craft()

// farsightedness can impose a penalty on electronics and tailoring success
// it's equivalent to a 2-rank electronics penalty, 1-rank tailoring
if( has_trait( "HYPEROPIC" ) && !is_wearing( "glasses_reading" ) &&
if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) &&
!is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) {
int main_rank_penalty = 0;
if( making->skill_used == skill_id( "electronics" ) ) {
@@ -399,9 +403,9 @@ void player::complete_craft()

// It's tough to craft with paws. Fortunately it's just a matter of grip and fine-motor,
// not inability to see what you're doing
if( has_trait( "PAWS" ) || has_trait( "PAWS_LARGE" ) ) {
if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) {
int paws_rank_penalty = 0;
if( has_trait( "PAWS_LARGE" ) ) {
if( has_trait( trait_PAWS_LARGE ) ) {
paws_rank_penalty += 1;
}
if( making->skill_used == skill_id( "electronics" )
@@ -494,7 +498,7 @@ void player::complete_craft()
// This should fail and return, but currently crafting_command isn't saved
// Meaning there are still cases where has_cached_selections will be false
// @todo Allow saving last_craft and debugmsg+fail craft if selection isn't cached
if( !has_trait( "DEBUG_HS" ) ) {
if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
const auto &req = making->requirements();
for( const auto &it : req.get_components() ) {
std::list<item> tmp = consume_items( it, batch_size );
@@ -504,7 +508,7 @@ void player::complete_craft()
consume_tools( it, batch_size );
}
}
} else if( !has_trait( "DEBUG_HS" ) ) {
} else if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
used = last_craft->consume_components();
if( used.empty() ) {
return;
@@ -696,7 +700,7 @@ comp_selection<item_comp> player::select_item_component( const std::vector<item_

// Unlike with tools, it's a bad thing if there aren't any components available
if( cmenu.entries.empty() ) {
if( has_trait( "DEBUG_HS" ) ) {
if( has_trait( trait_id( "DEBUG_HS" ) ) ) {
selected.use_from = use_from_player;
return selected;
}
@@ -759,7 +763,7 @@ std::list<item> player::consume_items( const comp_selection<item_comp> &is, int
{
std::list<item> ret;

if( has_trait( "DEBUG_HS" ) ) {
if( has_trait( trait_DEBUG_HS ) ) {
return ret;
}

@@ -901,7 +905,7 @@ player::select_tool_component( const std::vector<tool_comp> &tools, int batch, i
/* we use this if we selected the tool earlier */
void player::consume_tools( const comp_selection<tool_comp> &tool, int batch )
{
if( has_trait( "DEBUG_HS" ) ) {
if( has_trait( trait_DEBUG_HS ) ) {
return;
}

@@ -1182,7 +1186,7 @@ void player::complete_disassemble( int item_pos, const tripoint &loc,
// Get the proper recipe - the one for disassembly, not assembly
const auto dis_requirements = dis.disassembly_requirements();
item &org_item = get_item_for_uncraft( *this, item_pos, loc, from_ground );
bool filthy = org_item.is_filthy();

if( org_item.is_null() ) {
add_msg( _( "The item has vanished." ) );
activity.set_to_null();
@@ -1280,10 +1284,6 @@ void player::complete_disassemble( int item_pos, const tripoint &loc,
// use newit, the default constructed.
item act_item = newit;

if( filthy ) {
act_item.item_tags.insert( "FILTHY" );
}

for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end();
++a ) {
if( a->type == newit.type ) {
@@ -1315,7 +1315,7 @@ void player::complete_disassemble( int item_pos, const tripoint &loc,
if( can_decomp_learn( dis ) ) {
// @todo: make this depend on intelligence
if( one_in( 4 ) ) {
learn_recipe( &dis );
learn_recipe( &recipe_dict[ dis.ident() ] );
add_msg( m_good, _( "You learned a recipe from disassembling it!" ) );
} else {
add_msg( m_info, _( "You might be able to learn a recipe if you disassemble another." ) );
@@ -1072,7 +1072,7 @@ bool Creature::resists_effect(effect e)
return false;
}

bool Creature::has_trait(const std::string &flag) const
bool Creature::has_trait( const trait_id &flag ) const
{
(void)flag;
return false;
@@ -31,6 +31,7 @@ class field;
class field_entry;
enum field_id : int;
struct pathfinding_settings;
using trait_id = string_id<mutation_branch>;

enum m_size : int {
MS_TINY = 0, // Squirrel
@@ -368,7 +369,7 @@ class Creature
virtual void process_effects();

/** Returns true if the player has the entered trait, returns false for non-humans */
virtual bool has_trait(const std::string &flag) const;
virtual bool has_trait( const trait_id &flag ) const;

// not-quite-stats, maybe group these with stats later
virtual void mod_pain(int npain);
@@ -31,12 +31,18 @@ damage_instance::damage_instance( damage_type dt, float a, float rp, float rm, f

void damage_instance::add_damage( damage_type dt, float a, float rp, float rm, float mul )
{
damage_unit du( dt, a, rp, rm, mul );
damage_units.push_back( du );
if( a * mul > 0.0f ) {
damage_unit du( dt, a, rp, rm, mul );
add( du );
}
}

void damage_instance::mult_damage( double multiplier, bool pre_armor )
{
if( multiplier <= 0.0 ) {
clear();
}

if( pre_armor ) {
for( auto &elem : damage_units ) {
elem.amount *= multiplier;
@@ -79,18 +85,26 @@ bool damage_instance::empty() const
void damage_instance::add( const damage_instance &b )
{
for( auto &added_du : b.damage_units ) {
auto iter = std::find_if( damage_units.begin(), damage_units.end(),
[&added_du]( const damage_unit & du ) {
return du.type == added_du.type;
} );
if( iter == damage_units.end() ) {
damage_units.emplace_back( added_du );
} else {
damage_unit &du = *iter;
float mult = added_du.damage_multiplier / du.damage_multiplier;
du.amount += added_du.amount * mult;
du.res_pen += added_du.res_pen * mult;
}
add( added_du );
}
}

void damage_instance::add( const damage_unit &added_du )
{
auto iter = std::find_if( damage_units.begin(), damage_units.end(),
[&added_du]( const damage_unit & du ) {
return du.type == added_du.type;
} );
if( iter == damage_units.end() ) {
damage_units.emplace_back( added_du );
} else {
damage_unit &du = *iter;
float mult = added_du.damage_multiplier / du.damage_multiplier;
du.amount += added_du.amount * mult;
du.res_pen += added_du.res_pen * mult;
// Linearly interpolate armor multiplier based on damage proportion contributed
float t = added_du.damage_multiplier / ( added_du.damage_multiplier + du.damage_multiplier );
du.res_mult = lerp( du.res_mult, added_du.damage_multiplier, t );
}
}

@@ -50,16 +50,23 @@ struct damage_instance {
std::vector<damage_unit> damage_units;
damage_instance();
static damage_instance physical( float bash, float cut, float stab, float arpen = 0.0f );
void add_damage( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f );
damage_instance( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f );
void mult_damage( double multiplier, bool pre_armor = false );
float type_damage( damage_type dt ) const;
float total_damage() const;
void clear();
bool empty() const;

/** Adds a damage instance to this one. Normalizes multipliers, which makes it very lossy. */
/**
* Adds damage to the instance.
* If the damage type already exists in the instance, the old and new instance are normalized.
* The normalization means that the effective damage can actually decrease (depending on target's armor).
*/
/*@{*/
void add_damage( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f );
void add( const damage_instance &b );
void add( const damage_unit &b );
/*@}*/
};

struct dealt_damage_instance {
@@ -136,9 +136,9 @@ void weed_msg(player *p) {
case 1: // Real Life
p->add_msg_if_player(_("Man, a cheeseburger sounds SO awesome right now."));
p->mod_hunger(4);
if(p->has_trait("VEGETARIAN")) {
if( p->has_trait( trait_id( "VEGETARIAN" ) ) ) {
p->add_msg_if_player(_("Eh... maybe not."));
} else if(p->has_trait("LACTOSE")) {
} else if( p->has_trait( trait_id( "LACTOSE" ) ) ) {
p->add_msg_if_player(_("I guess, maybe, without the cheese... yeah."));
}
return;
@@ -623,44 +623,18 @@ void effect::decay(std::vector<efftype_id> &rem_ids, std::vector<body_part> &rem
unsigned int turn, bool player)
{
// Decay duration if not permanent
if (!is_permanent()) {
duration -= 1;
add_msg( m_debug, "ID: %s, Duration %d", get_id().c_str(), duration );
if( !is_permanent() ) {
mod_duration( -1, player );
}
// Store current intensity for comparison later
int tmp_int = intensity;

// Fix bad intensities
if (intensity < 1) {
add_msg( m_debug, "Bad intensity, ID: %s", get_id().c_str() );
intensity = 1;
} else if (intensity > 1) {
// Decay intensity if necessary
if (eff_type->int_decay_tick != 0 && turn % eff_type->int_decay_tick == 0) {
intensity += eff_type->int_decay_step;
}
}
// Force intensity if it is duration based
if (eff_type->int_dur_factor != 0) {
// + 1 here so that the lowest is intensity 1, not 0
intensity = (duration / eff_type->int_dur_factor) + 1;
}
// Bound intensity to [1, max_intensity]
if (intensity > eff_type->max_intensity) {
intensity = eff_type->max_intensity;
}
if (intensity < 1) {
intensity = 1;
}
// Display decay message if available
if (player && tmp_int > intensity && (intensity - 1) < int(eff_type->decay_msgs.size())) {
// -1 because intensity = 1 is the first message
add_msg(eff_type->decay_msgs[intensity - 1].second,
eff_type->decay_msgs[intensity - 1].first.c_str());
// Decay intensity if supposed to do so
// @todo Remove effects that would decay to 0 intensity?
if( intensity > 1 && eff_type->int_decay_tick != 0 && turn % eff_type->int_decay_tick == 0 ) {
set_intensity( intensity + eff_type->int_decay_step, player );
}

// Add to removal list if duration is <= 0
if (duration <= 0) {
if( duration <= 0 ) {
rem_ids.push_back(get_id());
rem_bps.push_back(bp);
}
@@ -679,29 +653,29 @@ int effect::get_max_duration() const
{
return eff_type->max_duration;
}
void effect::set_duration(int dur)
void effect::set_duration( int dur, bool alert )
{
duration = dur;
// Cap to max_duration if it exists
if (eff_type->max_duration > 0 && duration > eff_type->max_duration) {
if( eff_type->max_duration > 0 && duration > eff_type->max_duration ) {
duration = eff_type->max_duration;
}

// Force intensity if it is duration based
if( eff_type->int_dur_factor != 0 ) {
// + 1 here so that the lowest is intensity 1, not 0
set_intensity( ( duration / eff_type->int_dur_factor ) + 1, alert );
}

add_msg( m_debug, "ID: %s, Duration %d", get_id().c_str(), duration );
}
void effect::mod_duration(int dur)
void effect::mod_duration( int dur, bool alert )
{
duration += dur;
// Cap to max_duration if it exists
if (eff_type->max_duration > 0 && duration > eff_type->max_duration) {
duration = eff_type->max_duration;
}
set_duration( duration + dur, alert );
}
void effect::mult_duration(double dur)
void effect::mult_duration( double dur, bool alert )
{
duration *= dur;
// Cap to max_duration if it exists
if (eff_type->max_duration > 0 && duration > eff_type->max_duration) {
duration = eff_type->max_duration;
}
set_duration( duration * dur, alert );
}

int effect::get_start_turn() const
@@ -742,24 +716,38 @@ int effect::get_max_intensity() const

int effect::set_intensity( int val, bool alert )
{
if( intensity < 1 ) {
// Fix bad intensity
add_msg( m_debug, "Bad intensity, ID: %s", get_id().c_str() );
intensity = 1;
}

val = std::max( std::min( val, eff_type->max_intensity ), 1 );
if( val == intensity ) {
// Nothing to change
return intensity;
}

if( alert && val < intensity ) {
if ( val - 1 < int( eff_type->decay_msgs.size() ) ) {
add_msg( eff_type->decay_msgs[ val - 1 ].second,
eff_type->decay_msgs[ val - 1 ].first.c_str() );
}
if( alert && val < intensity && val - 1 < int( eff_type->decay_msgs.size() ) ) {
add_msg( eff_type->decay_msgs[ val - 1 ].second,
eff_type->decay_msgs[ val - 1 ].first.c_str() );
}

int old_intensity = intensity;
intensity = val;
if( old_intensity != intensity ) {
add_msg( m_debug, "%s intensity %d->%d", get_id().c_str(), old_intensity, intensity );
}

return intensity = val;
return intensity;
}

int effect::mod_intensity( int mod, bool alert )
{
return set_intensity( intensity + mod, alert );
}

const std::vector<std::string> &effect::get_resist_traits() const
const std::vector<trait_id> &effect::get_resist_traits() const
{
return eff_type->resist_traits;
}
@@ -941,7 +929,7 @@ double effect::get_percentage(std::string arg, int val, bool reduced) const
if (found_tick_base != mod_data.end()) {
tick += found_tick_base->second;
}
if (found_bot_scale != mod_data.end()) {
if (found_tick_scale != mod_data.end()) {
tick += found_tick_scale->second * (intensity - 1);
}
// Tick is the exception where tick = 0 means tick = 1
@@ -1015,7 +1003,7 @@ bool effect::activated(int turn, std::string arg, int val, bool reduced, double
if (found_tick_base != mod_data.end()) {
tick += found_tick_base->second;
}
if (found_bot_scale != mod_data.end()) {
if (found_tick_scale != mod_data.end()) {
tick += found_tick_scale->second * (intensity - 1);
}
// Tick is the exception where tick = 0 means tick = 1
@@ -1146,14 +1134,10 @@ void load_effect_type(JsonObject &jo)
while (jsarr.has_more()) {
new_etype.reduced_desc.push_back(jsarr.next_string());
}
} else if (jo.has_member("desc")) {
JsonArray jsarr = jo.get_array("desc");
while (jsarr.has_more()) {
new_etype.reduced_desc.push_back(jsarr.next_string());
}
} else {
new_etype.reduced_desc.push_back("");
new_etype.reduced_desc = new_etype.desc;
}

new_etype.part_descs = jo.get_bool("part_descs", false);

if(jo.has_member("rating")) {
@@ -1177,7 +1161,9 @@ void load_effect_type(JsonObject &jo)
new_etype.apply_memorial_log = jo.get_string("apply_memorial_log", "");
new_etype.remove_memorial_log = jo.get_string("remove_memorial_log", "");

new_etype.resist_traits = jo.get_string_array("resist_traits");
for( auto &&f : jo.get_string_array( "resist_traits" ) ) {
new_etype.resist_traits.push_back( trait_id( f ) );
}
for( auto &&f : jo.get_string_array( "resist_effects" ) ) {
new_etype.resist_effects.push_back( efftype_id( f ) );
}
@@ -14,6 +14,8 @@ class Creature;
class player;
enum game_message_type : int;
using efftype_id = string_id<effect_type>;
struct mutation_branch;
using trait_id = string_id<mutation_branch>;

/** Handles the large variety of weed messages. */
void weed_msg(player *p);
@@ -84,7 +86,7 @@ class effect_type

bool main_parts_only;

std::vector<std::string> resist_traits;
std::vector<trait_id> resist_traits;
std::vector<efftype_id> resist_effects;
std::vector<efftype_id> removes_effects;
std::vector<efftype_id> blocks_effects;
@@ -161,11 +163,11 @@ class effect : public JsonSerializer, public JsonDeserializer
/** Returns the maximum duration of an effect. */
int get_max_duration() const;
/** Sets the duration, capping at max_duration if it exists. */
void set_duration( int dur );
void set_duration( int dur, bool alert = false );
/** Mods the duration, capping at max_duration if it exists. */
void mod_duration( int dur );
void mod_duration( int dur, bool alert = false );
/** Multiplies the duration, capping at max_duration if it exists. */
void mult_duration( double dur );
void mult_duration( double dur, bool alert = false );

/** Returns the turn the effect was applied. */
int get_start_turn() const;
@@ -202,7 +204,7 @@ class effect : public JsonSerializer, public JsonDeserializer
int mod_intensity( int mod, bool alert = false );

/** Returns the string id of the resist trait to be used in has_trait("id"). */
const std::vector<std::string> &get_resist_traits() const;
const std::vector<trait_id> &get_resist_traits() const;
/** Returns the string id of the resist effect to be used in has_effect("id"). */
const std::vector<efftype_id> &get_resist_effects() const;
/** Returns the string ids of the effects removed by this effect to be used in remove_effect("id"). */
@@ -38,6 +38,8 @@ const efftype_id effect_stunned( "stunned" );
const efftype_id effect_teargas( "teargas" );
const efftype_id effect_webbed( "webbed" );

static const trait_id trait_M_SKIN2( "M_SKIN2" );

#define INBOUNDS(x, y) \
(x >= 0 && x < SEEX * my_MAPSIZE && y >= 0 && y < SEEY * my_MAPSIZE)

@@ -1681,7 +1683,7 @@ void map::player_in_field( player &u )
case fd_web: {
//If we are in a web, can't walk in webs or are in a vehicle, get webbed maybe.
//Moving through multiple webs stacks the effect.
if (!u.has_trait("WEB_WALKER") && !u.in_vehicle) {
if (!u.has_trait( trait_id( "WEB_WALKER" ) ) && !u.in_vehicle) {
//between 5 and 15 minus your current web level.
u.add_effect( effect_webbed, 1, num_bp, true, cur->getFieldDensity());
cur->setFieldDensity( 0 ); //Its spent.
@@ -1702,7 +1704,7 @@ void map::player_in_field( player &u )
break;
}

if( u.has_trait( "ACIDPROOF" ) ) {
if( u.has_trait( trait_id( "ACIDPROOF" ) ) ) {
// No need for warnings
break;
}
@@ -1778,7 +1780,7 @@ void map::player_in_field( player &u )

case fd_fire:
if( u.has_active_bionic("bio_heatsink") || u.is_wearing("rm13_armor_on") ||
u.has_trait("M_SKIN2") ) {
u.has_trait( trait_M_SKIN2 ) ) {
//heatsink, suit, or internal restructuring prevents ALL fire damage.
break;
}
@@ -1905,7 +1907,7 @@ void map::player_in_field( player &u )
break;

case fd_fungal_haze:
if (!u.has_trait("M_IMMUNE") && (!inside || (inside && one_in(4))) ) {
if (!u.has_trait( trait_id( "M_IMMUNE" ) ) && (!inside || (inside && one_in(4))) ) {
u.add_env_effect( effect_fungus, bp_mouth, 4, 100, num_bp, true );
u.add_env_effect( effect_fungus, bp_eyes, 4, 100, num_bp, true );
}
@@ -1954,7 +1956,7 @@ void map::player_in_field( player &u )
//A burst of flame? Only hits the legs and torso.
if (inside) break; //fireballs can't touch you inside a car.
if (!u.has_active_bionic("bio_heatsink") && !u.is_wearing("rm13_armor_on") &&
!u.has_trait("M_SKIN2")) { //heatsink, suit, or Mycus fireproofing stops fire.
!u.has_trait( trait_M_SKIN2 )) { //heatsink, suit, or Mycus fireproofing stops fire.
u.add_msg_player_or_npc(m_bad, _("You're torched by flames!"), _("<npcname> is torched by flames!"));
u.deal_damage( nullptr, bp_leg_l, damage_instance( DT_HEAT, rng( 2, 6 ) ) );
u.deal_damage( nullptr, bp_leg_r, damage_instance( DT_HEAT, rng( 2, 6 ) ) );
@@ -2055,7 +2057,7 @@ void map::player_in_field( player &u )

case fd_incendiary:
// Mysterious incendiary substance melts you horribly.
if (u.has_trait("M_SKIN2") || cur->getFieldDensity() == 1) {
if (u.has_trait( trait_M_SKIN2 ) || cur->getFieldDensity() == 1) {
u.add_msg_player_or_npc(m_bad, _("The incendiary burns you!"), _("The incendiary burns <npcname>!"));
u.hurtall(rng(1, 3), nullptr);
} else {
@@ -2079,7 +2081,7 @@ void map::player_in_field( player &u )
bool inhaled = false;
const int density = cur->getFieldDensity();
inhaled = u.add_env_effect( effect_poison, bp_mouth, 5, density * 10 );
if( u.has_trait("THRESH_MYCUS") || u.has_trait("THRESH_MARLOSS") ) {
if( u.has_trait( trait_id( "THRESH_MYCUS" ) ) || u.has_trait( trait_id( "THRESH_MARLOSS" ) ) ) {
inhaled |= u.add_env_effect( effect_badpoison, bp_mouth, 5, density * 10 );
u.hurtall( rng( density, density * 2 ), nullptr );
u.add_msg_if_player( m_bad, _("The %s burns your skin."), cur->name().c_str() );
@@ -41,7 +41,7 @@ void fungal_effects::fungalize( const tripoint &sporep, Creature *origin, double
///\EFFECT_DEX increases chance of knocking fungal spores away with your TAIL_CATTLE

///\EFFECT_MELEE increases chance of knocking fungal sports away with your TAIL_CATTLE
if( pl.has_trait( "TAIL_CATTLE" ) &&
if( pl.has_trait( trait_id( "TAIL_CATTLE" ) ) &&
one_in( 20 - pl.dex_cur - pl.get_skill_level( skill_id( "melee" ) ) ) ) {
pl.add_msg_if_player(
_( "The spores land on you, but you quickly swat them off with your tail!" ) );
@@ -64,7 +64,7 @@ void fungal_effects::fungalize( const tripoint &sporep, Creature *origin, double
monster *origin_mon = dynamic_cast<monster *>( origin );
if( origin_mon != nullptr ) {
spore->make_ally( origin_mon );
} else if( origin != nullptr && origin->is_player() && gm.u.has_trait( "THRESH_MYCUS" ) ) {
} else if( origin != nullptr && origin->is_player() && gm.u.has_trait( trait_id( "THRESH_MYCUS" ) ) ) {
spore->friendly = 1000;
}
}
@@ -284,4 +284,4 @@ bool fungal_effects::spread_fungus( const tripoint &p )
}
return false;
}
}
}

Large diffs are not rendered by default.

@@ -96,6 +96,7 @@ class salvage_actor;
class input_context;
class map_item_stack;
struct WORLD;
class save_t;
typedef WORLD *WORLDPTR;
class overmap;
struct event;
@@ -512,6 +513,18 @@ class game
int weight_dragged; // Computed once, when you start dragging

int ter_view_x, ter_view_y, ter_view_z;

private:
WINDOW_PTR w_terrain_ptr;
WINDOW_PTR w_minimap_ptr;
WINDOW_PTR w_pixel_minimap_ptr;
WINDOW_PTR w_HP_ptr;
WINDOW_PTR w_messages_short_ptr;
WINDOW_PTR w_messages_long_ptr;
WINDOW_PTR w_location_ptr;
WINDOW_PTR w_status_ptr;
WINDOW_PTR w_status2_ptr;
public:
WINDOW *w_terrain;
WINDOW *w_overmap;
WINDOW *w_omlegend;
@@ -693,7 +706,7 @@ class game

private:
// Game-start procedures
void load( std::string worldname, std::string name ); // Load a player-specific save file
void load( std::string worldname, const save_t &name ); // Load a player-specific save file
bool load_master(std::string worldname); // Load the master data file, with factions &c
void load_weather(std::istream &fin);
bool start_game(std::string worldname); // Starts a new game in a world
@@ -71,7 +71,7 @@
#define STR_LIFT_FACTOR 50000 // 50kg/STR @todo revert to 10kg/STR

/** Weight per level of LIFT/JACK tool quality */
#define TOOL_LIFT_FACTOR 500000 // 500kg/level
#define TOOL_LIFT_FACTOR 5000000 // 5000kg/level

/** Cap JACK requirements to support arbritrarily large vehicles */
#define JACK_LIMIT 8500000 // 8500kg (8.5 metric tonnes)
@@ -52,6 +52,21 @@ const skill_id skill_survival( "survival" );
const efftype_id effect_pkill2( "pkill2" );
const efftype_id effect_teleglow( "teleglow" );

static const trait_id trait_AMORPHOUS( "AMORPHOUS" );
static const trait_id trait_ARACHNID_ARMS_OK( "ARACHNID_ARMS_OK" );
static const trait_id trait_BADKNEES( "BADKNEES" );
static const trait_id trait_BEAK_HUM( "BEAK_HUM" );
static const trait_id trait_ILLITERATE( "ILLITERATE" );
static const trait_id trait_INSECT_ARMS_OK( "INSECT_ARMS_OK" );
static const trait_id trait_M_DEFENDER( "M_DEFENDER" );
static const trait_id trait_M_DEPENDENT( "M_DEPENDENT" );
static const trait_id trait_M_FERTILE( "M_FERTILE" );
static const trait_id trait_M_SPORES( "M_SPORES" );
static const trait_id trait_PARKOUR( "PARKOUR" );
static const trait_id trait_PROBOSCIS( "PROBOSCIS" );
static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" );
static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" );

static void pick_plant( player &p, const tripoint &examp, std::string itemType, ter_id new_ter,
bool seeds = false );

@@ -734,20 +749,20 @@ void iexamine::chainfence( player &p, const tripoint &examp )
none( p, examp );
return;
}
if( p.has_trait( "ARACHNID_ARMS_OK" ) && !p.wearing_something_on( bp_torso ) ) {
if( p.has_trait( trait_ARACHNID_ARMS_OK ) && !p.wearing_something_on( bp_torso ) ) {
add_msg( _( "Climbing the fence is trivial for one such as you." ) );
p.moves -= 75; // Yes, faster than walking. 6-8 limbs are impressive.
} else if( p.has_trait( "INSECT_ARMS_OK" ) && !p.wearing_something_on( bp_torso ) ) {
} else if( p.has_trait( trait_INSECT_ARMS_OK ) && !p.wearing_something_on( bp_torso ) ) {
add_msg( _( "You quickly scale the fence." ) );
p.moves -= 90;
} else if( p.has_trait( "PARKOUR" ) ) {
} else if( p.has_trait( trait_PARKOUR ) ) {
add_msg( _( "The fence is no match for your freerunning abilities." ) );
p.moves -= 100;
} else {
p.moves -= 400;
///\EFFECT_DEX decreases chances of slipping while climbing
int climb = p.dex_cur;
if (p.has_trait( "BADKNEES" )) {
if (p.has_trait( trait_BADKNEES )) {
climb = climb / 2;
}
if( one_in( climb ) ) {
@@ -774,7 +789,7 @@ void iexamine::chainfence( player &p, const tripoint &examp )

void iexamine::bars(player &p, const tripoint &examp)
{
if(!(p.has_trait("AMORPHOUS"))) {
if(!(p.has_trait(trait_AMORPHOUS))) {
none( p, examp );
return;
}
@@ -1362,7 +1377,7 @@ bool dead_plant( bool flower, player &p, const tripoint &examp )

bool can_drink_nectar( const player &p )
{
return ((p.has_active_mutation("PROBOSCIS")) || (p.has_active_mutation("BEAK_HUM"))) &&
return (p.has_active_mutation( trait_id( "PROBOSCIS" ) ) || p.has_active_mutation( trait_id( "BEAK_HUM" ) ) ) &&
((p.get_hunger()) > 0) && (!(p.wearing_something_on(bp_mouth)));
}

@@ -1758,14 +1773,14 @@ void iexamine::aggie_plant(player &p, const tripoint &examp)
} else if (seedType == "marloss_seed") {
fungus(p, examp);
g->m.i_clear(examp);
if (p.has_trait("M_DEPENDENT") && ((p.get_hunger() > 500) || p.get_thirst() > 300 )) {
if (p.has_trait(trait_M_DEPENDENT) && ((p.get_hunger() > 500) || p.get_thirst() > 300 )) {
g->m.ter_set(examp, t_marloss);
add_msg(m_info, _("We have altered this unit's configuration to extract and provide local nutriment. The Mycus provides."));
} else if ( (p.has_trait("M_DEFENDER")) || ( (p.has_trait("M_SPORES") || p.has_trait("M_FERTILE")) &&
} else if ( (p.has_trait(trait_M_DEFENDER)) || ( (p.has_trait(trait_M_SPORES) || p.has_trait(trait_M_FERTILE)) &&
one_in(2)) ) {
g->summon_mon( mon_fungal_blossom, examp );
add_msg(m_info, _("The seed blooms forth! We have brought true beauty to this world."));
} else if ( (p.has_trait("THRESH_MYCUS")) || one_in(4)) {
} else if ( (p.has_trait(trait_THRESH_MYCUS)) || one_in(4)) {
g->m.furn_set(examp, f_flower_marloss);
add_msg(m_info, _("The seed blossoms rather rapidly..."));
} else {
@@ -2166,7 +2181,7 @@ void iexamine::keg(player &p, const tripoint &examp)
liquid_present = true;
}
}
if (!liquid_present) {
if( !liquid_present ) {
// Get list of all drinks
auto drinks_inv = p.items_with( []( const item &it ) {
return it.made_of( LIQUID );
@@ -2178,48 +2193,57 @@ void iexamine::keg(player &p, const tripoint &examp)
// Make lists of unique drinks... about third time we do this, maybe we oughta make a function next time
std::vector<itype_id> drink_types;
std::vector<std::string> drink_names;
std::vector<double> drink_rot;
for( auto &drink : drinks_inv ) {
if (std::find(drink_types.begin(), drink_types.end(), drink->typeId()) == drink_types.end()) {
drink_types.push_back(drink->typeId());
drink_names.push_back(drink->tname());
auto found_drink = std::find( drink_types.begin(), drink_types.end(), drink->typeId() );
if( found_drink == drink_types.end() ) {
drink_types.push_back( drink->typeId() );
drink_names.push_back( drink->tname()) ;
drink_rot.push_back( drink->get_relative_rot() );
} else {
auto rot_iter = std::next( drink_rot.begin(), std::distance( drink_types.begin(), found_drink ) );
// Yep, worst rot wins.
*rot_iter = std::max( *rot_iter, drink->get_relative_rot() );
}
}
// Choose drink to store in keg from list
int drink_index = 0;
if (drink_types.size() > 1) {
drink_names.push_back(_("Cancel"));
drink_index = menu_vec(false, _("Store which drink?"), drink_names) - 1;
if (drink_index == (int)drink_names.size() - 1) {
if( drink_types.size() > 1 ) {
drink_names.push_back( _( "Cancel" ) );
drink_index = menu_vec( false, _( "Store which drink?" ), drink_names ) - 1;
if( drink_index == (int)drink_names.size() - 1 ) {
drink_index = -1;
}
} else { //Only one drink type was in inventory, so it's automatically used
if (!query_yn(_("Fill the %1$s with %2$s?"), g->m.name(examp).c_str(), drink_names[0].c_str())) {
if( !query_yn( _( "Fill the %1$s with %2$s?" ),
g->m.name( examp ).c_str(), drink_names[0].c_str() ) ) {
drink_index = -1;
}
}
if (drink_index < 0) {
if( drink_index < 0 ) {
return;
}
//Store liquid chosen in the keg
itype_id drink_type = drink_types[drink_index];
int charges_held = p.charges_of(drink_type);
item drink (drink_type, 0);
itype_id drink_type = drink_types[ drink_index ];
int charges_held = p.charges_of( drink_type );
item drink( drink_type, 0 );
drink.set_relative_rot( drink_rot[ drink_index ] );
drink.charges = 0;
bool keg_full = false;
for (int i = 0; i < charges_held && !keg_full; i++) {
g->u.use_charges(drink.typeId(), 1);
for( int i = 0; i < charges_held && !keg_full; i++ ) {
g->u.use_charges( drink.typeId(), 1 );
drink.charges++;
keg_full = drink.volume() >= keg_cap;
}
if( keg_full ) {
add_msg(_("You completely fill the %1$s with %2$s."),
g->m.name(examp).c_str(), drink.tname().c_str());
add_msg( _( "You completely fill the %1$s with %2$s." ),
g->m.name( examp ).c_str(), drink.tname().c_str() );
} else {
add_msg(_("You fill the %1$s with %2$s."), g->m.name(examp).c_str(),
drink.tname().c_str());
add_msg( _( "You fill the %1$s with %2$s." ), g->m.name( examp ).c_str(),
drink.tname().c_str() );
}
p.moves -= 250;
g->m.i_clear(examp);
g->m.i_clear( examp );
g->m.add_item( examp, drink );
return;
} else {
@@ -2311,10 +2335,8 @@ bool iexamine::pour_into_keg( const tripoint &pos, item &liquid )

map_stack stack = g->m.i_at( pos );
if( stack.empty() ) {
// Not using map functions here because kegs have the NOITEM flags and map functions
// will put the liquid on a nearby tile instead.
stack.insert_at( stack.begin(), liquid );
stack.front().charges = 0; // Will be set later
g->m.add_item( pos, liquid );
g->m.i_at( pos ).front().charges = 0; // Will be set later
} else if( stack.front().typeId() != liquid.typeId() ) {
add_msg( _( "The %s already contains some %s, you can't add a different liquid to it." ),
keg_name.c_str(), stack.front().tname().c_str() );
@@ -2528,9 +2550,9 @@ void iexamine::tree_maple_tapped(player &p, const tripoint &examp)

void iexamine::shrub_marloss(player &p, const tripoint &examp)
{
if (p.has_trait("THRESH_MYCUS")) {
if (p.has_trait(trait_THRESH_MYCUS)) {
pick_plant(p, examp, "mycus_fruit", t_shrub_fungal);
} else if (p.has_trait("THRESH_MARLOSS")) {
} else if (p.has_trait(trait_THRESH_MARLOSS)) {
g->m.spawn_item( examp, "mycus_fruit" );
g->m.ter_set(examp, t_fungus);
add_msg( m_info, _("The shrub offers up a fruit, then crumbles into a fungal bed."));
@@ -2541,15 +2563,15 @@ void iexamine::shrub_marloss(player &p, const tripoint &examp)

void iexamine::tree_marloss(player &p, const tripoint &examp)
{
if (p.has_trait("THRESH_MYCUS")) {
if (p.has_trait(trait_THRESH_MYCUS)) {
pick_plant(p, examp, "mycus_fruit", t_tree_fungal);
if (p.has_trait("M_DEPENDENT") && one_in(3)) {
if (p.has_trait(trait_M_DEPENDENT) && one_in(3)) {
// Folks have a better shot at keeping fed.
add_msg(m_info, _("We have located a particularly vital nutrient deposit underneath this location."));
add_msg(m_good, _("Additional nourishment is available."));
g->m.ter_set(examp, t_marloss_tree);
}
} else if (p.has_trait("THRESH_MARLOSS")) {
} else if (p.has_trait(trait_THRESH_MARLOSS)) {
g->m.spawn_item( p.pos(), "mycus_fruit" );
g->m.ter_set(examp, t_tree_fungal);
add_msg(m_info, _("The tree offers up a fruit, then shrivels into a fungal tree."));
@@ -3006,7 +3028,7 @@ static int findBestGasDiscount(player &p)

static std::string str_to_illiterate_str(std::string s)
{
if (!g->u.has_trait("ILLITERATE")) {
if (!g->u.has_trait( trait_ILLITERATE )) {
return s;
} else {
for (auto &i : s) {
@@ -3160,7 +3182,7 @@ void iexamine::pay_gas( player &p, const tripoint &examp )
const int refund = 4;
const int cancel = 5;

if( p.has_trait( "ILLITERATE" ) ) {
if( p.has_trait( trait_ILLITERATE ) ) {
popup( _( "You're illiterate, and can't read the screen." ) );
}

@@ -3193,7 +3215,7 @@ void iexamine::pay_gas( player &p, const tripoint &examp )
long pricePerUnit = getGasPricePerLiter( discount );
std::string unitPriceStr = string_format( _( "$%0.2f" ), pricePerUnit / 100.0f );

bool can_hack = ( !p.has_trait( "ILLITERATE" ) && ( ( p.has_charges( "electrohack", 25 ) ) ||
bool can_hack = ( !p.has_trait( trait_ILLITERATE ) && ( ( p.has_charges( "electrohack", 25 ) ) ||
( p.has_bionic( "bio_fingerhack" ) && p.power_level > 24 ) ) );

uimenu amenu;
@@ -3503,7 +3525,7 @@ iexamine_function iexamine_function_from_string(std::string const &function_name
}

hack_result iexamine::hack_attempt( player &p ) {
if( p.has_trait( "ILLITERATE" ) ) {
if( p.has_trait( trait_ILLITERATE ) ) {
return HACK_UNABLE;
}
bool using_electrohack = ( p.has_charges( "electrohack", 25 ) &&
@@ -898,7 +898,7 @@ void input_context::display_help()
werase( w_help );
draw_border( w_help );
draw_scrollbar( w_help, scroll_offset, display_height,
filtered_registered_actions.size() - display_height, 10, 0, c_white, true );
filtered_registered_actions.size(), 10, 0, c_white, true );
center_print( w_help, 0, c_ltred, _( "Keybindings" ) );
fold_and_print( w_help, 1, 2, legwidth, c_white, legend.str() );

@@ -253,6 +253,19 @@ item &inventory::add_item(item newit, bool keep_invlet, bool assign_invlet)
newit.invlet = '\0';
}

// Remove letters if not in the favourites cache
if( !keep_invlet && assign_invlet && newit.invlet ) {
auto invlet_list_iter = invlet_cache.find( newit.typeId() );
bool found = false;
if( invlet_list_iter != invlet_cache.end() ) {
auto &invlet_list = invlet_list_iter->second;
found = std::find( invlet_list.begin(), invlet_list.end(), newit.invlet ) != invlet_list.end();
}
if( !found ) {
newit.invlet = '\0';
}
}

// Check how many stacks of this type already are in our inventory.
if(!keep_invlet && assign_invlet) {
// Do we have this item in our inventory favourites cache?
@@ -393,8 +406,9 @@ void inventory::form_from_map( const tripoint &origin, int range, bool assign_in
const itype *type = f.crafting_pseudo_item_type();
if (type != NULL) {
const itype *ammo = f.crafting_ammo_item_type();
item furn_item( type, calendar::turn, ammo ? count_charges_in_list( ammo, g->m.i_at( p ) ) : 0 );
item furn_item( type, calendar::turn, 0);
furn_item.item_tags.insert("PSEUDO");
furn_item.charges = ammo ? count_charges_in_list(ammo, g->m.i_at(p)) : 0;
add_item(furn_item);
}
}
@@ -525,10 +539,22 @@ void inventory::form_from_map( const tripoint &origin, int range, bool assign_in
dehydrator.item_tags.insert("PSEUDO");
add_item(dehydrator);

item press("press", 0);
press.charges = veh->fuel_left("battery", true);
press.item_tags.insert("PSEUDO");
add_item(press);
item sealer("can_sealer", 0);
sealer.item_tags.insert("PSEUDO");
add_item(sealer);

item extruder("pastaextruder", 0);
extruder.item_tags.insert("PSEUDO");
add_item(extruder);

item quern("rock_quern", 0);
extruder.item_tags.insert("PSEUDO");
add_item(quern);

// item press("press", 0);
// press.charges = veh->fuel_left("battery", true);
// press.item_tags.insert("PSEUDO");
// add_item(press);
}
if (forgepart >= 0) {
item forge("forge", 0);
@@ -1011,6 +1037,33 @@ void inventory::assign_empty_invlet(item &it, bool force)
debugmsg("could not find a hotkey for %s", it.tname().c_str());
}

void inventory::reassign_item(item &it, char invlet)
{
if( it.invlet == invlet ) { // no change needed
return;
}
std::vector<char> *invlet_list = nullptr;
if( invlet ) { // assigning a new invlet, so we always need to create the list
invlet_list = &invlet_cache[it.typeId()];
} else { // unsetting the old invlet, so we want to avoid creating the list
auto invlet_list_iter = invlet_cache.find( it.typeId() );
if( invlet_list_iter != invlet_cache.end() ) {
invlet_list = &invlet_list_iter->second;
}
}
if( invlet_list && it.invlet ) { // remove the old invlet from the cache
invlet_list->erase( std::remove_if( invlet_list->begin(), invlet_list->end(), [&it]( char cached_invlet ) {
return cached_invlet == it.invlet;
} ), invlet_list->end() );
}
if( invlet_list && invlet ) { // add the new invlet to the cache
if( std::find( invlet_list->begin(), invlet_list->end(), invlet ) == invlet_list->end() ) {
invlet_list->push_back( invlet );
}
}
it.invlet = invlet;
}

std::set<char> inventory::allocated_invlets() const
{
std::set<char> invlets;
@@ -173,6 +173,8 @@ class inventory : public visitable<inventory>
// Assigns an invlet if any remain. If none do, will assign ` if force is
// true, empty (invlet = 0) otherwise.
void assign_empty_invlet(item &it, bool force = false);
// Assigns the item with the given invlet, and updates the favourite invlet cache. Does not check for uniqueness
void reassign_item(item &it, char invlet);

std::set<char> allocated_invlets() const;

@@ -838,7 +838,7 @@ std::string item::info( bool showtext, std::vector<iteminfo> &info ) const

info.push_back( iteminfo( "FOOD", _( "Portions: " ), "", abs( int( food_item->charges ) ) ) );
if( food_item->corpse != NULL && ( debug == true || ( g != NULL &&
( g->u.has_bionic( "bio_scent_vision" ) || g->u.has_trait( "CARNIVORE" ) ||
( g->u.has_bionic( "bio_scent_vision" ) || g->u.has_trait( trait_id( "CARNIVORE" ) ) ||
g->u.has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) ) ) ) ) {
info.push_back( iteminfo( "FOOD", _( "Smells like: " ) + food_item->corpse->nname() ) );
}
@@ -884,7 +884,7 @@ std::string item::info( bool showtext, std::vector<iteminfo> &info ) const
if( g->u.has_bionic( "bio_digestion" ) ) {
info.push_back( iteminfo( "DESCRIPTION",
_( "This food has started to <neutral>rot</neutral>, but <info>your bionic digestion can tolerate it</info>." ) ) );
} else if( g->u.has_trait( "SAPROVORE" ) ) {
} else if( g->u.has_trait( trait_id( "SAPROVORE" ) ) ) {
info.push_back( iteminfo( "DESCRIPTION",
_( "This food has started to <neutral>rot</neutral>, but <info>you can tolerate it</info>." ) ) );
} else {
@@ -1591,10 +1591,6 @@ std::string item::info( bool showtext, std::vector<iteminfo> &info ) const
info.push_back( iteminfo( "DESCRIPTION",
_( "* This item can be worn on <info>either side</info> of the body." ) ) );
}
if( is_filthy() ) {
info.push_back( iteminfo( "DESCRIPTION",
_( "* This piece of clothing is <bad>filthy</bad>." ) ) );
}
if( is_power_armor() ) {
info.push_back( iteminfo( "DESCRIPTION",
_( "* This gear is a part of power armor." ) ) );
@@ -1919,8 +1915,6 @@ nc_color item::color_in_inventory() const
ret = c_cyan;
} else if(has_flag("LITCIG")) {
ret = c_red;
} else if( is_filthy() ) {
ret = c_brown;
} else if ( has_flag("LEAK_DAM") && has_flag("RADIOACTIVE") && damage() > 0 ) {
ret = c_ltgreen;
} else if (active && !is_food() && !is_food_container()) { // Active items show up as yellow
@@ -2265,10 +2259,6 @@ std::string item::tname( unsigned int quantity, bool with_prefix ) const
ret << _( " (fits)" );
}

if( is_filthy() ) {
ret << _( " (filthy)" );
}

if( is_tool() && has_flag( "USE_UPS" ) ){
ret << _( " (UPS)" );
}
@@ -2623,6 +2613,27 @@ int item::damage_melee( damage_type dt ) const
return std::max( res, 0 );
}

damage_instance item::base_damage_melee() const
{
// @todo Caching
damage_instance ret;
for( size_t i = DT_NULL + 1; i < NUM_DT; i++ ) {
damage_type dt = static_cast<damage_type>( i );
int dam = damage_melee( dt );
if( dam > 0 ) {
ret.add_damage( dt, dam );
}
}

return ret;
}

damage_instance item::base_damage_thrown() const
{
// @todo Create a separate cache for individual items (for modifiers like diamond etc.)
return type->thrown_damage;
}

int item::reach_range( const player &p ) const
{
int res = 1;
@@ -5468,9 +5479,9 @@ bool item::process_litcig( player *carrier, const tripoint &pos )
// only puff every other turn
if( item_counter % 2 == 0 ) {
int duration = 10;
if( carrier->has_trait( "TOLERANCE" ) ) {
if( carrier->has_trait( trait_id( "TOLERANCE" ) ) ) {
duration = 5;
} else if( carrier->has_trait( "LIGHTWEIGHT" ) ) {
} else if( carrier->has_trait( trait_id( "LIGHTWEIGHT" ) ) ) {
duration = 20;
}
carrier->add_msg_if_player( m_neutral, _( "You take a puff of your %s." ), tname().c_str() );
@@ -5483,7 +5494,7 @@ bool item::process_litcig( player *carrier, const tripoint &pos )
}

if( ( carrier->has_effect( effect_shakes ) && one_in( 10 ) ) ||
( carrier->has_trait( "JITTERY" ) && one_in( 200 ) ) ) {
( carrier->has_trait( trait_id( "JITTERY" ) ) && one_in( 200 ) ) ) {
carrier->add_msg_if_player( m_bad, _( "Your shaking hand causes you to drop your %s." ),
tname().c_str() );
g->m.add_item_or_charges( tripoint( pos.x + rng( -1, 1 ), pos.y + rng( -1, 1 ), pos.z ), *this );
@@ -5583,6 +5594,9 @@ void item::reset_cable( player* p )
int max_charges = type->maximum_charges();

set_var( "state", "attach_first" );
erase_var("source_x");
erase_var("source_y");
erase_var("source_z");
active = false;
charges = max_charges;

@@ -5968,11 +5982,6 @@ bool item_category::operator!=( const item_category &rhs ) const
return !( *this == rhs );
}

bool item::is_filthy() const
{
return has_flag( "FILTHY" ) && ( get_world_option<bool>( "FILTHY_MORALE" ) || g->u.has_trait( "SQUEAMISH" ) );
}

bool item::on_drop( const tripoint &pos )
{
return type->drop_action && type->drop_action.call( &g->u, this, false, pos );
@@ -46,6 +46,7 @@ using fault_id = string_id<fault>;
struct quality;
using quality_id = string_id<quality>;
struct fire_data;
struct damage_instance;

enum damage_type : int;

@@ -441,6 +442,11 @@ class item : public JsonSerializer, public JsonDeserializer, public visitable<it
/** Damage of given type caused when this item is used as melee weapon */
int damage_melee( damage_type dt ) const;

/** All damage types this item deals when used in melee (no skill modifiers etc. applied). */
damage_instance base_damage_melee() const;
/** All damage types this item deals when thrown (no skill modifiers etc. applied). */
damage_instance base_damage_thrown() const;

/**
* Whether the character needs both hands to wield this item.
*/
@@ -803,9 +809,6 @@ class item : public JsonSerializer, public JsonDeserializer, public visitable<it
* for other players. The player is identified by its id.
*/
void mark_as_used_by_player(const player &p);
/** Marks the item as filthy, so characters with squeamish trait can't wear it.
*/
bool is_filthy() const;
/**
* This is called once each turn. It's usually only useful for active items,
* but can be called for inactive items without problems.
@@ -1641,6 +1641,15 @@ void Item_factory::load_basic_info( JsonObject &jo, itype &def, const std::strin
assign( jo, "magazine_well", def.magazine_well );
assign( jo, "explode_in_fire", def.explode_in_fire );

if( jo.has_member( "thrown_damage" ) ) {
JsonArray jarr = jo.get_array( "thrown_damage" );
def.thrown_damage = load_damage_instance( jarr );
} else {
// @todo Move to finalization
def.thrown_damage.clear();
def.thrown_damage.add_damage( DT_BASH, def.melee[DT_BASH] + def.weight / 1000.0f );
}

if( jo.has_member( "damage_states" ) ) {
auto arr = jo.get_array( "damage_states" );
def.damage_min = arr.get_int( 0 );
@@ -674,6 +674,8 @@ struct itype {

/** Damage output in melee for zero or more damage types */
std::array<int, NUM_DT> melee;
/** Base damage output when thrown */
damage_instance thrown_damage;

int m_to_hit = 0; // To-hit bonus for melee combat; -5 to 5 is reasonable

Large diffs are not rendered by default.

@@ -52,6 +52,21 @@ const efftype_id effect_sleep( "sleep" );
const efftype_id effect_stunned( "stunned" );
const efftype_id effect_asthma( "asthma" );

static const trait_id trait_CENOBITE( "CENOBITE" );
static const trait_id trait_LIGHTWEIGHT( "LIGHTWEIGHT" );
static const trait_id trait_MASOCHIST( "MASOCHIST" );
static const trait_id trait_MASOCHIST_MED( "MASOCHIST_MED" );
static const trait_id trait_NOPAIN( "NOPAIN" );
static const trait_id trait_PACIFIST( "PACIFIST" );
static const trait_id trait_PRED1( "PRED1" );
static const trait_id trait_PRED2( "PRED2" );
static const trait_id trait_PRED3( "PRED3" );
static const trait_id trait_PRED4( "PRED4" );
static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" );
static const trait_id trait_SAPIOVORE( "SAPIOVORE" );
static const trait_id trait_SELFAWARE( "SELFAWARE" );
static const trait_id trait_TOLERANCE( "TOLERANCE" );

iuse_transform::~iuse_transform()
{
}
@@ -538,9 +553,9 @@ long consume_drug_iuse::use(player *p, item *it, bool, const tripoint& ) const
// Apply the various effects.
for( auto eff : effects ) {
int dur = eff.duration;
if (p->has_trait("TOLERANCE")) {
if (p->has_trait( trait_TOLERANCE )) {
dur *= .8;
} else if (p->has_trait("LIGHTWEIGHT")) {
} else if (p->has_trait( trait_LIGHTWEIGHT )) {
dur *= 1.2;
}
p->add_effect( eff.id, dur, eff.bp, eff.permanent );
@@ -1174,7 +1189,6 @@ bool salvage_actor::try_to_cut_up( player *p, item *it ) const
// *cut gets cut
int salvage_actor::cut_up(player *p, item *it, item *cut) const
{
bool filthy = cut->is_filthy();
int pos = p->get_item_position(cut);
// total number of raw components == total volume of item.
// This can go awry if there is a volume / recipe mismatch.
@@ -1249,9 +1263,6 @@ int salvage_actor::cut_up(player *p, item *it, item *cut) const
if (amount > 0) {
add_msg( m_good, ngettext("Salvaged %1$i %2$s.", "Salvaged %1$i %2$s.", amount),
amount, result.display_name( amount ).c_str() );
if( filthy ) {
result.item_tags.insert( "FILTHY" );
}
if( pos != INT_MIN ) {
p->i_add_or_drop(result, amount);
} else {
@@ -1434,7 +1445,7 @@ bool cauterize_actor::cauterize_effect( player *p, item *it, bool force )
hp_part hpart = dummy.use_healing_item( *p, *p, *it, force );
if( hpart != num_hp_parts ) {
p->add_msg_if_player(m_neutral, _("You cauterize yourself."));
if (!(p->has_trait("NOPAIN"))) {
if (!(p->has_trait( trait_NOPAIN ))) {
p->mod_pain(15);
p->add_msg_if_player(m_bad, _("It hurts like hell!"));
} else {
@@ -1472,7 +1483,7 @@ long cauterize_actor::use( player *p, item *it, bool t, const tripoint& ) const
} else if( has_disease ) {
did_cauterize = cauterize_effect( p, it, !has_disease );
} else {
if( ( p->has_trait("MASOCHIST") || p->has_trait("MASOCHIST_MED") || p->has_trait("CENOBITE") ) &&
if( ( p->has_trait( trait_MASOCHIST ) || p->has_trait( trait_MASOCHIST_MED ) || p->has_trait( trait_CENOBITE ) ) &&
query_yn(_("Cauterize yourself for fun?"))) {
did_cauterize = cauterize_effect( p, it, true );
} else {
@@ -1503,7 +1514,7 @@ bool cauterize_actor::can_use( const player *p, const item *it, bool, const trip
return false;
} else if( p->has_effect( effect_bite ) || p->has_effect( effect_bleed ) ) {
return true;
} else if( p->has_trait("MASOCHIST") || p->has_trait("MASOCHIST_MED") || p->has_trait("CENOBITE") ) {
} else if( p->has_trait( trait_MASOCHIST ) || p->has_trait( trait_MASOCHIST_MED ) || p->has_trait( trait_CENOBITE ) ) {
return true;
}

@@ -1545,11 +1556,11 @@ long enzlave_actor::use( player *p, item *it, bool t, const tripoint& ) const
}

int tolerance_level = 9;
if( p->has_trait("PSYCHOPATH") || p->has_trait("SAPIOVORE") ) {
if( p->has_trait( trait_PSYCHOPATH ) || p->has_trait( trait_SAPIOVORE ) ) {
tolerance_level = 0;
} else if( p->has_trait("PRED4") ) {
} else if( p->has_trait( trait_PRED4 ) ) {
tolerance_level = 5;
} else if( p->has_trait("PRED3") ) {
} else if( p->has_trait( trait_PRED3 ) ) {
tolerance_level = 7;
}

@@ -1590,12 +1601,12 @@ long enzlave_actor::use( player *p, item *it, bool t, const tripoint& ) const
int duration = 300 * (5.0 / (float)p->get_skill_level( skill_survival ));
int decayDelay = 30 * (5.0 / (float)p->get_skill_level( skill_survival ));

if (p->has_trait("PACIFIST")) {
if (p->has_trait( trait_PACIFIST )) {
moraleMalus *= 5;
maxMalus *= 3;
} else if (p->has_trait("PRED1")) {
} else if (p->has_trait( trait_PRED1 )) {
moraleMalus /= 4;
} else if (p->has_trait("PRED2")) {
} else if (p->has_trait( trait_PRED2 )) {
moraleMalus /= 5;
}

@@ -2703,11 +2714,6 @@ long heal_actor::use( player *p, item *it, bool, const tripoint &pos ) const
p->add_msg_if_player( m_info, _("You can't do that while underwater.") );
return 0;
}

if( get_option<bool>( "FILTHY_WOUNDS" ) && it->is_filthy() ) {
p->add_msg_if_player( m_info, _( "You can't use filthy items for healing." ) );
return 0;
}

player &patient = get_patient( *p, pos );
const hp_part hpp = use_healing_item( *p, patient, *it, false );
@@ -2864,8 +2870,8 @@ hp_part pick_part_to_heal(
const bool bite = bite_chance > 0.0f;
const bool infect = infect_chance > 0.0f;
const bool precise = &healer == &patient ?
patient.has_trait( "SELFAWARE" ) :
///\EFFECT_PER slightly increases precision when using first aid on someone else
patient.has_trait( trait_SELFAWARE ) :
/** @EFFECT_PER slightly increases precision when using first aid on someone else */

///\EFFECT_FIRSTAID increases precision when using first aid on someone else
(healer.get_skill_level( skill_firstaid ) * 4 + healer.per_cur >= 20);
@@ -365,7 +365,7 @@ class JsonIn
start_object();
m.clear();
while (!end_object()) {
std::string name = get_member_name();
typename T::key_type name( get_member_name() );
typename T::mapped_type element;
if (read(element)) {
m[std::move(name)] = std::move(element);
@@ -650,6 +650,7 @@ bool main_menu::new_character_tab()
if( templates.empty() ) {
mvwprintz( w_open, iMenuOffsetY - 4, iMenuOffsetX + 20 + extra_w / 2,
c_red, _( "No templates found!" ) );
sfx::play_variant_sound( "menu_error", "default", 100 );
} else {
mvwprintz( w_open, iMenuOffsetY - 2, iMenuOffsetX + 20 + extra_w / 2,
c_white, _( "Press 'd' to delete a preset." ) );
@@ -735,6 +736,7 @@ bool main_menu::load_character_tab()
if( world_generator->all_worldnames.empty() ) {
mvwprintz( w_open, iMenuOffsetY - 2, 15 + iMenuOffsetX + extra_w / 2,
c_red, _( "No Worlds found!" ) );
sfx::play_variant_sound( "menu_error", "default", 100 );
} else {
for( int i = 0; i < ( int )world_generator->all_worldnames.size(); ++i ) {
int line = iMenuOffsetY - 2 - i;
@@ -789,24 +791,26 @@ bool main_menu::load_character_tab()

if( MAP_SHARING::isSharing() ) {
auto new_end = std::remove_if( savegames.begin(), savegames.end(),
[]( const std::string & str ) {
return base64_decode( str ) != MAP_SHARING::getUsername();
[]( const save_t &str ) {
return str.player_name() != MAP_SHARING::getUsername();
} );
savegames.erase( new_end, savegames.end() );
}
if( ( wn != "TUTORIAL" && wn != "DEFENSE" ) && world_generator->world_need_lua_build( wn ) ) {
savegames.clear();
mvwprintz( w_open, iMenuOffsetY - 2, 15 + iMenuOffsetX + extra_w / 2,
c_red, _( "This world requires the game to be compiled with Lua." ) );
sfx::play_variant_sound( "menu_error", "default", 100 );
} else if( savegames.empty() ) {
mvwprintz( w_open, iMenuOffsetY - 2, 19 + 19 + iMenuOffsetX + extra_w / 2,
c_red, _( "No save games found!" ) );
sfx::play_variant_sound( "menu_error", "default", 100 );
} else {
int line = iMenuOffsetY - 2;
for( const std::string &savename : savegames ) {
for( const auto &savename : savegames ) {
const bool selected = sel3 + line == iMenuOffsetY - 2;
mvwprintz( w_open, line--, 19 + 19 + iMenuOffsetX + extra_w / 2,
selected ? h_white : c_white, base64_decode( savename ).c_str() );
selected ? h_white : c_white, savename.player_name().c_str() );
}
}
wrefresh( w_open );
@@ -9,6 +9,7 @@ class player;

#include "cursesdef.h"
#include "input.h"
#include "worldfactory.h"

class main_menu
{
@@ -60,7 +61,7 @@ class main_menu
int iMenuOffsetY;
std::vector<std::string> templates;
int extra_w;
std::vector<std::string> savegames;
std::vector<save_t> savegames;

/**
* Prints a horizontal list of options
@@ -38,6 +38,7 @@
#include "cata_utility.h"
#include "harvest.h"
#include "input.h"
#include "computer.h"

#include <cmath>
#include <stdlib.h>
@@ -1563,6 +1564,13 @@ void map::furn_set( const tripoint &p, const furn_id new_furniture )
const furn_t &old_t = old_id.obj();
const furn_t &new_t = new_furniture.obj();

// If player has grabbed this furniture and it's no longer grabbable, release the grab.
if( g->u.grab_type == OBJECT_FURNITURE && g->u.grab_point == p && new_t.move_str_req < 0 ) {
add_msg( _( "The %s you were grabbing is destroyed!" ), old_t.name.c_str() );
g->u.grab_type = OBJECT_NONE;
g->u.grab_point = tripoint_zero;
}

if( old_t.transparent != new_t.transparent ) {
set_transparency_cache_dirty( p.z );
}
@@ -3693,12 +3701,12 @@ void map::crush( const tripoint &p )
void map::shoot( const tripoint &p, projectile &proj, const bool hit_items )
{
// TODO: Make bashing count fully, but other types much less
const int initial_damage = proj.impact.total_damage();
const float initial_damage = proj.impact.total_damage();
if( initial_damage < 0 ) {
return;
}

int dam = initial_damage;
float dam = initial_damage;
const auto &ammo_effects = proj.proj_effects;

if( has_flag("ALARMED", p) && !g->event_queued(EVENT_WANTED) ) {
@@ -3872,7 +3880,7 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items )
bash( p, dam, false );
dam = 0; // TODO: Preserve some residual damage when it makes sense.
} else {
dam -= (rng(0, 1) * rng(0, 1) * rng(0, 1));
dam -= proj.momentum_loss;
}

if (ammo_effects.count("TRAIL") && !one_in(4)) {
@@ -3903,10 +3911,7 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items )
add_field(p, fd_laser, 2, 0 );
}

// Set damage to 0 if it's less
if( dam < 0 ) {
dam = 0;
}
dam = std::max( 0.0f, dam );

// Check fields?
const field_entry *fieldhit = get_field( p, fd_web );
@@ -4597,6 +4602,23 @@ item map::water_from( const tripoint &p )
return item();
}

void map::make_active( item_location &loc )
{
item *target = loc.get_item();

// Trust but verify, don't let stinking callers set items active when they shouldn't be.
if( !target->needs_processing() ) {
return;
}
int lx, ly;
submap *const current_submap = get_submap_at( loc.position(), lx, ly );
auto &item_stack = current_submap->itm[lx][ly];
auto iter = std::find_if( item_stack.begin(), item_stack.end(),
[&target]( const item &i ) { return &i == target; } );

current_submap->active_items.add( iter, point(lx, ly) );
}

// Check if it's in a fridge and is food, set the fridge
// date to current time, and also check contents.
static void apply_in_fridge(item &it)
@@ -5087,9 +5109,10 @@ std::list<item> map::use_charges(const tripoint &origin, const int range,
if (craftpart >= 0) { // we have a craftrig, now to see what to drain
itype_id ftype = "null";

if (type == "press") {
ftype = "battery";
} else if (type == "vac_sealer") {
// if (type == "press") {
// ftype = "battery";
if (type == "vac_sealer") {
// } else if (type == "vac_sealer") {
ftype = "battery";
} else if (type == "dehydrator") {
ftype = "battery";
@@ -5593,13 +5616,7 @@ computer* map::computer_at( const tripoint &p )
return nullptr;
}

submap * const current_submap = get_submap_at( p );

if( current_submap->comp.name.empty() ) {
return nullptr;
}

return &(current_submap->comp);
return get_submap_at( p )->comp.get();
}

bool map::allow_camp( const tripoint &p, const int radius)
@@ -845,6 +845,12 @@ void add_corpse( const tripoint &p );
item &spawn_an_item( const tripoint &p, item new_item,
const long charges, const int damlevel);

/**
* Update an item's active status, for example when adding
* hot or perishable liquid to a container.
*/
void make_active( item_location &loc );

/**
* @name Consume items on the map
*
@@ -14,6 +14,7 @@
#include "trap.h"
#include "vehicle.h"
#include "submap.h"
#include "computer.h"

#include <sstream>

@@ -371,8 +372,8 @@ void mapbuffer::save_quad( const std::string &dirname, const std::string &filena
jsout.end_array();

// Output the computer
if (sm->comp.name != "") {
jsout.member( "computers", sm->comp.save_data() );
if( sm->comp != nullptr ) {
jsout.member( "computers", sm->comp->save_data() );
}

// Output base camp if any
@@ -603,7 +604,9 @@ void mapbuffer::deserialize( JsonIn &jsin )
}
} else if( submap_member_name == "computers" ) {
std::string computer_data = jsin.get_string();
sm->comp.load_data( computer_data );
std::unique_ptr<computer> new_comp( new computer( "BUGGED_COMPUTER", -100 ) );
new_comp->load_data( computer_data );
sm->comp.reset( new_comp.release() );
} else if( submap_member_name == "camp" ) {
std::string camp_data = jsin.get_string();
sm->camp.load_data( camp_data );
@@ -27,6 +27,7 @@
#include "mtype.h"
#include "itype.h"
#include "item_factory.h"
#include "computer.h"

#include <algorithm>
#include <cassert>
@@ -82,7 +83,7 @@ const mtype_id mon_yugg( "mon_yugg" );
const mtype_id mon_zombie( "mon_zombie" );
const mtype_id mon_zombie_bio_op( "mon_zombie_bio_op" );
const mtype_id mon_zombie_brute( "mon_zombie_brute" );
const mtype_id mon_zombie_child( "mon_zombie_child" );
const mtype_id mon_zombie_child( "mon_zombie" );
const mtype_id mon_zombie_cop( "mon_zombie_cop" );
const mtype_id mon_zombie_dog( "mon_zombie_dog" );
const mtype_id mon_zombie_electric( "mon_zombie_electric" );
@@ -3443,19 +3444,19 @@ ff.......|....|WWWWWWWW|\n\
spawn_item(SEEX - 1, SEEY , "laser_rifle", dice (1, 0));
spawn_item(SEEX , SEEY , "ftk93");
spawn_item(SEEX - 1, SEEY , "recipe_atomic_battery");
spawn_item(SEEX , SEEY -1, "solar_panel_v3"); //quantum solar panel, 6 panels in one!
spawn_item(SEEX , SEEY -1, "solar_panel_v2"); //quantum solar panel, 6 panels in one!
} else if (loot_variant > 67 && loot_variant < 89) {
spawn_item(SEEX - 1, SEEY - 1, "mininuke", dice(3, 6));
spawn_item(SEEX , SEEY - 1, "mininuke", dice(3, 6));
spawn_item(SEEX - 1, SEEY , "mininuke", dice(3, 6));
spawn_item(SEEX , SEEY , "mininuke", dice(3, 6));
spawn_item(SEEX , SEEY , "recipe_atomic_battery");
spawn_item(SEEX , SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one!
spawn_item(SEEX , SEEY , "solar_panel_v2"); //quantum solar panel, 6 panels in one!
} else { // loot_variant between 90 and 96.
spawn_item(SEEX - 1, SEEY - 1, "rm13_armor");
spawn_item(SEEX - 1, SEEY - 1, "ftk93");
spawn_item(SEEX , SEEY - 1, "plut_cell");
spawn_item(SEEX - 1, SEEY , "plut_cell");
spawn_item(SEEX , SEEY , "recipe_caseless");
spawn_item(SEEX , SEEY , "laser_rifle", dice (1, 0));
}
} else { // 4% of the lab ends will be this weapons testing end.
madd_trap( this, SEEX - 4, SEEY - 3, tr_dissector);
@@ -3477,7 +3478,7 @@ ff.......|....|WWWWWWWW|\n\
line(this, t_reinforced_glass, SEEX + 2, SEEY - 2, SEEX + 2, SEEY + 1);
place_items("ammo_rare", 96, SEEX - 2, SEEY - 1, SEEX + 1, SEEY - 1, false, 0);
place_items("guns_rare", 96, SEEX - 2, SEEY, SEEX + 1, SEEY, false, 0);
spawn_item(SEEX + 1, SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one!
spawn_item(SEEX + 1, SEEY , "solar_panel_v2"); //quantum solar panel, 6 panels in one!
}
break;

@@ -8567,8 +8568,6 @@ std::vector<item *> map::place_items( items_location loc, int chance, int x1, in
{
std::vector<item *> res;

const float spawn_rate = get_world_option<float>( "ITEM_SPAWNRATE" );

if (chance > 100 || chance <= 0) {
debugmsg("map::place_items() called with an invalid chance (%d)", chance);
return res;
@@ -8581,35 +8580,31 @@ std::vector<item *> map::place_items( items_location loc, int chance, int x1, in
return res;
}

int px, py;
while (chance == 100 || rng(0, 99) < chance) {
float lets_spawn = spawn_rate;
while( rng_float( 0.0, 1.0 ) <= lets_spawn ) {
lets_spawn -= 1.0;

// Might contain one item or several that belong together like guns & their ammo
int tries = 0;
auto is_valid_terrain = [this,ongrass](int x,int y){
auto &terrain = ter( x, y ).obj();
return terrain.movecost == 0 &&
!terrain.has_flag("PLACE_ITEM") &&
!ongrass &&
!terrain.has_flag("FLAT");
};
do {
px = rng(x1, x2);
py = rng(y1, y2);
tries++;
} while ( is_valid_terrain(px,py) && tries < 20 );
if (tries < 20) {
auto put = put_items_from_loc( loc, tripoint( px, py, abs_sub.z ), turn );
res.insert( res.end(), put.begin(), put.end() );
}
}
if (chance == 100) {
break;
const float spawn_rate = get_world_option<float>( "ITEM_SPAWNRATE" );
int spawn_count = roll_remainder( chance * spawn_rate / 100.0f );
for( int i = 0; i < spawn_count; i++ ) {
// Might contain one item or several that belong together like guns & their ammo
int tries = 0;
auto is_valid_terrain = [this,ongrass](int x,int y){
auto &terrain = ter( x, y ).obj();
return terrain.movecost == 0 &&
!terrain.has_flag("PLACE_ITEM") &&
!ongrass &&
!terrain.has_flag("FLAT");
};

int px, py;
do {
px = rng(x1, x2);
py = rng(y1, y2);
tries++;
} while ( is_valid_terrain(px,py) && tries < 20 );
if (tries < 20) {
auto put = put_items_from_loc( loc, tripoint( px, py, abs_sub.z ), turn );
res.insert( res.end(), put.begin(), put.end() );
}
}

for( auto e : res ) {
if( e->is_tool() || e->is_gun() || e->is_magazine() ) {
if( rng( 0, 99 ) < magazine && !e->magazine_integral() && !e->magazine_current() ) {
@@ -8847,8 +8842,8 @@ computer *map::add_computer( const tripoint &p, std::string name, int security )
{
ter_set( p, t_console ); // TODO: Turn this off?
submap *place_on_submap = get_submap_at( p );
place_on_submap->comp = computer(name, security);
return &(place_on_submap->comp);
place_on_submap->comp.reset( new computer( name, security ) );
return place_on_submap->comp.get();
}

/**
@@ -8924,7 +8919,7 @@ void map::rotate(int turns)

std::vector<spawn_point> sprot[MAPSIZE * MAPSIZE];
std::vector<vehicle*> vehrot[MAPSIZE * MAPSIZE];
computer tmpcomp[MAPSIZE * MAPSIZE];
copyable_unique_ptr<computer> tmpcomp[MAPSIZE * MAPSIZE];
int field_count[MAPSIZE * MAPSIZE];
int temperature[MAPSIZE * MAPSIZE];

@@ -2738,8 +2738,8 @@ void mapgen_church_new_england(map *m, oter_id terrain_type, mapgendata dat, int
f_null, f_null, f_bench, f_table, f_null, f_null, f_null, f_null,
f_toilet, f_sink, f_fridge, f_bookcase, f_chair, f_counter, f_desk, f_locker, f_null)
);
m->spawn_item(9, 6, "brazier");
m->spawn_item(14, 6, "brazier");
madd_trap(m, 9, 6, tr_brazier);
madd_trap(m, 14, 6, tr_brazier);
m->place_items("church", 40, 5, 5, 8, 16, false, 0);
m->place_items("church", 40, 5, 5, 8, 16, false, 0);
m->place_items("church", 85, 12, 2, 14, 2, false, 0);
@@ -2790,8 +2790,8 @@ ssssssssssssssssssssssss\n",
f_null, f_null, f_null, f_bench, f_table, f_null, f_null, f_toilet,
f_sink, f_chair, f_counter, f_locker, f_null)
);
m->spawn_item(8, 4, "brazier");
m->spawn_item(15, 4, "brazier");
madd_trap(m, 8, 4, tr_brazier);
madd_trap(m, 15, 4, tr_brazier);
m->place_items("church", 70, 6, 7, 17, 16, false, 0);
m->place_items("church", 70, 6, 7, 17, 16, false, 0);
m->place_items("church", 60, 6, 7, 17, 16, false, 0);