int player::get_acquirable_energy( const item &it, rechargeable_cbm cbm ) const { switch( cbm ) { case rechargeable_cbm::none: break; case rechargeable_cbm::battery: return std::min<long>( it.charges, std::numeric_limits<int>::max() ); case rechargeable_cbm::reactor: if( it.charges > 0 ) { const auto iter = plut_charges.find( it.typeId() ); return iter != plut_charges.end() ? it.charges * iter->second : 0; } break; case rechargeable_cbm::furnace: { int amount = ( it.volume() / 250_ml + it.weight() / 1_gram ) / 9; // @todo JSONize. if( it.made_of( material_id( "leather" ) ) ) { amount /= 4; } if( it.made_of( material_id( "wood" ) ) ) { amount /= 2; } return amount; } } return 0; }
itype_id mtype::get_meat_itype() const { if( has_flag( MF_POISON ) ) { if( made_of( material_id( "flesh" ) ) || made_of( material_id( "hflesh" ) ) ) { return "meat_tainted"; } else if( made_of( material_id( "iflesh" ) ) ) { //In the future, insects could drop insect flesh rather than plain ol' meat. return "meat_tainted"; } else if( made_of( material_id( "veggy" ) ) ) { return "veggy_tainted"; } else if( made_of( material_id( "bone" ) ) ) { return "bone_tainted"; } } else { if( made_of( material_id( "flesh" ) ) || made_of( material_id( "hflesh" ) ) ) { if( has_flag( MF_HUMAN ) ) { return "human_flesh"; } else if( has_flag( MF_AQUATIC ) ) { return "fish"; } else { return "meat"; } } else if( made_of( material_id( "iflesh" ) ) ) { //In the future, insects could drop insect flesh rather than plain ol' meat. return "meat"; } else if( made_of( material_id( "veggy" ) ) ) { return "veggy"; } else if( made_of( material_id( "bone" ) ) ) { return "bone"; } } return "null"; }
void mdeath::normal(monster *z) { if ((g->u.sees(*z)) && (!z->no_corpse_quiet)) { add_msg(m_good, _("The %s dies!"), z->name().c_str()); //Currently it is possible to get multiple messages that a monster died. } if ( z->type->in_species( ZOMBIE )) { sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume(z->pos())); } m_size monSize = z->type->size; bool leaveCorpse = !z->no_corpse_quiet; // leave some blood if we have to field_id type_blood = z->bloodType(); if (type_blood != fd_null) { g->m.add_field( z->pos(), type_blood, 1, 0 ); } int maxHP = z->get_hp_max(); if (!maxHP) { maxHP = 1; } float overflowDamage = std::max( -z->get_hp(), 0 ); float corpseDamage = 5 * (overflowDamage / (maxHP * 2)); if (leaveCorpse) { int gibAmount = int(floor(corpseDamage)) - 1; // allow one extra gib per 5 HP int gibLimit = 1 + (maxHP / 5.0); if (gibAmount > gibLimit) { gibAmount = gibLimit; } bool pulverized = (corpseDamage > 5 && overflowDamage > 150); if (!pulverized) { make_mon_corpse(z, int(floor(corpseDamage))); } else if (monSize >= MS_MEDIUM) { gibAmount += rng(1, 6); sfx::play_variant_sound( "mon_death", "zombie_gibbed", sfx::get_heard_volume(z->pos())); } // Limit chunking to flesh, veggy and insect creatures until other kinds are supported. bool leaveGibs = (z->made_of( material_id( "flesh" ) ) || z->made_of( material_id( "hflesh" ) ) || z->made_of( material_id( "veggy" ) ) || z->made_of( material_id( "iflesh" ) )); if (leaveGibs) { make_gibs( z, gibAmount ); } } }
/** * @brief Constructor sets the ID and unique ID for the Material. * @param id the user-specified optional Material ID * @param name the user-specified optional Material name */ Material::Material(int id, const char* name) { /* If the user did not define an optional ID, create one */ if (id == 0) _id = material_id(); /* Use the user-defined ID */ else _id = id; _name = NULL; setName(name); _volume = 0.; _num_instances = 0; /* Initialize a dummy number groups */ _num_groups = -1; _sigma_t = NULL; _sigma_s = NULL; _sigma_f = NULL; _nu_sigma_f = NULL; _chi = NULL; _fiss_matrix = NULL; _fissionable = false; _data_aligned = false; return; }
mtype::mtype() { id = mtype_id::NULL_ID(); name = "human"; name_plural = "humans"; description = ""; sym = " "; color = c_white; size = MS_MEDIUM; mat = { material_id( "flesh" ) }; phase = SOLID; def_chance = 0; upgrades = false; half_life = -1; upgrade_into = mtype_id::NULL_ID(); upgrade_group = mongroup_id::NULL_ID(); burn_into = mtype_id::NULL_ID(); dies.push_back( &mdeath::normal ); sp_defense = nullptr; harvest = harvest_id::NULL_ID(); luminance = 0; bash_skill = 0; flags.insert( MF_HUMAN ); flags.insert( MF_BONES ); flags.insert( MF_LEATHER ); }
// load a material object from incoming JSON void material_type::load_material( JsonObject &jsobj ) { material_type mat; mat._ident = material_id( jsobj.get_string( "ident" ) ); mat._name = _( jsobj.get_string( "name" ).c_str() ); mat._salvage_id = jsobj.get_string( "salvage_id", "null" ); mat._salvage_multiplier = jsobj.get_float( "salvage_multiplier", 1.0 ); mat._bash_resist = jsobj.get_int( "bash_resist" ); mat._cut_resist = jsobj.get_int( "cut_resist" ); mat._bash_dmg_verb = _( jsobj.get_string( "bash_dmg_verb" ).c_str() ); mat._cut_dmg_verb = _( jsobj.get_string( "cut_dmg_verb" ).c_str() ); mat._acid_resist = jsobj.get_int( "acid_resist" ); mat._elec_resist = jsobj.get_int( "elec_resist" ); mat._fire_resist = jsobj.get_int( "fire_resist" ); mat._chip_resist = jsobj.get_int( "chip_resist" ); mat._density = jsobj.get_int( "density" ); JsonArray jsarr = jsobj.get_array( "dmg_adj" ); mat._dmg_adj[0] = _( jsarr.next_string().c_str() ); mat._dmg_adj[1] = _( jsarr.next_string().c_str() ); mat._dmg_adj[2] = _( jsarr.next_string().c_str() ); mat._dmg_adj[3] = _( jsarr.next_string().c_str() ); _all_materials[mat._ident] = mat; DebugLog( D_INFO, DC_ALL ) << "Loaded material: " << mat._name; }
/** * @brief Constructor sets the ID and unique ID for the Material. * @param id the user-specified optional Material ID * @param name the user-specified optional Material name */ Material::Material(int id, const char* name) { /* If the user did not define an optional ID, create one */ if (id == 0) _id = material_id(); /* Use the user-defined ID */ else _id = id; _name = NULL; setName(name); _sigma_t = NULL; _sigma_a = NULL; _sigma_s = NULL; _sigma_f = NULL; _nu_sigma_f = NULL; _chi = NULL; _dif_coef = NULL; _dif_hat = NULL; _dif_tilde = NULL; _buckling = NULL; _fissionable = false; _data_aligned = false; return; }
field_id mtype::gibType() const { if( has_flag( MF_LARVA ) || in_species( MOLLUSK ) ) { return fd_gibs_invertebrate; } if( made_of( material_id( "veggy" ) ) ) { return fd_gibs_veggy; } if( made_of( material_id( "iflesh" ) ) ) { return fd_gibs_insect; } if( made_of( material_id( "flesh" ) ) ) { return fd_gibs_flesh; } // There are other materials not listed here like steel, protoplasmic, powder, null, stone, bone return fd_null; }
void sfx::do_projectile_hit( const Creature &target ) { const int heard_volume = sfx::get_heard_volume( target.pos() ); const int angle = get_heard_angle( target.pos() ); if( target.is_monster() ) { const monster &mon = dynamic_cast<const monster &>( target ); static std::set<material_id> const fleshy = { material_id( "flesh" ), material_id( "hflesh" ), material_id( "iflesh" ), material_id( "veggy" ), material_id( "bone" ), }; const bool is_fleshy = std::any_of( fleshy.begin(), fleshy.end(), [&mon]( const material_id &m ) { return mon.made_of( m ); } ); if( is_fleshy ) { play_variant_sound( "bullet_hit", "hit_flesh", heard_volume, angle, 0.8, 1.2 ); return; } else if( mon.made_of( material_id( "stone" ) ) ) { play_variant_sound( "bullet_hit", "hit_wall", heard_volume, angle, 0.8, 1.2 ); return; } else if( mon.made_of( material_id( "steel" ) ) ) { play_variant_sound( "bullet_hit", "hit_metal", heard_volume, angle, 0.8, 1.2 ); return; } else { play_variant_sound( "bullet_hit", "hit_flesh", heard_volume, angle, 0.8, 1.2 ); return; } } play_variant_sound( "bullet_hit", "hit_flesh", heard_volume, angle, 0.8, 1.2 ); }
void inventory::rust_iron_items() { for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { if( elem_stack_iter.made_of( material_id( "iron" ) ) && !elem_stack_iter.has_flag( "WATERPROOF_GUN" ) && !elem_stack_iter.has_flag( "WATERPROOF" ) && elem_stack_iter.damage() < elem_stack_iter.max_damage() && one_in( 500 ) ) { elem_stack_iter.inc_damage( DT_ACID ); // rusting never completely destroys an item } } } }
void inventory::rust_iron_items() { for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { if( elem_stack_iter.made_of( material_id( "iron" ) ) && !elem_stack_iter.has_flag( "WATERPROOF_GUN" ) && !elem_stack_iter.has_flag( "WATERPROOF" ) && elem_stack_iter.damage < 5 && one_in( 500 ) ) { elem_stack_iter.damage++; } } } }
field_id mtype::bloodType() const { if( has_flag( MF_ACID_BLOOD ) ) //A monster that has the death effect "ACID" does not need to have acid blood. { return fd_acid; } if( has_flag( MF_BILE_BLOOD ) ) { return fd_bile; } if( has_flag( MF_LARVA ) || has_flag( MF_ARTHROPOD_BLOOD ) ) { return fd_blood_invertebrate; } if( made_of( material_id( "veggy" ) ) ) { return fd_blood_veggy; } if( made_of( material_id( "iflesh" ) ) ) { return fd_blood_insect; } if( has_flag( MF_WARM ) && made_of( material_id( "flesh" ) ) ) { return fd_blood; } return fd_null; }
material_type::material_type() { _ident = material_id( "null" ); _name = "null"; _salvage_id = "null"; _salvage_multiplier = 1.0; _bash_resist = 0; _cut_resist = 0; _bash_dmg_verb = _( "damages" ); _cut_dmg_verb = _( "damages" ); _dmg_adj[0] = _( "lightly damaged" ); _dmg_adj[1] = _( "damaged" ); _dmg_adj[2] = _( "very damaged" ); _dmg_adj[3] = _( "thoroughly damaged" ); _acid_resist = 0; _elec_resist = 0; _fire_resist = 0; _chip_resist = 0; _density = 1; }
void LLMaterialMgr::onGetResponse(bool success, const LLSD& content, const LLUUID& region_id) { if (!success) { // *TODO: is there any kind of error handling we can do here? LL_WARNS("Materials")<< "failed"<<LL_ENDL; return; } llassert(content.isMap()); llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); std::istringstream content_stream(content_string); LLSD response_data; if (!unzip_llsd(response_data, content_stream, content_binary.size())) { LL_WARNS("Materials") << "Cannot unzip LLSD binary content" << LL_ENDL; return; } llassert(response_data.isArray()); LL_DEBUGS("Materials") << "response has "<< response_data.size() << " materials" << LL_ENDL; for (LLSD::array_const_iterator itMaterial = response_data.beginArray(); itMaterial != response_data.endArray(); ++itMaterial) { const LLSD& material_data = *itMaterial; llassert(material_data.isMap()); llassert(material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD)); llassert(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary()); LLMaterialID material_id(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary()); llassert(material_data.has(MATERIALS_CAP_MATERIAL_FIELD)); llassert(material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap()); setMaterial(region_id, material_id, material_data[MATERIALS_CAP_MATERIAL_FIELD]); } }
mtype::mtype() { id = mtype_id::NULL_ID(); name = "human"; name_plural = "humans"; sym = " "; color = c_white; size = MS_MEDIUM; volume = 62499_ml; weight = 81499_gram; mat = { material_id( "flesh" ) }; phase = SOLID; def_chance = 0; upgrades = false; half_life = -1; age_grow = -1; upgrade_into = mtype_id::NULL_ID(); upgrade_group = mongroup_id::NULL_ID(); reproduces = false; baby_timer = -1; baby_count = -1; baby_monster = mtype_id::NULL_ID(); baby_egg = "null"; biosignatures = false; biosig_timer = -1; biosig_item = "null"; burn_into = mtype_id::NULL_ID(); dies.push_back( &mdeath::normal ); sp_defense = nullptr; harvest = harvest_id::NULL_ID(); luminance = 0; bash_skill = 0; flags .set( MF_HUMAN ) .set( MF_BONES ) .set( MF_LEATHER ); }
void inventory::rust_iron_items() { for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { if( elem_stack_iter.made_of( material_id( "iron" ) ) && !elem_stack_iter.has_flag( "WATERPROOF_GUN" ) && !elem_stack_iter.has_flag( "WATERPROOF" ) && elem_stack_iter.damage() < elem_stack_iter.max_damage() / 2 && //Passivation layer prevents further rusting one_in( 500 ) && //Scale with volume, bigger = slower (see #24204) one_in( static_cast<int>( 14 * std::cbrt( 0.5 * std::max( 0.05, static_cast<double>( elem_stack_iter.base_volume().value() ) / 250 ) ) ) ) && // ^season length ^14/5*0.75/3.14 (from volume of sphere) g->m.water_from( g->u.pos() ).typeId() == "salt_water" ) { //Freshwater without oxygen rusts slower than air elem_stack_iter.inc_damage( DT_ACID ); // rusting never completely destroys an item add_msg( m_bad, _( "Your %s is damaged by rust." ), elem_stack_iter.tname() ); } } } }
/** * @brief Create a duplicate of the Material. * @return a pointer to the clone */ Material* Material::clone() { Material* clone = new Material(material_id(), _name); /* Set the number of groups if this Material's groups have been set */ if (_num_groups > 0) clone->setNumEnergyGroups(_num_groups); for (int i=0; i < _num_groups; i++) { clone->setSigmaTByGroup((double)_sigma_t[i], i+1); clone->setSigmaFByGroup((double)_sigma_f[i], i+1); clone->setNuSigmaFByGroup((double)_nu_sigma_f[i], i+1); clone->setChiByGroup((double)_chi[i], i+1); for (int j=0; j < _num_groups; j++) clone->setSigmaSByGroup((double)getSigmaSByGroup(i+1,j+1), i+1, j+1); } if (_fiss_matrix != NULL) clone->buildFissionMatrix(); return clone; }
void mdeath::splatter( monster &z ) { // Limit chunking to flesh, veggy and insect creatures until other kinds are supported. const std::vector<material_id> gib_mats = {{ material_id( "flesh" ), material_id( "hflesh" ), material_id( "veggy" ), material_id( "iflesh" ), material_id( "bone" ) } }; const bool gibbable = !z.type->has_flag( MF_NOGIB ) && std::any_of( gib_mats.begin(), gib_mats.end(), [&z]( const material_id & gm ) { return z.made_of( gm ); } ); const int max_hp = std::max( z.get_hp_max(), 1 ); const float overflow_damage = std::max( -z.get_hp(), 0 ); const float corpse_damage = 2.5 * overflow_damage / max_hp; bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max(); // make sure that full splatter happens when this is a set death function, not part of normal for( const auto &deathfunction : z.type->dies ) { if( deathfunction == mdeath::splatter ) { pulverized = true; } } const field_id type_blood = z.bloodType(); const field_id type_gib = z.gibType(); if( gibbable ) { const auto area = g->m.points_in_radius( z.pos(), 1 ); int number_of_gibs = std::min( std::floor( corpse_damage ) - 1, 1 + max_hp / 5.0f ); if( pulverized && z.type->size >= MS_MEDIUM ) { number_of_gibs += rng( 1, 6 ); sfx::play_variant_sound( "mon_death", "zombie_gibbed", sfx::get_heard_volume( z.pos() ) ); } for( int i = 0; i < number_of_gibs; ++i ) { g->m.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) ); g->m.add_splatter( type_blood, random_entry( area ) ); } } int num_chunks = rng( 0, z.type->get_meat_chunks_count() / 4 ); num_chunks = std::min( num_chunks, 10 ); if( pulverized && gibbable ) { const itype_id meat = z.type->get_meat_itype(); const item chunk( meat ); for( int i = 0; i < num_chunks; i++ ) { bool drop_chunks = true; tripoint tarp( z.pos() + point( rng( -3, 3 ), rng( -3, 3 ) ) ); const auto traj = line_to( z.pos(), tarp ); for( size_t j = 0; j < traj.size(); j++ ) { tarp = traj[j]; if( one_in( 2 ) && type_blood != fd_null ) { g->m.add_splatter( type_blood, tarp ); } else { g->m.add_splatter( type_gib, tarp, rng( 1, j + 1 ) ); } if( g->m.impassable( tarp ) ) { g->m.bash( tarp, 3 ); if( g->m.impassable( tarp ) ) { // Target is obstacle, not destroyed by bashing, // stop trajectory in front of it, if this is the first // point (e.g. wall adjacent to monster), don't drop anything on it if( j > 0 ) { tarp = traj[j - 1]; } else { drop_chunks = false; } break; } } } if( drop_chunks ) { g->m.add_item_or_charges( tarp, chunk ); } } } }
void mutation_branch::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "id", id ); mandatory( jo, was_loaded, "name", raw_name, translated_string_reader ); mandatory( jo, was_loaded, "description", raw_desc, translated_string_reader ); mandatory( jo, was_loaded, "points", points ); optional( jo, was_loaded, "visibility", visibility, 0 ); optional( jo, was_loaded, "ugliness", ugliness, 0 ); optional( jo, was_loaded, "starting_trait", startingtrait, false ); optional( jo, was_loaded, "mixed_effect", mixed_effect, false ); optional( jo, was_loaded, "active", activated, false ); optional( jo, was_loaded, "starts_active", starts_active, false ); optional( jo, was_loaded, "destroys_gear", destroys_gear, false ); optional( jo, was_loaded, "allow_soft_gear", allow_soft_gear, false ); optional( jo, was_loaded, "cost", cost, 0 ); optional( jo, was_loaded, "time", cooldown, 0 ); optional( jo, was_loaded, "hunger", hunger, false ); optional( jo, was_loaded, "thirst", thirst, false ); optional( jo, was_loaded, "fatigue", fatigue, false ); optional( jo, was_loaded, "valid", valid, true ); optional( jo, was_loaded, "purifiable", purifiable, true ); if( jo.has_object( "spawn_item" ) ) { auto si = jo.get_object( "spawn_item" ); optional( si, was_loaded, "type", spawn_item ); optional( si, was_loaded, "message", raw_spawn_item_message ); } if( jo.has_object( "ranged_mutation" ) ) { auto si = jo.get_object( "ranged_mutation" ); optional( si, was_loaded, "type", ranged_mutation ); optional( si, was_loaded, "message", raw_ranged_mutation_message ); } optional( jo, was_loaded, "initial_ma_styles", initial_ma_styles ); if( jo.has_array( "bodytemp_modifiers" ) ) { auto bodytemp_array = jo.get_array( "bodytemp_modifiers" ); bodytemp_min = bodytemp_array.get_int( 0 ); bodytemp_max = bodytemp_array.get_int( 1 ); } optional( jo, was_loaded, "bodytemp_sleep", bodytemp_sleep, 0 ); optional( jo, was_loaded, "threshold", threshold, false ); optional( jo, was_loaded, "profession", profession, false ); optional( jo, was_loaded, "debug", debug, false ); optional( jo, was_loaded, "player_display", player_display, true ); JsonArray vr = jo.get_array( "vitamin_rates" ); while( vr.has_more() ) { auto pair = vr.next_array(); vitamin_rates.emplace( vitamin_id( pair.get_string( 0 ) ), time_duration::from_turns( pair.get_int( 1 ) ) ); } auto vam = jo.get_array( "vitamins_absorb_multi" ); while( vam.has_more() ) { auto pair = vam.next_array(); std::map<vitamin_id, double> vit; auto vit_array = pair.get_array( 1 ); // fill the inner map with vitamins while( vit_array.has_more() ) { auto vitamins = vit_array.next_array(); vit.emplace( vitamin_id( vitamins.get_string( 0 ) ), vitamins.get_float( 1 ) ); } // assign the inner vitamin map to the material_id key vitamin_absorb_multi.emplace( material_id( pair.get_string( 0 ) ), vit ); } optional( jo, was_loaded, "healing_awake", healing_awake, 0.0f ); optional( jo, was_loaded, "healing_resting", healing_resting, 0.0f ); optional( jo, was_loaded, "hp_modifier", hp_modifier, 0.0f ); optional( jo, was_loaded, "hp_modifier_secondary", hp_modifier_secondary, 0.0f ); optional( jo, was_loaded, "hp_adjustment", hp_adjustment, 0.0f ); optional( jo, was_loaded, "stealth_modifier", stealth_modifier, 0.0f ); optional( jo, was_loaded, "str_modifier", str_modifier, 0.0f ); optional( jo, was_loaded, "dodge_modifier", dodge_modifier, 0.0f ); optional( jo, was_loaded, "speed_modifier", speed_modifier, 1.0f ); optional( jo, was_loaded, "movecost_modifier", movecost_modifier, 1.0f ); optional( jo, was_loaded, "movecost_flatground_modifier", movecost_flatground_modifier, 1.0f ); optional( jo, was_loaded, "movecost_obstacle_modifier", movecost_obstacle_modifier, 1.0f ); optional( jo, was_loaded, "attackcost_modifier", attackcost_modifier, 1.0f ); optional( jo, was_loaded, "max_stamina_modifier", max_stamina_modifier, 1.0f ); optional( jo, was_loaded, "weight_capacity_modifier", weight_capacity_modifier, 1.0f ); optional( jo, was_loaded, "hearing_modifier", hearing_modifier, 1.0f ); optional( jo, was_loaded, "noise_modifier", noise_modifier, 1.0f ); optional( jo, was_loaded, "metabolism_modifier", metabolism_modifier, 0.0f ); optional( jo, was_loaded, "thirst_modifier", thirst_modifier, 0.0f ); optional( jo, was_loaded, "fatigue_modifier", fatigue_modifier, 0.0f ); optional( jo, was_loaded, "fatigue_regen_modifier", fatigue_regen_modifier, 0.0f ); optional( jo, was_loaded, "stamina_regen_modifier", stamina_regen_modifier, 0.0f ); optional( jo, was_loaded, "overmap_sight", overmap_sight, 0.0f ); optional( jo, was_loaded, "overmap_multiplier", overmap_multiplier, 1.0f ); if( jo.has_object( "social_modifiers" ) ) { JsonObject sm = jo.get_object( "social_modifiers" ); social_mods = load_mutation_social_mods( sm ); } load_mutation_mods( jo, "passive_mods", mods ); /* Not currently supported due to inability to save active mutation state load_mutation_mods(jsobj, "active_mods", new_mut.mods); */ optional( jo, was_loaded, "prereqs", prereqs ); optional( jo, was_loaded, "prereqs2", prereqs2 ); optional( jo, was_loaded, "threshreq", threshreq ); optional( jo, was_loaded, "cancels", cancels ); optional( jo, was_loaded, "changes_to", replacements ); optional( jo, was_loaded, "leads_to", additions ); optional( jo, was_loaded, "flags", flags ); optional( jo, was_loaded, "types", types ); auto jsarr = jo.get_array( "category" ); while( jsarr.has_more() ) { std::string s = jsarr.next_string(); category.push_back( s ); mutations_category[s].push_back( trait_id( id ) ); } jsarr = jo.get_array( "wet_protection" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); std::string part_id = jo.get_string( "part" ); int ignored = jo.get_int( "ignored", 0 ); int neutral = jo.get_int( "neutral", 0 ); int good = jo.get_int( "good", 0 ); tripoint protect = tripoint( ignored, neutral, good ); protection[get_body_part_token( part_id )] = protect; } jsarr = jo.get_array( "encumbrance_always" ); while( jsarr.has_more() ) { JsonArray jo = jsarr.next_array(); std::string part_id = jo.next_string(); int enc = jo.next_int(); encumbrance_always[get_body_part_token( part_id )] = enc; } jsarr = jo.get_array( "encumbrance_covered" ); while( jsarr.has_more() ) { JsonArray jo = jsarr.next_array(); std::string part_id = jo.next_string(); int enc = jo.next_int(); encumbrance_covered[get_body_part_token( part_id )] = enc; } jsarr = jo.get_array( "restricts_gear" ); while( jsarr.has_more() ) { restricts_gear.insert( get_body_part_token( jsarr.next_string() ) ); } jsarr = jo.get_array( "armor" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); auto parts = jo.get_tags( "parts" ); std::set<body_part> bps; for( const std::string &part_string : parts ) { if( part_string == "ALL" ) { // Shorthand, since many mutations protect whole body bps.insert( all_body_parts.begin(), all_body_parts.end() ); } else { bps.insert( get_body_part_token( part_string ) ); } } resistances res = load_resistances_instance( jo ); for( body_part bp : bps ) { armor[ bp ] = res; } } if( jo.has_array( "attacks" ) ) { jsarr = jo.get_array( "attacks" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); attacks_granted.emplace_back( load_mutation_attack( jo ) ); } } else if( jo.has_object( "attacks" ) ) { JsonObject attack = jo.get_object( "attacks" ); attacks_granted.emplace_back( load_mutation_attack( attack ) ); } }
void mdeath::normal( monster &z ) { if( z.no_corpse_quiet ) { return; } if( z.type->in_species( ZOMBIE ) ) { sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume( z.pos() ) ); } if( g->u.sees( z ) ) { //Currently it is possible to get multiple messages that a monster died. add_msg( m_good, _( "The %s dies!" ), z.name().c_str() ); } const int max_hp = std::max( z.get_hp_max(), 1 ); const float overflow_damage = std::max( -z.get_hp(), 0 ); const float corpse_damage = 2.5 * overflow_damage / max_hp; const bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max(); z.bleed(); // leave some blood if we have to if( !pulverized ) { make_mon_corpse( z, int( std::floor( corpse_damage ) ) ); } // Limit chunking to flesh, veggy and insect creatures until other kinds are supported. const std::vector<material_id> gib_mats = {{ material_id( "flesh" ), material_id( "hflesh" ), material_id( "veggy" ), material_id( "iflesh" ), material_id( "bone" ) }}; const bool gibbable = !z.type->has_flag( MF_NOGIB ) && std::any_of( gib_mats.begin(), gib_mats.end(), [&z]( const material_id &gm ) { return z.made_of( gm ); } ); const field_id type_blood = z.bloodType(); const field_id type_gib = z.gibType(); if( gibbable ) { const auto area = g->m.points_in_radius( z.pos(), 1 ); int number_of_gibs = std::min( std::floor( corpse_damage ) - 1, 1 + max_hp / 5.0f ); if( pulverized && z.type->size >= MS_MEDIUM ) { number_of_gibs += rng( 1, 6 ); sfx::play_variant_sound( "mon_death", "zombie_gibbed", sfx::get_heard_volume( z.pos() ) ); } for( int i = 0; i < number_of_gibs; ++i ) { g->m.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) ); g->m.add_splatter( type_blood, random_entry( area ) ); } } const int num_chunks = z.type->get_meat_chunks_count(); if( pulverized && gibbable ) { const itype_id meat = z.type->get_meat_itype(); const item chunk( meat ); for( int i = 0; i < num_chunks; i++ ) { tripoint tarp( z.pos() + point( rng( -3, 3 ), rng( -3, 3 ) ) ); const auto traj = line_to( z.pos(), tarp ); for( size_t j = 0; j < traj.size(); j++ ) { tarp = traj[j]; if( one_in( 2 ) && type_blood != fd_null ) { g->m.add_splatter( type_blood, tarp ); } else { g->m.add_splatter( type_gib, tarp, rng( 1, j + 1 ) ); } if( g->m.impassable( tarp ) ) { g->m.bash( tarp, 3 ); if( g->m.impassable( tarp ) ) { // Target is obstacle, not destroyed by bashing, // stop trajectory in front of it, if this is the first // point (e.g. wall adjacent to monster) , make it invalid. if( j > 0 ) { tarp = traj[j - 1]; } else { tarp = tripoint_min; } break; } } } if( tarp != tripoint_min ) { g->m.add_item_or_charges( tarp, chunk ); } } } }
/** * Attempts to harm a creature with a projectile. * * @param source Pointer to the creature who shot the projectile. * @param attack A structure describing the attack and its results. */ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack ) { const double missed_by = attack.missed_by; if( missed_by >= 1.0 ) { // Total miss return; } const projectile &proj = attack.proj; dealt_damage_instance &dealt_dam = attack.dealt_dam; const auto &proj_effects = proj.proj_effects; const bool u_see_this = g->u.sees(*this); const int avoid_roll = dodge_roll(); // Do dice(10, speed) instead of dice(speed, 10) because speed could potentially be > 10000 const int diff_roll = dice( 10, proj.speed ); // Partial dodge, capped at [0.0, 1.0], added to missed_by const double dodge_rescaled = avoid_roll / static_cast<double>( diff_roll ); const double goodhit = missed_by + std::max( 0.0, std::min( 1.0, dodge_rescaled ) ) ; if( goodhit >= 1.0 ) { // "Avoid" rather than "dodge", because it includes removing self from the line of fire // rather than just Matrix-style bullet dodging if( source != nullptr && g->u.sees( *source ) ) { add_msg_player_or_npc( m_warning, _("You avoid %s projectile!"), _("<npcname> avoids %s projectile."), source->disp_name(true).c_str() ); } else { add_msg_player_or_npc( m_warning, _("You avoid an incoming projectile!"), _("<npcname> avoids an incoming projectile.") ); } attack.missed_by = 1.0; // Arbitrary value return; } // Bounce applies whether it does damage or not. if( proj.proj_effects.count( "BOUNCE" ) ) { add_effect( effect_bounced, 1_turns ); } body_part bp_hit; double hit_value = missed_by + rng_float(-0.5, 0.5); // Headshots considered elsewhere if( hit_value <= 0.4 ) { bp_hit = bp_torso; } else if (one_in(4)) { if( one_in(2)) { bp_hit = bp_leg_l; } else { bp_hit = bp_leg_r; } } else { if( one_in(2)) { bp_hit = bp_arm_l; } else { bp_hit = bp_arm_r; } } double damage_mult = 1.0; std::string message = ""; game_message_type gmtSCTcolor = m_neutral; if( goodhit < accuracy_headshot ) { message = _("Headshot!"); gmtSCTcolor = m_headshot; damage_mult *= rng_float(1.95, 2.05); bp_hit = bp_head; // headshot hits the head, of course } else if( goodhit < accuracy_critical ) { message = _("Critical!"); gmtSCTcolor = m_critical; damage_mult *= rng_float(1.5, 2.0); } else if( goodhit < accuracy_goodhit ) { message = _("Good hit!"); gmtSCTcolor = m_good; damage_mult *= rng_float(1, 1.5); } else if( goodhit < accuracy_standard ) { damage_mult *= rng_float(0.5, 1); } else if( goodhit < accuracy_grazing ) { message = _("Grazing hit."); gmtSCTcolor = m_grazing; damage_mult *= rng_float(0, .25); } if( source != nullptr && !message.empty() ) { source->add_msg_if_player(m_good, message.c_str()); } attack.missed_by = goodhit; // copy it, since we're mutating damage_instance impact = proj.impact; if( damage_mult > 0.0f && proj_effects.count( "NO_DAMAGE_SCALING" ) ) { damage_mult = 1.0f; } impact.mult_damage(damage_mult); if( proj_effects.count( "NOGIB" ) > 0 ) { float dmg_ratio = (float)impact.total_damage() / get_hp_max( player::bp_to_hp( bp_hit ) ); if( dmg_ratio > 1.25f ) { impact.mult_damage( 1.0f / dmg_ratio ); } } dealt_dam = deal_damage(source, bp_hit, impact); dealt_dam.bp_hit = bp_hit; // Apply ammo effects to target. if (proj.proj_effects.count("FLAME")) { if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) || made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) || made_of( material_id( "wood" ) ) ) { add_effect( effect_onfire, rng( 8_turns, 20_turns ), bp_hit ); } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) { add_effect( effect_onfire, rng( 5_turns, 10_turns ), bp_hit ); } } else if (proj.proj_effects.count("INCENDIARY") ) { if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) || made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) || made_of( material_id( "wood" ) ) ) { add_effect( effect_onfire, rng( 2_turns, 6_turns ), bp_hit ); } else if ( (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) && one_in(4) ) { add_effect( effect_onfire, rng( 1_turns, 4_turns ), bp_hit ); } } else if (proj.proj_effects.count("IGNITE")) { if (made_of( material_id( "veggy" ) ) || made_of( material_id( "cotton" ) ) || made_of( material_id( "wool" ) ) || made_of( material_id( "paper" ) ) || made_of( material_id( "wood" ) ) ) { add_effect( effect_onfire, 6_turns, bp_hit ); } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) { add_effect( effect_onfire, 10_turns, bp_hit ); } } if( bp_hit == bp_head && proj_effects.count( "BLINDS_EYES" ) ) { // TODO: Change this to require bp_eyes add_env_effect( effect_blind, bp_eyes, 5, rng( 3_turns, 10_turns ) ); } if( proj_effects.count( "APPLY_SAP" ) ) { add_effect( effect_sap, 1_turns * dealt_dam.total_damage() ); } int stun_strength = 0; if (proj.proj_effects.count("BEANBAG")) { stun_strength = 4; } if (proj.proj_effects.count("LARGE_BEANBAG")) { stun_strength = 16; } if( stun_strength > 0 ) { switch( get_size() ) { case MS_TINY: stun_strength *= 4; break; case MS_SMALL: stun_strength *= 2; break; case MS_MEDIUM: default: break; case MS_LARGE: stun_strength /= 2; break; case MS_HUGE: stun_strength /= 4; break; } add_effect( effect_stunned, 1_turns * rng( stun_strength / 2, stun_strength ) ); } if(u_see_this) { if( damage_mult == 0 ) { if( source != nullptr ) { add_msg( source->is_player() ? _("You miss!") : _("The shot misses!") ); } } else if( dealt_dam.total_damage() == 0 ) { //~ 1$ - monster name, 2$ - character's bodypart or monster's skin/armor add_msg( _("The shot reflects off %1$s %2$s!"), disp_name(true).c_str(), is_monster() ? skin_name().c_str() : body_part_name_accusative(bp_hit).c_str() ); } else if( is_player() ) { //monster hits player ranged //~ Hit message. 1$s is bodypart name in accusative. 2$d is damage value. add_msg_if_player(m_bad, _( "You were hit in the %1$s for %2$d damage." ), body_part_name_accusative(bp_hit).c_str(), dealt_dam.total_damage()); } else if( source != nullptr ) { if( source->is_player() ) { //player hits monster ranged SCT.add(posx(), posy(), direction_from(0, 0, posx() - source->posx(), posy() - source->posy()), get_hp_bar(dealt_dam.total_damage(), get_hp_max(), true).first, m_good, message, gmtSCTcolor); if (get_hp() > 0) { SCT.add(posx(), posy(), direction_from(0, 0, posx() - source->posx(), posy() - source->posy()), get_hp_bar(get_hp(), get_hp_max(), true).first, m_good, //~ "hit points", used in scrolling combat text _("hp"), m_neutral, "hp"); } else { SCT.removeCreatureHP(); } add_msg(m_good, _("You hit %s for %d damage."), disp_name().c_str(), dealt_dam.total_damage()); } else if( u_see_this ) { //~ 1$ - shooter, 2$ - target add_msg(_("%1$s shoots %2$s."), source->disp_name().c_str(), disp_name().c_str()); } } } check_dead_state(); attack.hit_critter = this; attack.missed_by = goodhit; }
const efftype_id effect_bounced( "bounced" ); const efftype_id effect_downed( "downed" ); const efftype_id effect_onfire( "onfire" ); const efftype_id effect_sap( "sap" ); const efftype_id effect_sleep( "sleep" ); const efftype_id effect_stunned( "stunned" ); const efftype_id effect_zapped( "zapped" ); const efftype_id effect_lying_down( "lying_down" ); const std::map<std::string, m_size> Creature::size_map = { {"TINY", MS_TINY}, {"SMALL", MS_SMALL}, {"MEDIUM", MS_MEDIUM}, {"LARGE", MS_LARGE}, {"HUGE", MS_HUGE} }; const std::set<material_id> Creature::cmat_flesh{ material_id( "flesh" ), material_id( "iflesh" ) }; const std::set<material_id> Creature::cmat_fleshnveg{ material_id( "flesh" ), material_id( "iflesh" ), material_id( "veggy" ) }; const std::set<material_id> Creature::cmat_flammable{ material_id( "paper" ), material_id( "powder" ), material_id( "wood" ), material_id( "cotton" ), material_id( "wool" ) }; const std::set<material_id> Creature::cmat_flameres{ material_id( "stone" ), material_id( "kevlar" ), material_id( "steel" ) }; Creature::Creature() { moves = 0;
void LLMaterialMgr::onGetAllResponse(bool success, const LLSD& content, const LLUUID& region_id) { if (!success) { // *TODO: is there any kind of error handling we can do here? LL_WARNS("Materials")<< "failed"<<LL_ENDL; return; } llassert(content.isMap()); llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); std::istringstream content_stream(content_string); LLSD response_data; if (!unzip_llsd(response_data, content_stream, content_binary.size())) { LL_WARNS("Materials") << "Cannot unzip LLSD binary content" << LL_ENDL; return; } get_queue_t::iterator itQueue = mGetQueue.find(region_id); material_map_t materials; llassert(response_data.isArray()); LL_DEBUGS("Materials") << "response has "<< response_data.size() << " materials" << LL_ENDL; for (LLSD::array_const_iterator itMaterial = response_data.beginArray(); itMaterial != response_data.endArray(); ++itMaterial) { const LLSD& material_data = *itMaterial; llassert(material_data.isMap()); llassert(material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD)); llassert(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary()); LLMaterialID material_id(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary()); if (mGetQueue.end() != itQueue) { itQueue->second.erase(material_id); } llassert(material_data.has(MATERIALS_CAP_MATERIAL_FIELD)); llassert(material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap()); LLMaterialPtr material = setMaterial(region_id, material_id, material_data[MATERIALS_CAP_MATERIAL_FIELD]); materials[material_id] = material; } getall_callback_map_t::iterator itCallback = mGetAllCallbacks.find(region_id); if (itCallback != mGetAllCallbacks.end()) { (*itCallback->second)(region_id, materials); delete itCallback->second; mGetAllCallbacks.erase(itCallback); } if ( (mGetQueue.end() != itQueue) && (itQueue->second.empty()) ) { mGetQueue.erase(itQueue); } LL_DEBUGS("Materials")<< "recording that getAll has been done for region id " << region_id << LL_ENDL; mGetAllRequested.insert(region_id); // prevents subsequent getAll requests for this region mGetAllPending.erase(region_id); // Invalidates region_id }
bool material_type::is_null() const { return ( _ident == material_id( "null" ) ); }