diff --git a/data/json/items/armor/shields.json b/data/json/items/armor/shields.json index 0fe5d14f71b5..8b7e73143892 100644 --- a/data/json/items/armor/shields.json +++ b/data/json/items/armor/shields.json @@ -3,7 +3,7 @@ "id": "shield_wooden", "type": "ARMOR", "name": { "str": "wooden shield" }, - "description": "A crude wooden shield, lacking any metal or leather reinforcement. Tolerable weight but not very tough.", + "description": "A crude wooden shield lacking any metal or leather reinforcement. Tolerable weight but not very tough.", "weight": "3 kg", "volume": "3 L", "price": "50 USD", @@ -24,7 +24,7 @@ "id": "shield_wooden_large", "type": "ARMOR", "name": { "str": "large wooden shield" }, - "description": "An crude wooden tower shield, lacking any metal or leather reinforcement. Bulky, but offers a decent amount of protection.", + "description": "A crude wooden tower shield lacking any metal or leather reinforcement. Bulky, but offers a decent amount of protection.", "weight": "5 kg", "volume": "5 L", "price": "60 USD", @@ -45,7 +45,7 @@ "id": "shield_riot", "type": "ARMOR", "name": { "str": "riot shield" }, - "description": "A large but fairly light plastic shield, designed for riot police officers. Not too encumbering, but designed for fending off thrown rocks rather than bullets.", + "description": "A large but fairly light plastic shield designed for riot police officers. Not too encumbering, but designed for fending off thrown rocks rather than bullets.", "weight": "2500 g", "volume": "5 L", "price": "200 USD", diff --git a/src/catalua_bindings.cpp b/src/catalua_bindings.cpp index 63bbaebd68ce..a56f8c19eaaa 100644 --- a/src/catalua_bindings.cpp +++ b/src/catalua_bindings.cpp @@ -1011,6 +1011,7 @@ void cata::reg_all_bindings( sol::state &lua ) reg_colors( lua ); reg_enums( lua ); reg_game_ids( lua ); + mod_mutation_branch( lua ); reg_coords_library( lua ); reg_constants( lua ); reg_hooks_examples( lua ); diff --git a/src/catalua_bindings.h b/src/catalua_bindings.h index 8002e55ea943..6b910d8d70f6 100644 --- a/src/catalua_bindings.h +++ b/src/catalua_bindings.h @@ -32,6 +32,7 @@ void reg_item( sol::state &lua ); void reg_locale_api( sol::state &lua ); void reg_map( sol::state &lua ); void reg_monster( sol::state &lua ); +void mod_mutation_branch( sol::state &lua ); void reg_npc( sol::state &lua ); void reg_player( sol::state &lua ); void reg_point_tripoint( sol::state &lua ); diff --git a/src/catalua_bindings_creature.cpp b/src/catalua_bindings_creature.cpp index e94433122b21..dbadd231487f 100644 --- a/src/catalua_bindings_creature.cpp +++ b/src/catalua_bindings_creature.cpp @@ -412,6 +412,18 @@ void cata::detail::reg_character( sol::state &lua ) SET_FX_T( has_watch, bool() const ); + // These are named with 'BTU' (body temperature units) because other + // body temperature measurements might/can/should be added later. + DOC( "Gets the current temperature of a specific body part (in Body Temperature Units)." ); + SET_FX_N_T( get_part_temp_cur, "get_part_temp_btu", int( const bodypart_id & id ) const ); + DOC( "Sets a specific body part to a given temperature (in Body Temperature Units)." ); + SET_FX_N_T( set_part_temp_cur, "set_part_temp_btu", void( const bodypart_id & id, int temp ) ); + DOC( "Gets all bodyparts and their associated temperatures (in Body Temperature Units)." ); + // May want to remove the 'resolve' call, but it clarifies the type... + luna::set_fx( ut, "get_temp_btu", sol::resolve< std::map() >( &UT_CLASS::get_temp_cur ) ); + DOC( "Sets ALL body parts on a creature to the given temperature (in Body Temperature Units)." ); + SET_FX_N_T( set_temp_cur, "set_temp_btu", void( int temp ) ); + SET_FX_T( blood_loss, int( const bodypart_id & bp ) const ); SET_FX_N_T( encumb, "get_part_encumbrance", int( const bodypart_str_id & bp ) const ); @@ -526,6 +538,12 @@ void cata::detail::reg_character( sol::state &lua ) SET_FX_T( mutate_towards, bool( const trait_id & ) ); + luna::set_fx( ut, "mutate_towards", sol::overload( + sol::resolve, int )>( &UT_CLASS::mutate_towards ), + sol::resolve( &UT_CLASS::mutate_towards ) + ) ); + + SET_FX_T( remove_mutation, void( const trait_id &, bool ) ); SET_FX_T( has_child_flag, bool( const trait_id & flag ) const ); @@ -714,8 +732,10 @@ void cata::detail::reg_character( sol::state &lua ) SET_FX_T( rooted, void() ); - SET_FX_T( fall_asleep, void() ); - SET_FX_T( fall_asleep, void( const time_duration & duration ) ); + luna::set_fx( ut, "fall_asleep", sol::overload( + sol::resolve( &UT_CLASS::fall_asleep ), + sol::resolve( &UT_CLASS::fall_asleep ) + ) ); SET_FX_T( get_hostile_creatures, std::vector( int ) const ); diff --git a/src/catalua_bindings_mutation.cpp b/src/catalua_bindings_mutation.cpp new file mode 100644 index 000000000000..64c031844f76 --- /dev/null +++ b/src/catalua_bindings_mutation.cpp @@ -0,0 +1,177 @@ +#ifdef LUA +#include "catalua_bindings.h" + +#include "catalua.h" +// Thx Almantuxas +#include "catalua_bindings_utils.h" +#include "catalua_impl.h" +#include "catalua_log.h" +#include "catalua_luna.h" +#include "catalua_luna_doc.h" + +#include "mutation.h" + +void cata::detail::mod_mutation_branch( sol::state &lua ) +{ +#define UT_CLASS mutation_branch + { + /* NOTE: These changes are applied to the "MutationBranchRaw" Lua obj. + * Because mutation_branch was bound as an ID, the actual object is + * shoved into a 'Raw' binding. + * The following code makes that useful, while also stopping the + * related object from being messed with. + */ + sol::usertype ut = + luna::new_usertype( + lua, + luna::no_bases, + luna::no_constructor + ); + + // Lets us grab the MutationBranchId from a MutationBranchRaw object. + SET_MEMB_RO( id ); + + /* === General Mutation Statistics === */ + DOC( "Whether this mutation is available through generic mutagen." ); + SET_MEMB_RO( valid ); + DOC( "Whether this mutation is possible to remove through Purifier. False for 'special' mutations." ); + SET_MEMB_RO( purifiable ); + DOC( "Whether this is a Threshold mutation, and thus especially difficult to mutate. One per character." ); + SET_MEMB_RO( threshold ); + DOC( "Whether this trait is ONLY gained through professional training/experience (and/or quests)." ); + SET_MEMB_RO( profession ); + DOC( "Whether or not this mutation is limited to debug use." ); + SET_MEMB_RO( debug ); + DOC( "Whether or not this mutation shows up in the status (`@`) menu." ); + SET_MEMB_RO( player_display ); + DOC( "Whether this mutation has positive /and/ negative effects." ); + SET_MEMB_RO( mixed_effect ); + DOC( "Whether this trait can normally be taken during character generation." ); + SET_MEMB_N_RO( startingtrait, "starting_trait" ); + DOC( "Whether this mutation can be activated at will." ); + SET_MEMB_RO( activated ); + DOC( "Whether a mutation activates when granted." ); + SET_MEMB_RO( starts_active ); + DOC( "Mutation allows soft gear to be worn over otherwise-restricted parts." ); + SET_MEMB_RO( allow_soft_gear ); + DOC( "Mutation causes fatigue when used." ); + SET_MEMB_RO( fatigue ); + DOC( "Mutation deducts calories when used." ); + SET_MEMB_RO( hunger ); + DOC( "Mutation dehydrates when used." ); + SET_MEMB_RO( thirst ); + DOC( "Point cost in character creation(?)." ); + SET_MEMB_RO( points ); + DOC( "How visible the mutation is to others." ); + SET_MEMB_RO( visibility ); + DOC( "How physically unappealing the mutation is. Can be negative." ); + SET_MEMB_RO( ugliness ); + + SET_MEMB_RO( cost ); + DOC( "Costs are incurred every 'cooldown' turns." ); + SET_MEMB_RO( cooldown ); + + /* === Modifiers === */ + SET_MEMB_N_RO( bodytemp_min, "bodytemp_min_btu" ); + SET_MEMB_N_RO( bodytemp_max, "bodytemp_max_btu" ); + SET_MEMB_N_RO( bodytemp_sleep, "bodytemp_sleep_btu" ); + + DOC( "Pain recovery per turn from mutation." ); + SET_MEMB_RO( pain_recovery ); + DOC( "Healing per turn from mutation." ); + SET_MEMB_RO( healing_awake ); + DOC( "Healing per turn from mutation, while asleep." ); + SET_MEMB_RO( healing_resting ); + DOC( "Multiplier applied to broken limb regeneration. Normally 0.25; clamped to 0.25..1.0." ); + SET_MEMB_RO( mending_modifier ); + DOC( "Bonus HP multiplier. 1.0 doubles HP; -0.5 halves it." ); + SET_MEMB_RO( hp_modifier ); + DOC( "Secondary HP multiplier; stacks with the other one. 1.0 doubles HP; -0.5 halves it." ); + SET_MEMB_RO( hp_modifier_secondary ); + DOC( "Flat adjustment to HP." ); + SET_MEMB_RO( hp_adjustment ); + DOC( "Adjustment to Strength that doesn't affect HP." ); + SET_MEMB_RO( str_modifier ); + + SET_MEMB_RO( dodge_modifier ); + SET_MEMB_RO( speed_modifier ); + SET_MEMB_RO( movecost_modifier ); + SET_MEMB_RO( movecost_flatground_modifier ); + SET_MEMB_RO( movecost_obstacle_modifier ); + SET_MEMB_RO( attackcost_modifier ); + SET_MEMB_RO( falling_damage_multiplier ); + SET_MEMB_RO( max_stamina_modifier ); + SET_MEMB_RO( weight_capacity_modifier ); + SET_MEMB_RO( hearing_modifier ); + SET_MEMB_RO( movecost_swim_modifier ); + SET_MEMB_RO( noise_modifier ); + SET_MEMB_RO( scent_modifier ); + SET_MEMB_RO( bleed_resist ); + + DOC( "How quickly health (not HP) trends toward healthy_mod." ); + SET_MEMB_RO( healthy_rate ); + SET_MEMB_RO( stealth_modifier ); + SET_MEMB_RO( night_vision_range ); + SET_MEMB_RO( temperature_speed_modifier ); + SET_MEMB_RO( metabolism_modifier ); + SET_MEMB_RO( thirst_modifier ); + SET_MEMB_RO( fatigue_modifier ); + SET_MEMB_RO( fatigue_regen_modifier ); + SET_MEMB_RO( stamina_regen_modifier ); + + SET_MEMB_RO( overmap_sight ); + SET_MEMB_RO( overmap_multiplier ); + SET_MEMB_RO( reading_speed_multiplier ); + SET_MEMB_RO( skill_rust_multiplier ); + + SET_FX_T( name, std::string() const ); + SET_FX_T( desc, std::string() const ); + + DOC( "Returns a (long) list of every mutation in the game." ); + SET_FX_T( get_all, const std::vector &() ); + + // The string conversion function references this object's str_id. + luna::set_fx( ut, sol::meta_function::to_string, + []( const UT_CLASS & id ) -> std::string { + return string_format( "%s[%s]", luna::detail::luna_traits::name, id.id.c_str() ); + } ); + + // Copy the return values so they can't be modified in-place. + DOC( "Lists the primary mutation(s) needed to gain this mutation." ); + luna::set_fx( ut, "prerequisites", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.prereqs; return rv; + } ); + DOC( "Lists the secondary mutation(s) needed to gain this mutation." ); + luna::set_fx( ut, "other_prerequisites", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.prereqs2; return rv; + } ); + DOC( "Lists the threshold mutation(s) required to gain this mutation." ); + luna::set_fx( ut, "thresh_requirements", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.threshreq; return rv; + } ); + DOC( "Lists the type(s) of this mutation. Mutations of a given type are mutually exclusive." ); + luna::set_fx( ut, "mutation_types", []( const UT_CLASS & mut ) -> std::set { + std::set rv = mut.types; return rv; + } ); + DOC( "Lists conflicting mutations." ); + luna::set_fx( ut, "conflicts_with", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.cancels; return rv; + } ); + DOC( "Lists mutations that replace (e.g. evolve from) this one." ); + luna::set_fx( ut, "replaced_by", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.replacements; return rv; + } ); + luna::set_fx( ut, "addition_mutations", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.additions; return rv; + } ); + DOC( "Lists the categories this mutation belongs to." ); + luna::set_fx( ut, "categories", []( const UT_CLASS & mut ) -> std::vector { + std::vector rv = mut.category; return rv; + } ); + /* Would bind .flags to .trait_flags(), but json_trait_flag does not + * seem to have definitions for comparison to itself. + */ + } +#undef UT_CLASS // #define UT_CLASS mutation_branch +} +#endif // #ifdef LUA diff --git a/src/catalua_bindings_utils.h b/src/catalua_bindings_utils.h index 071df434d671..df40dd3b4123 100644 --- a/src/catalua_bindings_utils.h +++ b/src/catalua_bindings_utils.h @@ -21,6 +21,10 @@ void reg_serde_functions( sol::usertype &ut ) #define SET_MEMB(prop_name) luna::set( ut, #prop_name, &UT_CLASS::prop_name ) // SET MEMBer with Name //#define SET_MEMB_N(prop_name, lua_name_str) luna::set( ut, lua_name_str, &UT_CLASS::prop_name ) +// SET MEMBer, Read-Only +#define SET_MEMB_RO(prop_name) luna::set( ut, #prop_name, sol::readonly( &UT_CLASS::prop_name ) ) +// SET MEMBer with Name, Read-Only +#define SET_MEMB_N_RO(prop_name, lua_name_str) luna::set( ut, lua_name_str, sol::readonly( &UT_CLASS::prop_name ) ) // SET FX (function) #define SET_FX(func_name) luna::set_fx ( ut, #func_name, &UT_CLASS::func_name) // SET FX (function) with Type diff --git a/src/catalua_luna.h b/src/catalua_luna.h index 738e733cd393..3c7fb8c945b8 100644 --- a/src/catalua_luna.h +++ b/src/catalua_luna.h @@ -287,6 +287,15 @@ void doc_member( sol::table &dt, sol::types && ) dt[KEY_MEMBER_VARIABLE_TYPE] = doc_value( sol::types() ); } +// Olanti! Curse thee for what I must do! +template +void doc_member( sol::table &dt, sol::types> && ) +{ + dt[KEY_MEMBER_TYPE] = MEMBER_IS_VAR; + add_comment( dt, KEY_MEMBER_COMMENT ); + dt[KEY_MEMBER_VARIABLE_TYPE] = doc_value( sol::types() ); +} + template void doc_member_fx_impl2( sol::table &dt, sol::types &&, sol::types && ) { diff --git a/src/catalua_luna_doc.h b/src/catalua_luna_doc.h index cac64cd607d2..85be3ff13dfb 100644 --- a/src/catalua_luna_doc.h +++ b/src/catalua_luna_doc.h @@ -53,6 +53,7 @@ struct damage_instance; struct damage_unit; struct dealt_damage_instance; struct field_type; +struct mutation_branch; struct npc_opinion; struct npc_personality; struct point; diff --git a/src/character.cpp b/src/character.cpp index 68ad2ec6c4f2..1cf90c52d761 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -6083,6 +6083,36 @@ void Character::update_bodytemp( const map &m, const weather_manager &weather ) } } +int Character::get_part_temp_cur( const bodypart_id &id ) const +{ + return get_part( id ).get_temp_cur(); +} + +void Character::set_part_temp_cur( const bodypart_id &id, int temp ) +{ + get_part( id ).set_temp_cur( temp ); +} + +std::map Character::get_temp_cur() +{ + std::map temps; + + for( auto &pr : get_body() ) { + bodypart &bp = pr.second; + temps[ bp.get_id() ] = bp.get_temp_cur(); + } + return temps; +} + +void Character::set_temp_cur( int temp ) +{ + for( auto &pr : get_body() ) { + bodypart &bp = pr.second; + bp.set_temp_cur( temp ); + } +} + + int Character::blood_loss( const bodypart_id &bp ) const { int hp_cur_sum = get_part_hp_cur( bp ); diff --git a/src/character.h b/src/character.h index 84514a24ee84..46c2e834121a 100644 --- a/src/character.h +++ b/src/character.h @@ -475,6 +475,13 @@ class Character : public Creature, public location_visitable /** Maintains body temperature */ void update_bodytemp( const map &m, const weather_manager &weather ); + /** Getters/setters for body part temperature. + * This could go under Creature, but Character is the class with update_bodytemp. */ + int get_part_temp_cur( const bodypart_id &id ) const; + void set_part_temp_cur( const bodypart_id &id, int temp ); + std::map get_temp_cur(); + void set_temp_cur( int temp ); + /** Define blood loss (in percents) */ int blood_loss( const bodypart_id &bp ) const; diff --git a/src/npc.h b/src/npc.h index 2fc7c8c711fa..54b4393ed7ad 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1060,7 +1060,7 @@ class npc : public player bool dispose_item( item &obj, const std::string &prompt = std::string() ) override; - void aim(); + bool aim(); void do_reload( item &it ); // Physical movement from one tile to the next diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 9bcd8d5c3732..5f6ee01cb28c 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -1079,9 +1079,22 @@ void npc::execute_action( npc_action action ) } break; - case npc_aim: - aim(); - break; + case npc_aim: { + gun_mode mode = cbm_active.is_null() ? primary_weapon().gun_current_mode() : + cbm_fake_active->gun_current_mode(); + if( !mode ) { + std::string error_weapon = cbm_active.is_null() ? primary_weapon().tname() : + cbm_fake_active->tname(); + debugmsg( "NPC tried to aim %s without valid mode.", error_weapon ); + } + + bool did_aim = aim(); + if( !did_aim ) { + debugmsg( "%s is trying to aim, but failing repeatedly", disp_name().c_str() ); + set_moves( 0 ); + } + } + break; case npc_shoot: { gun_mode mode = cbm_active.is_null() ? primary_weapon().gun_current_mode() : @@ -2178,15 +2191,27 @@ bool npc::enough_time_to_reload( const item &gun ) const return turns_til_reloaded < turns_til_reached; } -void npc::aim() +bool npc::aim() { - double aim_amount = ranged::aim_per_move( *this, primary_weapon(), recoil ); + gun_mode mode = cbm_active.is_null() ? primary_weapon().gun_current_mode() : + cbm_fake_active->gun_current_mode(); + if( !mode ) { + std::string error_weapon = cbm_active.is_null() ? primary_weapon().tname() : + cbm_fake_active->tname(); + debugmsg( "NPC tried to aim %s without valid mode.", error_weapon ); + } + + bool did_aim = false; + double aim_amount = ranged::aim_per_move( *this, *mode.target, recoil ); while( aim_amount > 0 && recoil > 0 && moves > 0 ) { + did_aim = true; moves--; recoil -= aim_amount; recoil = std::max( 0.0, recoil ); - aim_amount = ranged::aim_per_move( *this, primary_weapon(), recoil ); + aim_amount = ranged::aim_per_move( *this, *mode.target, recoil ); } + + return did_aim; } bool npc::update_path( const tripoint &p, const bool no_bashing, bool force )