technique_id player::pick_defensive_technique(game *g, monster *z, player *p) { if (blocks_left == 0) return TEC_NULL; int foe_melee_skill = 0; if (z != NULL) foe_melee_skill = z->type->melee_skill; else if (p != NULL) foe_melee_skill = p->dex_cur + p->skillLevel("melee"); int foe_dodge = 0; if (z != NULL) foe_dodge = z->dodge_roll(); else if (p != NULL) foe_dodge = p->dodge_roll(g); int foe_size = 0; if (z) foe_size = 4 + z->type->size * 4; else if (p) { foe_size = 12; if (p->str_max <= 5) foe_size -= 3; if (p->str_max >= 12) foe_size += 3; } blocks_left--; if (weapon.has_technique(TEC_WBLOCK_3) && dice(dex_cur + skillLevel("melee"), 12) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_3; if (weapon.has_technique(TEC_WBLOCK_2) && dice(dex_cur + skillLevel("melee"), 6) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_2; if (weapon.has_technique(TEC_WBLOCK_1) && dice(dex_cur + skillLevel("melee"), 3) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_1; if (weapon.has_technique(TEC_DEF_DISARM, this) && z == NULL && p->weapon.typeId() != "null" && !p->weapon.has_flag(IF_UNARMED_WEAPON) && dice( dex_cur + skillLevel("unarmed"), 8) > dice(p->dex_cur + p->skillLevel("melee"), 10)) return TEC_DEF_DISARM; if (weapon.has_technique(TEC_DEF_THROW, this) && str_cur + skillLevel("melee") >= foe_size + rng(-4, 4) && hit_roll() > rng(1, 5) + foe_dodge && !one_in(3)) return TEC_DEF_THROW; if (weapon.has_technique(TEC_COUNTER, this) && hit_roll() > rng(1, 10) + foe_dodge && !one_in(3)) return TEC_COUNTER; if (weapon.has_technique(TEC_BLOCK_LEGS, this) && (hp_cur[hp_leg_l] >= 20 || hp_cur[hp_leg_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 13) > dice(8 + foe_melee_skill, 10)) return TEC_BLOCK_LEGS; if (weapon.has_technique(TEC_BLOCK, this) && (hp_cur[hp_arm_l] >= 20 || hp_cur[hp_arm_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 16) > dice(6 + foe_melee_skill, 10)) return TEC_BLOCK; blocks_left++; // We didn't use any blocks, so give it back! return TEC_NULL; }
void monster::melee_attack(Creature &target, bool) { mod_moves(-100); if (type->melee_dice == 0) { // We don't attack, so just return return; } add_effect("hit_by_player", 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect("run", 4); } bool u_see_me = g->u_see(this); body_part bp_hit; //int highest_hit = 0; int hitstat = std::max(type->melee_skill - 2,0); int hitroll = dice(hitstat,10); damage_instance damage; if(!is_hallucination()) { if (type->melee_dice > 0) { damage.add_damage(DT_BASH, dice(type->melee_dice,type->melee_sides)); } if (type->melee_cut > 0) { damage.add_damage(DT_CUT, type->melee_cut); } } /* TODO: height-related bodypart selection //If the player is knocked down or the monster can fly, any body part is a valid target if(target.is_on_ground() || has_flag(MF_FLIES)){ highest_hit = 20; } else { switch (type->size) { case MS_TINY: highest_hit = 3; break; case MS_SMALL: highest_hit = 12; break; case MS_MEDIUM: highest_hit = 20; break; case MS_LARGE: highest_hit = 28; break; case MS_HUGE: highest_hit = 35; break; } if (digging()){ highest_hit -= 8; } if (highest_hit <= 1){ highest_hit = 2; } } if (highest_hit > 20){ highest_hit = 20; } int bp_rand = rng(0, highest_hit - 1); if (bp_rand <= 2){ bp_hit = bp_legs; } else if (bp_rand <= 10){ bp_hit = bp_torso; } else if (bp_rand <= 14){ bp_hit = bp_arms; } else if (bp_rand <= 16){ bp_hit = bp_mouth; } else if (bp_rand == 18){ bp_hit = bp_eyes; } else{ bp_hit = bp_head; } */ dealt_damage_instance dealt_dam; int hitspread = target.deal_melee_attack(this, hitroll, false, damage, dealt_dam); bp_hit = dealt_dam.bp_hit; //Hallucinations always produce messages but never actually deal damage if (is_hallucination() || dealt_dam.total_damage() > 0) { if (target.is_player()) { if (u_see_me) { g->add_msg(_("The %1$s hits your %2$s."), name().c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } else { g->add_msg(_("Something hits your %s."), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } } else { if (u_see_me) { g->add_msg(_("The %1$s hits %2$s's %3$s."), name().c_str(), target.disp_name().c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } } } else if (hitspread < 0) { // a miss // TODO: characters practice dodge when a hit misses 'em if (target.is_player()) { if (u_see_me) { g->add_msg(_("You dodge %1$s."), disp_name().c_str()); } else { g->add_msg(_("You dodge an attack from an unseen source.")); } } else { if (u_see_me) { g->add_msg(_("The %1$s dodges %2$s's attack."), name().c_str(), target.disp_name().c_str()); } } } if (is_hallucination()) { if(one_in(7)) { die(); } return; } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust += 15; } if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){ morale_adjust -= 15; } if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
bool spec_random_orc (CHAR_DATA * ch) { char mob_long[MSL]; char mob_name[MSL]; char mob_short[MSL]; int name_range = number_range(1, MAX_RMOB_ORC_NAME - 1); int long_range = number_range(1, MAX_RMOB_ORC_LONG - 1); int other_range = number_range(1, MAX_RMOB_OTHER - 1); if(ch->wimpy != 0) { if(IS_SET(ch->act, ACT_THIEF)) return spec_thief(ch); if(IS_SET(ch->act, ACT_MAGE)) return spec_cast_mage(ch); return false; } /* Wimpy isn't used by mobs, so we'll use this as a check. if Wimpy == 1, dont do the script prevents mobs from changing every 8 pulses. */ ch->wimpy = 1; /* Select Keyword/Short/long */ sprintf (mob_name, "%s orc warrior", rmob_orc_name_table[name_range].name); // Set keywords sprintf (mob_short, "The orc, %s", rmob_orc_name_table[name_range].name); // Set Short_descr sprintf (mob_long, "%s", rmob_orc_long_table[long_range].long_descr); // Set Long_descr /* Now set the names */ ReplaceString(ch->name, mob_name); ReplaceString(ch->short_descr, mob_short); ReplaceString(ch->long_descr, mob_long); // ch->name = str_dup (mob_name); // ch->short_descr = str_dup (mob_short); // ch->long_descr = str_dup (mob_long); /* play with level ranges a bit, dont want static mobs */ int level_range = number_range( rmob_orc_other_table[other_range].low_level_range, rmob_orc_other_table[other_range].hig_level_range ); ch->level += level_range; /* Set hitroll/dam/affected flags */ ch->hitroll = number_range( rmob_orc_other_table[other_range].l_hitroll, rmob_orc_other_table[other_range].h_hitroll ); ch->damroll = number_range( rmob_orc_other_table[other_range].l_damroll, rmob_orc_other_table[other_range].h_damroll ); ch->affected_by = 0; ch->affected_by = rmob_orc_other_table[other_range].affect_flags; // Set Hitpoints/mana/move int hit_points = dice(ch->level, rmob_orc_other_table[other_range].hit_dice); ch->hit = hit_points; ch->max_hit = hit_points; ch->mana = 100; ch->max_mana = 100; ch->move = 100; ch->max_move = 100; // Give them a Randomized Dam message. I would of put this in a table, but thats overkill switch (number_range (1, 3)) { case (1): ch->dam_type = 3; break; /* slash */ case (2): ch->dam_type = 7; break; /* pound */ case (3): ch->dam_type = 11; break; /* pierce */ } // And were done! return TRUE; }
void sounds::process_sound_markers( player *p ) { bool is_deaf = p->is_deaf(); const float volume_multiplier = p->hearing_ability(); const int weather_vol = weather_data( g->weather ).sound_attn; for( const auto &sound_event_pair : sounds_since_last_turn ) { const int volume = sound_event_pair.second.volume * volume_multiplier; const std::string& sfx_id = sound_event_pair.second.id; const std::string& sfx_variant = sound_event_pair.second.variant; const int max_volume = std::max( volume, sound_event_pair.second.volume ); // For deafness checks int dist = rl_dist( p->pos(), sound_event_pair.first ); bool ambient = sound_event_pair.second.ambient; // Too far away, we didn't hear it! if( dist > volume ) { continue; } if( is_deaf ) { // Has to be here as well to work for stacking deafness (loud noises prolong deafness) if( !p->is_immune_effect( effect_deaf ) && rng( ( max_volume - dist ) / 2, ( max_volume - dist ) ) >= 150 ) { // Prolong deafness, but not as much as if it was freshly applied int duration = std::min( 40, ( max_volume - dist - 130 ) / 8 ); p->add_effect( effect_deaf, duration ); if( !p->has_trait( "DEADENED" ) ) { p->add_msg_if_player( m_bad, _( "Your eardrums suddenly ache!" ) ); if( p->pain < 10 ) { p->mod_pain( rng( 0, 2 ) ); } } } // We're deaf, skip rest of processing. continue; } // Player volume meter includes all sounds from their tile and adjacent tiles // TODO: Add noises from vehicle player is in. if( dist <= 1 ) { p->volume = std::max( p->volume, volume ); } // Check for deafness if( !p->is_immune_effect( effect_deaf ) && rng((max_volume - dist) / 2, (max_volume - dist)) >= 150 ) { int duration = (max_volume - dist - 130) / 4; p->add_effect( effect_deaf, duration ); if( p->is_deaf() ) { // Need to check for actual deafness is_deaf = true; sfx::do_hearing_loss( duration ); continue; } } // At this point we are dealing with attention (as opposed to physical effects) // so reduce volume by the amount of ambient noise from the weather. const int mod_vol = ( sound_event_pair.second.volume - weather_vol ) * volume_multiplier; // The noise was drowned out by the surroundings. if( mod_vol - dist < 0 ) { continue; } // See if we need to wake someone up if( p->has_effect( effect_sleep ) ) { if( ( !( p->has_trait( "HEAVYSLEEPER" ) || p->has_trait( "HEAVYSLEEPER2" ) ) && dice( 2, 15 ) < mod_vol - dist ) || ( p->has_trait( "HEAVYSLEEPER" ) && dice( 3, 15 ) < mod_vol - dist ) || ( p->has_trait( "HEAVYSLEEPER2" ) && dice( 6, 15 ) < mod_vol - dist ) ) { //Not kidding about sleep-thru-firefight p->wake_up(); add_msg( m_warning, _( "Something is making noise." ) ); } else { continue; } } const tripoint &pos = sound_event_pair.first; const std::string &description = sound_event_pair.second.description; if( !ambient && ( pos != p->pos() ) && !g->m.pl_sees( pos, dist ) ) { if( p->activity.ignore_trivial != true ) { std::string query; if( description.empty() ) { query = _( "Heard a noise!" ); } else { query = string_format( _( "Heard %s!" ), sound_event_pair.second.description.c_str() ); } if( g->cancel_activity_or_ignore_query( query.c_str() ) ) { p->activity.ignore_trivial = true; for( auto activity : p->backlog ) { activity.ignore_trivial = true; } } } } // Only print a description if it exists if( !description.empty() ) { // If it came from us, don't print a direction if( pos == p->pos() ) { std::string uppercased = description; capitalize_letter( uppercased, 0 ); add_msg( "%s", uppercased.c_str() ); } else { // Else print a direction as well std::string direction = direction_name( direction_from( p->pos(), pos ) ); add_msg( m_warning, _( "From the %s you hear %s" ), direction.c_str(), description.c_str() ); } } // Play the sound effect, if any. if( !sfx_id.empty() ) { // for our sfx API, 100 is "normal" volume, so scale accordingly int heard_volume = sfx::get_heard_volume( pos ); sfx::play_variant_sound( sfx_id, sfx_variant, heard_volume ); //add_msg("Playing sound effect %s, %s, %d", sfx_id.c_str(), sfx_variant.c_str(), heard_volume); } // If Z coord is different, draw even when you can see the source const bool diff_z = pos.z != p->posz(); // Place footstep markers. if( pos == p->pos() || p->sees( pos ) ) { // If we are or can see the source, don't draw a marker. continue; } int err_offset; if( mod_vol / dist < 2 ) { err_offset = 3; } else if( mod_vol / dist < 3 ) { err_offset = 2; } else { err_offset = 1; } // Enumerate the valid points the player *cannot* see. // Unless the source is on a different z-level, then any point is fine std::vector<tripoint> unseen_points; tripoint newp = pos; int &newx = newp.x; int &newy = newp.y; for( newx = pos.x - err_offset; newx <= pos.x + err_offset; newx++ ) { for( newy = pos.y - err_offset; newy <= pos.y + err_offset; newy++ ) { if( diff_z || !p->sees( newp ) ) { unseen_points.emplace_back( newp ); } } } // Then place the sound marker in a random one. if( !unseen_points.empty() ) { sound_markers.emplace( random_entry( unseen_points ), sound_event_pair.second ); } } sounds_since_last_turn.clear(); }
bool map::process_fields_in_submap(game *g, int gridn) { bool found_field = false; field *cur; field_id curtype; for (int locx = 0; locx < SEEX; locx++) { for (int locy = 0; locy < SEEY; locy++) { cur = &(grid[gridn].fld[locx][locy]); int x = locx + SEEX * (gridn % my_MAPSIZE), y = locy + SEEY * int(gridn / my_MAPSIZE); curtype = cur->type; if (!found_field && curtype != fd_null) found_field = true; if (cur->density > 3 || cur->density < 1) debugmsg("Whoooooa density of %d", cur->density); if (cur->age == 0) // Don't process "newborn" fields curtype = fd_null; int part; vehicle *veh; switch (curtype) { case fd_null: break; // Do nothing, obviously. OBVIOUSLY. case fd_blood: case fd_bile: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 250; break; case fd_acid: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 20; for (int i = 0; i < i_at(x, y).size(); i++) { item *melting = &(i_at(x, y)[i]); if (melting->made_of(LIQUID) || melting->made_of(VEGGY) || melting->made_of(FLESH) || melting->made_of(POWDER) || melting->made_of(COTTON) || melting->made_of(WOOL) || melting->made_of(PAPER) || melting->made_of(PLASTIC) || (melting->made_of(GLASS) && !one_in(3)) || one_in(4)) { // Acid destructable objects here melting->damage++; if (melting->damage >= 5 || (melting->made_of(PAPER) && melting->damage >= 3)) { cur->age += melting->volume(); for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } } break; case fd_sap: break; // It doesn't do anything. case fd_fire: { // Consume items as fuel to help us grow/last longer. bool destroyed = false; int vol = 0, smoke = 0, consumed = 0; for (int i = 0; i < i_at(x, y).size() && consumed < cur->density * 2; i++) { destroyed = false; vol = i_at(x, y)[i].volume(); item *it = &(i_at(x, y)[i]); if (it->is_ammo() && it->ammo_type() != AT_BATT && it->ammo_type() != AT_NAIL && it->ammo_type() != AT_BB && it->ammo_type() != AT_BOLT && it->ammo_type() != AT_ARROW) { cur->age /= 2; cur->age -= 600; destroyed = true; smoke += 6; consumed++; } else if (it->made_of(PAPER)) { destroyed = it->burn(cur->density * 3); consumed++; if (cur->density == 1) cur->age -= vol * 10; if (vol >= 4) smoke++; } else if ((it->made_of(WOOD) || it->made_of(VEGGY))) { if (vol <= cur->density * 10 || cur->density == 3) { cur->age -= 4; destroyed = it->burn(cur->density); smoke++; consumed++; } else if (it->burnt < cur->density) { destroyed = it->burn(1); smoke++; } } else if ((it->made_of(COTTON) || it->made_of(WOOL))) { if (vol <= cur->density * 5 || cur->density == 3) { cur->age--; destroyed = it->burn(cur->density); smoke++; consumed++; } else if (it->burnt < cur->density) { destroyed = it->burn(1); smoke++; } } else if (it->made_of(FLESH)) { if (vol <= cur->density * 5 || (cur->density == 3 && one_in(vol / 20))) { cur->age--; destroyed = it->burn(cur->density); smoke += 3; consumed++; } else if (it->burnt < cur->density * 5 || cur->density >= 2) { destroyed = it->burn(1); smoke++; } } else if (it->made_of(LIQUID)) { switch (it->type->id) { // TODO: Make this be not a hack. case itm_whiskey: case itm_vodka: case itm_rum: case itm_tequila: cur->age -= 300; smoke += 6; break; default: cur->age += rng(80 * vol, 300 * vol); smoke++; } destroyed = true; consumed++; } else if (it->made_of(POWDER)) { cur->age -= vol; destroyed = true; smoke += 2; } else if (it->made_of(PLASTIC)) { smoke += 3; if (it->burnt <= cur->density * 2 || (cur->density == 3 && one_in(vol))) { destroyed = it->burn(cur->density); if (one_in(vol + it->burnt)) cur->age--; } } if (destroyed) { for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } veh = &(veh_at(x, y, part)); if (veh->type != veh_null && (veh->parts[part].flags & VHP_FUEL_TANK) && veh->fuel_type == AT_GAS) { if (cur->density > 1 && one_in (8) && veh->fuel > 0) veh->explode (g, x, y); } // Consume the terrain we're on if (has_flag(explodes, x, y)) { ter(x, y) = ter_id(int(ter(x, y)) + 1); cur->age = 0; cur->density = 3; g->explosion(x, y, 40, 0, true); } else if (has_flag(inflammable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; smoke += 15; if (cur->density == 3) ter(x, y) = t_ash; } else if (has_flag(flammable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; smoke += 15; if (cur->density == 3) ter(x, y) = t_rubble; } else if (has_flag(meltable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; if (cur->density == 3) ter(x, y) = t_b_metal; } else if (terlist[ter(x, y)].flags & mfb(swimmable)) cur->age += 800; // Flames die quickly on water // If we consumed a lot, the flames grow higher while (cur->density < 3 && cur->age < 0) { cur->age += 300; cur->density++; } // If the flames are in a pit, it can't spread to non-pit bool in_pit = (ter(x, y) == t_pit); // If the flames are REALLY big, they contribute to adjacent flames if (cur->density == 3 && cur->age < 0) { // Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to int starti = rng(0, 2); int startj = rng(0, 2); for (int i = 0; i < 3 && cur->age < 0; i++) { for (int j = 0; j < 3 && cur->age < 0; j++) { int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1; if (field_at(fx, fy).type == fd_fire && field_at(fx, fy).density < 3 && (!in_pit || ter(fx, fy) == t_pit)) { field_at(fx, fy).density++; field_at(fx, fy).age = 0; cur->age = 0; } } } } // Consume adjacent fuel / terrain to spread. // Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to int starti = rng(0, 2); int startj = rng(0, 2); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1; if (INBOUNDS(fx, fy)) { int spread_chance = 20 * (cur->density - 1) + 10 * smoke; if (has_flag(explodes, fx, fy) && one_in(8 - cur->density)) { ter(fx, fy) = ter_id(int(ter(fx, fy)) + 1); g->explosion(fx, fy, 40, 0, true); } else if ((i != 0 || j != 0) && rng(1, 100) < spread_chance && (!in_pit || ter(fx, fy) == t_pit) && ((cur->density == 3 && (has_flag(flammable, fx, fy) || one_in(20))) || flammable_items_at(fx, fy) || field_at(fx, fy).type == fd_web)) { if (field_at(fx, fy).type == fd_smoke || field_at(fx, fy).type == fd_web) field_at(fx, fy) = field(fd_fire, 1, 0); else add_field(g, fx, fy, fd_fire, 1); } else { bool nosmoke = true; for (int ii = -1; ii <= 1; ii++) { for (int jj = -1; jj <= 1; jj++) { if (field_at(x+ii, y+jj).type == fd_fire && field_at(x+ii, y+jj).density == 3) smoke++; else if (field_at(x+ii, y+jj).type == fd_smoke) nosmoke = false; } } // If we're not spreading, maybe we'll stick out some smoke, huh? if (move_cost(fx, fy) > 0 && (!one_in(smoke) || (nosmoke && one_in(40))) && rng(3, 35) < cur->density * 10 && cur->age < 1000) { smoke--; add_field(g, fx, fy, fd_smoke, rng(1, cur->density)); } } } } } } break; case fd_smoke: for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 50; if (one_in(2)) { std::vector <point> spread; for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if ((field_at(x+a, y+b).type == fd_smoke && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; if (field_at(p.x, p.y).type == fd_smoke && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_smoke, 1)){ cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_tear_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 30; // One in three chance that it spreads (less than smoke!) if (one_in(3)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby teargas grows thicker if (field_at(p.x, p.y).type == fd_tear_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke is converted into teargas } else if (field_at(p.x, p.y).type == fd_smoke) { field_at(p.x, p.y).type = fd_tear_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_tear_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_toxic_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 40; if (one_in(2)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas || field_at(x+a, y+b).type == fd_toxic_gas || field_at(x+a, y+b).type == fd_nuke_gas ) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby toxic gas grows thicker if (field_at(p.x, p.y).type == fd_toxic_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke & teargas is converted into toxic gas } else if (field_at(p.x, p.y).type == fd_smoke || field_at(p.x, p.y).type == fd_tear_gas) { field_at(p.x, p.y).type = fd_toxic_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_toxic_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_nuke_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 40; // Increase long-term radiation in the land underneath radiation(x, y) += rng(0, cur->density); if (one_in(2)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas || field_at(x+a, y+b).type == fd_toxic_gas || field_at(x+a, y+b).type == fd_nuke_gas ) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby nukegas grows thicker if (field_at(p.x, p.y).type == fd_nuke_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke, tear, and toxic gas is converted into nukegas } else if (field_at(p.x, p.y).type == fd_smoke || field_at(p.x, p.y).type == fd_toxic_gas || field_at(p.x, p.y).type == fd_tear_gas) { field_at(p.x, p.y).type = fd_nuke_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_nuke_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_gas_vent: for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { if (field_at(i, j).type == fd_toxic_gas && field_at(i, j).density < 3) field_at(i, j).density++; else add_field(g, i, j, fd_toxic_gas, 3); } } break; case fd_fire_vent: if (cur->density > 1) { if (one_in(3)) cur->density--; } else { cur->type = fd_flame_burst; cur->density = 3; } break; case fd_flame_burst: if (cur->density > 1) cur->density--; else { cur->type = fd_fire_vent; cur->density = 3; } break; case fd_electricity: if (!one_in(5)) { // 4 in 5 chance to spread std::vector<point> valid; if (move_cost(x, y) == 0 && cur->density > 1) { // We're grounded int tries = 0; while (tries < 10 && cur->age < 50) { int cx = x + rng(-1, 1), cy = y + rng(-1, 1); if (move_cost(cx, cy) != 0 && field_at(cx, cy).is_null()) { add_field(g, cx, cy, fd_electricity, 1); cur->density--; tries = 0; } else tries++; } } else { // We're not grounded; attempt to ground for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (move_cost(x + a, y + b) == 0 && // Grounded tiles first field_at(x + a, y + b).is_null()) valid.push_back(point(x + a, y + b)); } } if (valid.size() == 0) { // Spread to adjacent space, then int px = x + rng(-1, 1), py = y + rng(-1, 1); if (move_cost(px, py) > 0 && field_at(px, py).type == fd_electricity && field_at(px, py).density < 3) field_at(px, py).density++; else if (move_cost(px, py) > 0) add_field(g, px, py, fd_electricity, 1); cur->density--; } while (valid.size() > 0 && cur->density > 0) { int index = rng(0, valid.size() - 1); add_field(g, valid[index].x, valid[index].y, fd_electricity, 1); cur->density--; valid.erase(valid.begin() + index); } } } break; case fd_fatigue: if (cur->density < 3 && int(g->turn) % 3600 == 0 && one_in(10)) cur->density++; else if (cur->density == 3 && one_in(600)) { // Spawn nether creature! mon_id type = mon_id(rng(mon_flying_polyp, mon_blank)); monster creature(g->mtypes[type]); creature.spawn(x + rng(-3, 3), y + rng(-3, 3)); g->z.push_back(creature); } break; } cur->age++; if (fieldlist[cur->type].halflife > 0) { if (cur->age > 0 && dice(3, cur->age) > dice(3, fieldlist[cur->type].halflife)) { cur->age = 0; cur->density--; } if (cur->density <= 0) { // Totally dissapated. grid[gridn].field_count--; grid[gridn].fld[locx][locy] = field(); } } } } return found_field; }
int monster::move_to(game *g, int x, int y, bool force) { // Make sure that we can move there, unless force is true. if(!force) if(!g->is_empty(x, y) || !can_move_to(g, x, y)) { return 0; } if (has_effect(ME_BEARTRAP)) { moves = 0; return 0; } if (plans.size() > 0) { plans.erase(plans.begin()); } moves -= calc_movecost(g, posx(), posy(), x, y); //Check for moving into/out of water bool was_water = g->m.is_divable(posx(), posy()); bool will_be_water = g->m.is_divable(x, y); if(was_water && !will_be_water && g->u_see(x, y)) { //Use more dramatic messages for swimming monsters g->add_msg(_("A %s %s from the %s!"), name().c_str(), has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("leaps") : _("emerges"), g->m.tername(posx(), posy()).c_str()); } else if(!was_water && will_be_water && g->u_see(x, y)) { g->add_msg(_("A %s %s into the %s!"), name().c_str(), has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("dives") : _("sinks"), g->m.tername(x, y).c_str()); } setpos(x, y); footsteps(g, x, y); if(is_hallucination()) { //Hallucinations don't do any of the stuff after this point return 1; } if (type->size != MS_TINY && g->m.has_flag("SHARP", posx(), posy()) && !one_in(4)) { hurt(rng(2, 3)); } if (type->size != MS_TINY && g->m.has_flag("ROUGH", posx(), posy()) && one_in(6)) { hurt(rng(1, 2)); } if (!digging() && !has_flag(MF_FLIES) && g->m.tr_at(posx(), posy()) != tr_null) { // Monster stepped on a trap! trap* tr = g->traps[g->m.tr_at(posx(), posy())]; if (dice(3, type->sk_dodge + 1) < dice(3, tr->avoidance)) { trapfuncm f; (f.*(tr->actm))(g, this, posx(), posy()); } } // Diggers turn the dirt into dirtmound if (digging()){ int factor = 0; switch (type->size) { case MS_TINY: factor = 100; break; case MS_SMALL: factor = 30; break; case MS_MEDIUM: factor = 6; break; case MS_LARGE: factor = 3; break; case MS_HUGE: factor = 1; break; } if (has_flag(MF_VERMIN)) { factor *= 100; } if (one_in(factor)) { g->m.ter_set(posx(), posy(), t_dirtmound); } } // Acid trail monsters leave... a trail of acid if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx(), posy(), fd_acid, 3); } if (has_flag(MF_SLUDGETRAIL)) { for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { const int fstr = 3 - (abs(dx) + abs(dy)); if (fstr >= 2) { g->m.add_field(g, posx() + dx, posy() + dy, fd_sludge, fstr); } } } } return 1; }
/* returns 1 if polymorph successful */ int polymon(int mntmp) { boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow, was_blind = !!Blind, dochange = FALSE; boolean could_pass_walls = Passes_walls; int mlvl; if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */ pline("You feel rather %s-ish.",mons[mntmp].mname); exercise(A_WIS, TRUE); return 0; } /* KMH, conduct */ u.uconduct.polyselfs++; if (!Upolyd) { /* Human to monster; save human stats */ u.macurr = u.acurr; u.mamax = u.amax; u.mfemale = flags.female; } else { /* Monster to monster; restore human stats, to be * immediately changed to provide stats for the new monster */ u.acurr = u.macurr; u.amax = u.mamax; flags.female = u.mfemale; } if (youmonst.m_ap_type) { /* stop mimicking immediately */ if (multi < 0) unmul(""); } else if (mons[mntmp].mlet != S_MIMIC) { /* as in polyman() */ youmonst.m_ap_type = M_AP_NOTHING; } if (is_male(&mons[mntmp])) { if (flags.female) dochange = TRUE; } else if (is_female(&mons[mntmp])) { if (!flags.female) dochange = TRUE; } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) { if (!rn2(10)) dochange = TRUE; } if (dochange) { flags.female = !flags.female; pline("You %s %s%s!", (u.umonnum != mntmp) ? "turn into a" : "feel like a new", (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" : flags.female ? "female " : "male ", mons[mntmp].mname); } else { if (u.umonnum != mntmp) pline("You turn into %s!", an(mons[mntmp].mname)); else pline("You feel like a new %s!", mons[mntmp].mname); } if (Stoned && poly_when_stoned(&mons[mntmp])) { /* poly_when_stoned already checked stone golem genocide */ pline("You turn to stone!"); mntmp = PM_STONE_GOLEM; Stoned = 0; delayed_killer = 0; } u.mtimedone = rn1(500, 500); u.umonnum = mntmp; set_uasmon(); /* New stats for monster, to last only as long as polymorphed. * Currently only strength gets changed. */ if (strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100); if (Stone_resistance && Stoned) { /* [email protected] */ Stoned = 0; delayed_killer = 0; pline("You no longer seem to be petrifying."); } if (Sick_resistance && Sick) { make_sick(0L, NULL, FALSE, SICK_ALL); pline("You no longer feel sick."); } if (Slimed) { if (flaming(youmonst.data)) { pline("The slime burns away!"); Slimed = 0L; iflags.botl = 1; } else if (mntmp == PM_GREEN_SLIME) { /* do it silently */ Slimed = 0L; iflags.botl = 1; } } if (nohands(youmonst.data)) Glib = 0; /* mlvl = adj_lev(&mons[mntmp]); * We can't do the above, since there's no such thing as an * "experience level of you as a monster" for a polymorphed character. */ mlvl = (int)mons[mntmp].mlevel; if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) { u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + dice(mlvl,4)); } else if (is_golem(youmonst.data)) { u.mhmax = golemhp(mntmp); } else { if (!mlvl) u.mhmax = rnd(4); else u.mhmax = dice(mlvl, 8); if (is_home_elemental(&u.uz, &mons[mntmp])) u.mhmax *= 3; } u.mh = u.mhmax; if (u.ulevel < mlvl) { /* Low level characters can't become high level monsters for long */ u.mtimedone = u.mtimedone * u.ulevel / mlvl; } if (uskin && mntmp != armor_to_dragon(uskin->otyp)) skinback(FALSE); break_armor(); drop_weapon(1); if (hides_under(youmonst.data)) u.uundetected = OBJ_AT(u.ux, u.uy); else if (youmonst.data->mlet == S_EEL) u.uundetected = is_pool(level, u.ux, u.uy); else u.uundetected = 0; if (u.utraptype == TT_PIT) { if (could_pass_walls && !Passes_walls) { u.utrap = rn1(6,2); } else if (!could_pass_walls && Passes_walls) { u.utrap = 0; } } if (was_blind && !Blind) { /* previous form was eyeless */ Blinded = 1L; make_blinded(0L, TRUE); /* remove blindness */ } newsym(u.ux,u.uy); /* Change symbol */ if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0; else if (sticky && !sticks(youmonst.data)) uunstick(); if (u.usteed) { if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) { char buf[BUFSZ]; pline("No longer petrifying-resistant, you touch %s.", mon_nam(u.usteed)); sprintf(buf, "riding %s", an(u.usteed->data->mname)); instapetrify(buf); } if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY); } if (flags.verbose) { static const char use_thec[] = "Use the command #%s to %s."; static const char monsterc[] = "monster"; if (can_breathe(youmonst.data)) pline(use_thec,monsterc,"use your breath weapon"); if (attacktype(youmonst.data, AT_SPIT)) pline(use_thec,monsterc,"spit venom"); if (youmonst.data->mlet == S_NYMPH) pline(use_thec,monsterc,"remove an iron ball"); if (attacktype(youmonst.data, AT_GAZE)) pline(use_thec,monsterc,"gaze at monsters"); if (is_hider(youmonst.data)) pline(use_thec,monsterc,"hide"); if (is_were(youmonst.data)) pline(use_thec,monsterc,"summon help"); if (webmaker(youmonst.data)) pline(use_thec,monsterc,"spin a web"); if (u.umonnum == PM_GREMLIN) pline(use_thec,monsterc,"multiply in a fountain"); if (is_unicorn(youmonst.data)) pline(use_thec,monsterc,"use your horn"); if (is_mind_flayer(youmonst.data)) pline(use_thec,monsterc,"emit a mental blast"); if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */ pline(use_thec,monsterc,"shriek"); if (lays_eggs(youmonst.data) && flags.female) pline(use_thec,"sit","lay an egg"); } /* you now know what an egg of your type looks like */ if (lays_eggs(youmonst.data)) { learn_egg_type(u.umonnum); /* make queen bees recognize killer bee eggs */ learn_egg_type(egg_type_from_parent(u.umonnum, TRUE)); } find_ac(); if ((!Levitation && !u.ustuck && !Flying && (is_pool(level, u.ux,u.uy) || is_lava(level, u.ux,u.uy))) || (Underwater && !Swimming)) spoteffects(TRUE); if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) { u.utrap = 0; pline("The rock seems to no longer trap you."); } else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) { u.utrap = 0; pline("The lava now feels soothing."); } if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) { if (Punished) { pline("You slip out of the iron chain."); unpunish(); } } if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) && (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) || (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) { pline("You are no longer stuck in the %s.", u.utraptype == TT_WEB ? "web" : "bear trap"); /* probably should burn webs too if PM_FIRE_ELEMENTAL */ u.utrap = 0; } if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) { pline("You orient yourself on the web."); u.utrap = 0; } iflags.botl = 1; vision_full_recalc = 1; see_monsters(); exercise(A_CON, FALSE); exercise(A_WIS, TRUE); encumber_msg(); return 1; }
void faction::randomize() { // Set up values // TODO: Not always in overmap 0,0 omx = 0; omy = 0; mapx = rng(OMAPX / 10, OMAPX - OMAPX / 10); mapy = rng(OMAPY / 10, OMAPY - OMAPY / 10); // Pick an overall goal. goal = faction_goal(rng(1, NUM_FACGOALS - 1)); if (one_in(4)) goal = FACGOAL_NONE; // Slightly more likely to not have a real goal good = facgoal_data[goal].good; strength = facgoal_data[goal].strength; sneak = facgoal_data[goal].sneak; crime = facgoal_data[goal].crime; cult = facgoal_data[goal].cult; job1 = faction_job(rng(1, NUM_FACJOBS - 1)); do job2 = faction_job(rng(0, NUM_FACJOBS - 1)); while (job2 == job1); good += facjob_data[job1].good + facjob_data[job2].good; strength += facjob_data[job1].strength + facjob_data[job2].strength; sneak += facjob_data[job1].sneak + facjob_data[job2].sneak; crime += facjob_data[job1].crime + facjob_data[job2].crime; cult += facjob_data[job1].cult + facjob_data[job2].cult; int num_values = 0; int tries = 0; values = 0; do { int v = rng(1, NUM_FACVALS - 1); if (!has_value(faction_value(v)) && matches_us(faction_value(v))) { values |= mfb(v); tries = 0; num_values++; good += facval_data[v].good; strength += facval_data[v].strength; sneak += facval_data[v].sneak; crime += facval_data[v].crime; cult += facval_data[v].cult; } else tries++; } while((one_in(num_values) || one_in(num_values)) && tries < 15); std::string noun; int sel = 1, best = strength; if (sneak > best) { sel = 2; best = sneak; } if (crime > best) { sel = 3; best = crime; } if (cult > best) sel = 4; if (strength <= 0 && sneak <= 0 && crime <= 0 && cult <= 0) sel = 0; switch (sel) { case 1: noun = faction_noun_strong[rng(0, 14)]; power = dice(5, 20); size = dice(5, 6); break; case 2: noun = faction_noun_sneak [rng(0, 14)]; power = dice(5, 8); size = dice(5, 8); break; case 3: noun = faction_noun_crime [rng(0, 14)]; power = dice(5, 16); size = dice(5, 8); break; case 4: noun = faction_noun_cult [rng(0, 14)]; power = dice(8, 8); size = dice(4, 6); break; default: noun = faction_noun_none [rng(0, 14)]; power = dice(6, 8); size = dice(6, 6); } if (one_in(4)) { do name = _("The ") + noun + _(" of ") + invent_name(); while (name.length() > MAX_FAC_NAME_SIZE); } else if (one_in(2)) { do name = _("The ") + invent_adj() + " " + noun; while (name.length() > MAX_FAC_NAME_SIZE); } else { do { std::string adj; if (good >= 3) adj = faction_adj_pos[rng(0, 14)]; else if (good <= -3) adj = faction_adj_bad[rng(0, 14)]; else adj = faction_adj_neu[rng(0, 14)]; name = _("The ") + adj + " " + noun; if (one_in(4)) name += _(" of ") + invent_name(); } while (name.length() > MAX_FAC_NAME_SIZE); } }
std::string invent_adj() { int syllables = dice(2, 2) - 1; std::string ret, tmp; switch (rng(0, 25)) { case 0: ret = "Ald"; break; case 1: ret = "Brogg"; break; case 2: ret = "Cald"; break; case 3: ret = "Dredd"; break; case 4: ret = "Eld"; break; case 5: ret = "Forr"; break; case 6: ret = "Gugg"; break; case 7: ret = "Horr"; break; case 8: ret = "Ill"; break; case 9: ret = "Jov"; break; case 10: ret = "Kok"; break; case 11: ret = "Lill"; break; case 12: ret = "Moom"; break; case 13: ret = "Nov"; break; case 14: ret = "Orb"; break; case 15: ret = "Perv"; break; case 16: ret = "Quot"; break; case 17: ret = "Rar"; break; case 18: ret = "Suss"; break; case 19: ret = "Torr"; break; case 20: ret = "Umbr"; break; case 21: ret = "Viv"; break; case 22: ret = "Warr"; break; case 23: ret = "Xen"; break; case 24: ret = "Yend"; break; case 25: ret = "Zor"; break; } for (int i = 0; i < syllables - 2; i++) { switch (rng(0, 17)) { case 0: tmp = "al"; break; case 1: tmp = "arn"; break; case 2: tmp = "astr"; break; case 3: tmp = "antr"; break; case 4: tmp = "ent"; break; case 5: tmp = "ell"; break; case 6: tmp = "ev"; break; case 7: tmp = "emm"; break; case 8: tmp = "empr"; break; case 9: tmp = "ill"; break; case 10: tmp = "ial"; break; case 11: tmp = "ior"; break; case 12: tmp = "ordr"; break; case 13: tmp = "oth"; break; case 14: tmp = "omn"; break; case 15: tmp = "uv"; break; case 16: tmp = "ulv"; break; case 17: tmp = "urn"; break; } ret += tmp; } switch (rng(0, 24)) { case 0: tmp = ""; break; case 1: tmp = "al"; break; case 2: tmp = "an"; break; case 3: tmp = "ard"; break; case 4: tmp = "ate"; break; case 5: tmp = "e"; break; case 6: tmp = "ed"; break; case 7: tmp = "en"; break; case 8: tmp = "er"; break; case 9: tmp = "ial"; break; case 10: tmp = "ian"; break; case 11: tmp = "iated"; break; case 12: tmp = "ier"; break; case 13: tmp = "ious"; break; case 14: tmp = "ish"; break; case 15: tmp = "ive"; break; case 16: tmp = "oo"; break; case 17: tmp = "or"; break; case 18: tmp = "oth"; break; case 19: tmp = "old"; break; case 20: tmp = "ous"; break; case 21: tmp = "ul"; break; case 22: tmp = "un"; break; case 23: tmp = "ule"; break; case 24: tmp = "y"; break; } ret += tmp; return ret; }
int player::dodge_roll() { return dice(dodge(), 6); }
int player::hit_mon(game *g, monster *z) { bool is_u = (this == &(g->u)); // Affects how we'll display messages int j; bool can_see = (is_u || g->u_see(posx, posy, j)); std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); // Types of combat (may overlap!) bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(), stabbing = (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)); // Recoil penalty if (recoil <= 30) recoil += 6; // Movement cost moves -= weapon.attack_time() + 20 * encumb(bp_torso); // Different sizes affect your chance to hit if (hit_roll() < z->dodge_roll()) {// A miss! stumble(g); return 0; } if (z->has_flag(MF_SHOCK) && !wearing_something_on(bp_hands) && (unarmed || weapon.conductive())) { if (is_u) g->add_msg("The %s's electric body shocks you!", z->name().c_str()); hurtall(rng(1, 3)); } // For very high hit rolls, we crit! bool critical_hit = (hit_roll() >= 50 + 10 * z->dodge_roll()); int dam = base_damage(true); int cutting_penalty = 0; // Moves lost from getting a cutting weapon stuck // Drunken Master damage bonuses if (has_trait(PF_DRUNKEN) && has_disease(DI_DRUNK)) { // Remember, a single drink gives 600 levels of DI_DRUNK if (unarmed) dam += disease_level(DI_DRUNK) / 250; else dam += disease_level(DI_DRUNK) / 400; } if (unarmed) { // Unarmed bonuses dam += rng(0, sklevel[sk_unarmed]); if (has_trait(PF_TALONS) && z->type->armor - sklevel[sk_unarmed] < 10) { int z_armor = (z->type->armor - sklevel[sk_unarmed]); if (z_armor < 0) z_armor = 0; dam += 10 - z_armor; } } else if (rng(1, 45 - dex_cur) < 2 * sklevel[sk_unarmed] && rng(1, 65 - dex_cur) < 2 * sklevel[sk_unarmed] ) { // If we're not unarmed, there's still a possibility of getting in a bonus // unarmed attack. if (is_u || can_see) { switch (rng(1, 4)) { case 1: g->add_msg("%s kick%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 2: g->add_msg("%s headbutt%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 3: g->add_msg("%s elbow%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 4: g->add_msg("%s knee%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; } } dam += rng(1, sklevel[sk_unarmed]); practice(sk_unarmed, 2); } // Melee skill bonus dam += rng(0, sklevel[sk_melee]); // Bashing damage bonus int bash_dam = weapon.type->melee_dam, bash_cap = 5 + str_cur + sklevel[sk_bashing]; if (bash_dam > bash_cap)// Cap for weak characters bash_dam = (bash_cap * 3 + bash_dam) / 4; if (bashing) bash_dam += rng(0, sklevel[sk_bashing]) * sqrt(str_cur); int bash_min = bash_dam / 4; if (bash_min < sklevel[sk_bashing] * 2) bash_min = sklevel[sk_bashing] * 2; dam += rng(bash_dam / 4, bash_dam); // Take some moves away from the target; at this point it's skill & bash damage z->moves -= rng(0, dam * 2); // Spears treat cutting damage specially. if (weapon.has_weapon_flag(WF_SPEAR) && weapon.type->melee_cut > z->type->armor - int(sklevel[sk_stabbing])) { int z_armor = z->type->armor - int(sklevel[sk_stabbing]); dam += int(weapon.type->melee_cut / 5); int minstab = sklevel[sk_stabbing] * 8 + weapon.type->melee_cut * 2, maxstab = sklevel[sk_stabbing] * 20 + weapon.type->melee_cut * 4; int monster_penalty = rng(minstab, maxstab); if (monster_penalty >= 150) g->add_msg("You force the %s to the ground!", z->name().c_str()); else if (monster_penalty >= 80) g->add_msg("The %s is skewered and flinches!", z->name().c_str()); z->moves -= monster_penalty; cutting_penalty = weapon.type->melee_cut * 4 + z_armor * 8 - dice(sklevel[sk_stabbing], 10); practice(sk_stabbing, 2); // Cutting damage bonus } else if (weapon.type->melee_cut > z->type->armor - int(sklevel[sk_cutting] / 2)) { int z_armor = z->type->armor - int(sklevel[sk_cutting] / 2); if (z_armor < 0) z_armor = 0; dam += weapon.type->melee_cut - z_armor; cutting_penalty = weapon.type->melee_cut * 3 + z_armor * 8 - dice(sklevel[sk_cutting], 10); } if (weapon.has_weapon_flag(WF_MESSY)) { // e.g. chainsaws cutting_penalty /= 6; // Harder to get stuck for (int x = z->posx - 1; x <= z->posx + 1; x++) { for (int y = z->posy - 1; y <= z->posy + 1; y++) { if (!one_in(3)) { if (g->m.field_at(x, y).type == fd_blood && g->m.field_at(x, y).density < 3) g->m.field_at(x, y).density++; else g->m.add_field(g, x, y, fd_blood, 1); } } } } // Bonus attacks! bool shock_them = (!z->has_flag(MF_SHOCK) && has_bionic(bio_shock) && power_level >= 2 && unarmed && one_in(3)); bool drain_them = (has_bionic(bio_heat_absorb) && power_level >= 1 && !is_armed() && z->has_flag(MF_WARM)); bool bite_them = (has_trait(PF_FANGS) && z->armor() < 18 && one_in(20 - dex_cur - sklevel[sk_unarmed])); bool peck_them = (has_trait(PF_BEAK) && z->armor() < 16 && one_in(15 - dex_cur - sklevel[sk_unarmed])); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time // Critical hit effects if (critical_hit) { bool headshot = (!z->has_flag(MF_NOHEAD) && !one_in(3)); // Second chance for shock_them, drain_them, bite_them and peck_them shock_them = (shock_them || (!z->has_flag(MF_SHOCK) && has_bionic(bio_shock)&& power_level >= 2 && unarmed && !one_in(3))); drain_them = (drain_them || (has_bionic(bio_heat_absorb) && !is_armed() && power_level >= 1 && z->has_flag(MF_WARM) && !one_in(3))); bite_them = ( bite_them || (has_trait(PF_FANGS) && z->armor() < 18 && one_in(5))); peck_them = ( peck_them || (has_trait(PF_BEAK) && z->armor() < 16 && one_in(4))); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) { dam += weapon.type->melee_cut; dam += weapon.type->melee_cut * double(sklevel[sk_stabbing] / 10); practice(sk_stabbing, 5); } if (unarmed) { dam += rng(2, 6) * sklevel[sk_unarmed]; if (sklevel[sk_unarmed] > 5) dam += 4 * (sklevel[sk_unarmed - 3]); z->moves -= dam; // Stunning blow if (weapon.type->id == itm_bio_claws) { if (sklevel[sk_cutting] >= 3) dam += 5; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s claws pierce the %s's skull!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s claws stab straight through the %s!", Your.c_str(), z->name().c_str()); } else if (has_trait(PF_TALONS)) { dam += 2; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s talons tear the %s's head open!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s bur%s %s talons into the %s!", You.c_str(),(is_u?"y":"ies"), your.c_str(), z->name().c_str()); } else { headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s crush%s the %s's skull in a single blow!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s deliver%s a crushing punch!",You.c_str(),(is_u ? "" : "s")); } if (z->hp > 0 && rng(1, 5) < sklevel[sk_unarmed]) z->add_effect(ME_STUNNED, 1 + sklevel[sk_unarmed]); } else { // Not unarmed if (bashing) { dam += 8 + (str_cur / 2); int turns_stunned = int(dam / 20) + int(sklevel[sk_bashing] / 2); if (turns_stunned > 6) turns_stunned = 6; z->add_effect(ME_STUNNED, turns_stunned); } if (cutting) { double cut_multiplier = double(sklevel[sk_cutting] / 12); if (cut_multiplier > 1.5) cut_multiplier = 1.5; dam += cut_multiplier * weapon.type->melee_cut; headshot &= z->hp < dam; if (stabbing) { if (headshot && can_see) g->add_msg("%s %s stabs through the %s's skull!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s stab %s %s through the %s!", You.c_str(), your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } else { if (headshot && can_see) g->add_msg("%s %s slices the %s's head off!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else g->add_msg("%s %s cuts the %s deeply!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } } else { // Not cutting, probably bashing headshot &= z->hp < dam; if (headshot && can_see) g->add_msg("%s crush%s the %s's skull!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s crush%s the %s's body!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); } } // End of not-unarmed } // End of critical hit if (shock_them) { power_level -= 2; if (can_see) g->add_msg("%s shock%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); int shock = rng(2, 5); dam += shock * rng(1, 3); z->moves -= shock * 180; } if (drain_them) { charge_power(rng(0, 4)); if (can_see) g->add_msg("%s drain%s the %s's body heat!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += rng(4, 10); z->moves -= rng(80, 120); } if (bite_them) { if (can_see) g->add_msg("%s sink %s fangs into the %s!", You.c_str(), your.c_str(), z->name().c_str()); dam += 18 - z->armor(); } if (peck_them) { if (can_see) g->add_msg("%s peck%s the %s viciously!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += 16 - z->armor(); } // Make a rather quiet sound, to alert any nearby monsters g->sound(posx, posy, 8, ""); // Glass weapons shatter sometimes if (weapon.made_of(GLASS) && rng(0, weapon.volume() + 8) < weapon.volume() + str_cur) { if (can_see) g->add_msg("%s %s shatters!", Your.c_str(), weapon.tname(g).c_str()); g->sound(posx, posy, 16, ""); // Dump its contents on the ground for (int i = 0; i < weapon.contents.size(); i++) g->m.add_item(posx, posy, weapon.contents[i]); hit(g, bp_arms, 1, 0, rng(0, weapon.volume() * 2));// Take damage if (weapon.is_two_handed(this))// Hurt left arm too, if it was big hit(g, bp_arms, 0, 0, rng(0, weapon.volume())); dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra remove_weapon(); } if (dam <= 0) { if (is_u) g->add_msg("You hit the %s, but do no damage.", z->name().c_str()); else if (can_see) g->add_msg("%s's %s hits the %s, but does no damage.", You.c_str(), weapon.tname(g).c_str(), z->name().c_str()); practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 2); if (stabbing) practice(sk_stabbing, 2); return 0; } if (is_u) g->add_msg("You hit the %s for %d damage.", z->name().c_str(), dam); else if (can_see) g->add_msg("%s hits the %s with %s %s.", You.c_str(), z->name().c_str(), (male ? "his" : "her"), (weapon.type->id == 0 ? "fists" : weapon.tname(g).c_str())); practice(sk_melee, rng(5, 10)); if (unarmed) practice(sk_unarmed, rng(5, 10)); if (bashing) practice(sk_bashing, rng(5, 10)); if (cutting) practice(sk_cutting, rng(5, 10)); if (stabbing) practice(sk_stabbing, rng(5, 10)); // Penalize the player if their cutting weapon got stuck if (!unarmed && dam < z->hp && cutting_penalty > dice(str_cur * 2, 20)) { if (is_u) g->add_msg("Your %s gets stuck in the %s, pulling it out of your hands!", weapon.tname().c_str(), z->type->name.c_str()); z->add_item(remove_weapon()); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) z->speed *= .7; else z->speed *= .85; } else { if (dam >= z->hp) { cutting_penalty /= 2; cutting_penalty -= rng(sklevel[sk_cutting], sklevel[sk_cutting] * 2 + 2); } if (cutting_penalty > 0) moves -= cutting_penalty; if (cutting_penalty >= 50 && is_u) g->add_msg("Your %s gets stuck in the %s, but you yank it free.", weapon.tname().c_str(), z->type->name.c_str()); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) z->speed *= .9; } return dam; }
/* cutworm() * * Check for mon->wormno before calling this function! * * When hitting a worm (worm) at position x, y, with a weapon (weap), * there is a chance that the worm will be cut in half, and a chance * that both halves will survive. */ void cutworm(struct monst *worm, xchar x, xchar y, struct obj *weap) { struct wseg *curr, *new_tail; struct monst *new_worm; int wnum = worm->wormno; int cut_chance, new_wnum; if (!wnum) return; /* bullet proofing */ if (x == worm->mx && y == worm->my) return; /* hit on head */ /* cutting goes best with a bladed weapon */ cut_chance = rnd(20); /* Normally 1-16 does not cut */ /* Normally 17-20 does */ if (weap && objects[weap->otyp].oc_dir & SLASH) cut_chance += 10; /* With a slashing weapon 1- 6 does not cut */ /* 7-20 does */ if (cut_chance < 17) return; /* not good enough */ /* Find the segment that was attacked. */ curr = level->wtails[wnum]; while ((curr->wx != x) || (curr->wy != y)) { curr = curr->nseg; if (!curr) { impossible("cutworm: no segment at (%d,%d)", (int)x, (int)y); return; } } /* If this is the tail segment, then the worm just loses it. */ if (curr == level->wtails[wnum]) { shrink_worm(wnum); return; } /* * Split the worm. The tail for the new worm is the old worm's tail. * The tail for the old worm is the segment that follows "curr", * and "curr" becomes the dummy segment under the new head. */ new_tail = level->wtails[wnum]; level->wtails[wnum] = curr->nseg; curr->nseg = NULL; /* split the worm */ /* * At this point, the old worm is correct. Any new worm will have * it's head at "curr" and its tail at "new_tail". */ /* Sometimes the tail end dies. */ if (rn2(3) || !(new_wnum = get_wormno(level)) || !worm->m_lev) { cutoff(worm, new_tail); return; } remove_monster(level, x, y); /* clone_mon puts new head here */ if (!(new_worm = clone_mon(worm, x, y))) { cutoff(worm, new_tail); return; } new_worm->wormno = new_wnum; /* affix new worm number */ /* Devalue the monster level of both halves of the worm. */ worm->m_lev = ((unsigned)worm->m_lev <= 3) ? (unsigned)worm->m_lev : max((unsigned)worm->m_lev - 2, 3); new_worm->m_lev = worm->m_lev; /* Calculate the mhp on the new_worm for the (lower) monster level. */ new_worm->mhpmax = new_worm->mhp = dice((int)new_worm->m_lev, 8); /* Calculate the mhp on the old worm for the (lower) monster level. */ if (worm->m_lev > 3) { worm->mhpmax = dice((int)worm->m_lev, 8); if (worm->mhpmax < worm->mhp) worm->mhp = worm->mhpmax; } level->wtails[new_wnum] = new_tail; /* We've got all the info right now */ level->wheads[new_wnum] = curr; /* so we can do this faster than */ level->wgrowtime[new_wnum] = 0L; /* trying to call initworm(). */ /* Place the new monster at all the segment locations. */ place_wsegs(new_worm); if (flags.mon_moving) pline(msgc_monneutral, "%s is cut in half.", Monnam(worm)); else pline(msgc_combatgood, "You cut %s in half.", mon_nam(worm)); }
void game::complete_craft() { recipe making = recipes[u.activity.index]; // Which recipe is it? std::vector<component> will_use; // List of all items we're using, w/ count // Up to 5 components / tools for (int i = 0; i < 5; i++) { if (making.components[i].size() > 0) { // For each set of components in the recipe, fill you_have with the list of all // matching ingredients the player has. std::vector<component> you_have; for (int j = 0; j < making.components[i].size(); j++) { if (u.has_amount(making.components[i][j].type, making.components[i][j].count)) you_have.push_back(making.components[i][j]); } if (you_have.size() == 1) // Only one, so we'll definitely use it will_use.push_back(component(you_have[0].type, you_have[0].count)); else { // Let the player pick which component they want to use WINDOW* w = newwin(you_have.size() + 2, 30, 10, 25); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); mvwprintz(w, 0, 5, c_red, "Use which component?"); for (int j = 0; j < you_have.size(); j++) mvwprintz(w, j + 1, 1, c_white, "%d: %s", j + 1, itypes[you_have[j].type]->name.c_str()); wrefresh(w); char ch; do ch = getch(); while (ch < '1' || ch >= '1' + you_have.size()); ch -= '1'; will_use.push_back(component(you_have[ch].type, you_have[ch].count)); delwin(w); } } // Done looking at components // Use charges of any tools that require charges used if (making.tools[i].size() > 0) { for (int j = 0; j < making.tools[i].size(); j++) { if (making.tools[i][j].count > 0) u.use_charges(making.tools[i][j].type, making.tools[i][j].count); } } } // Done finding the components/tools needed // # of dice is 75% primary skill, 25% secondary (unless secondary is null) int skill_dice = u.sklevel[making.sk_primary] * 3; if (making.sk_secondary == sk_null) skill_dice += u.sklevel[making.sk_primary]; else skill_dice += u.sklevel[making.sk_secondary]; // Sides on dice is 16 plus your current intelligence int skill_sides = 16 + u.int_cur; int diff_dice = making.difficulty * 4; // Since skill level is * 4 also int diff_sides = 24; // 16 + 8 (default intelligence) int skill_roll = dice(skill_dice, skill_sides); int diff_roll = dice(diff_dice, diff_sides); if (making.sk_primary != sk_null) u.practice(making.sk_primary, making.difficulty * 5 + 20); if (making.sk_secondary != sk_null) u.practice(making.sk_secondary, 5); // Messed up badly; waste some components. if (making.difficulty != 0 && diff_roll > skill_roll * (1 + 0.1 * rng(1, 5))) { add_msg("You fail to make the %s, and waste some materials.", itypes[making.result]->name.c_str()); int num_lost = rng(1, will_use.size()); for (int i = 0; i < num_lost; i++) { int n = rng(0, will_use.size() - 1); if (itypes[will_use[n].type]->is_ammo() && will_use[i].type != itm_gasoline) u.use_charges(will_use[n].type, will_use[n].count); else u.use_amount(will_use[n].type, will_use[n].count); will_use.erase(will_use.begin() + n); } u.activity.type = ACT_NULL; return; // Messed up slightly; no components wasted. } else if (diff_roll > skill_roll) { add_msg("You fail to make the %s, but don't waste any materials.", itypes[making.result]->name.c_str()); u.activity.type = ACT_NULL; return; } // If we're here, the craft was a success! // Use up the items in will_use for (int i = 0; i < will_use.size(); i++) { if (itypes[will_use[i].type]->is_ammo() && will_use[i].type != itm_gasoline) u.use_charges(will_use[i].type, will_use[i].count); else u.use_amount(will_use[i].type, will_use[i].count); } // Set up the new item, and pick an inventory letter int iter = 0; item newit(itypes[making.result], turn, nextinv); do { newit.invlet = nextinv; advance_nextinv(); iter++; } while (u.has_item(newit.invlet) && iter < 52); newit = newit.in_its_container(&itypes); // We might not have space for the item if (iter == 52 || u.volume_carried()+newit.volume() > u.volume_capacity()) { add_msg("There's no room in your inventory for the %s, so you drop it.", newit.tname().c_str()); m.add_item(u.posx, u.posy, newit); } else if (u.weight_carried() + newit.volume() > u.weight_capacity()) { add_msg("The %s is too heavy to carry, so you drop it.", newit.tname().c_str()); m.add_item(u.posx, u.posy, newit); } else { u.i_add(newit); add_msg("%c - %s", newit.invlet, newit.tname().c_str()); } }
bool game::grabbed_veh_move( const tripoint &dp ) { const optional_vpart_position grabbed_vehicle_vp = m.veh_at( u.pos() + u.grab_point ); if( !grabbed_vehicle_vp ) { add_msg( m_info, _( "No vehicle at grabbed point." ) ); u.grab( OBJECT_NONE ); return false; } vehicle *grabbed_vehicle = &grabbed_vehicle_vp->vehicle(); const int grabbed_part = grabbed_vehicle_vp->part_index(); const vehicle *veh_under_player = veh_pointer_or_null( m.veh_at( u.pos() ) ); if( grabbed_vehicle == veh_under_player ) { u.grab_point = -dp; return false; } tripoint dp_veh = -u.grab_point; const tripoint prev_grab = u.grab_point; tripoint next_grab = u.grab_point; bool zigzag = false; if( dp == prev_grab ) { // We are pushing in the direction of vehicle dp_veh = dp; } else if( abs( dp.x + dp_veh.x ) != 2 && abs( dp.y + dp_veh.y ) != 2 ) { // Not actually moving the vehicle, don't do the checks u.grab_point = -( dp + dp_veh ); return false; } else if( ( dp.x == prev_grab.x || dp.y == prev_grab.y ) && next_grab.x != 0 && next_grab.y != 0 ) { // Zig-zag (or semi-zig-zag) pull: player is diagonal to vehicle // and moves away from it, but not directly away dp_veh.x = ( dp.x == -dp_veh.x ) ? 0 : dp_veh.x; dp_veh.y = ( dp.y == -dp_veh.y ) ? 0 : dp_veh.y; next_grab = -dp_veh; zigzag = true; } else { // We are pulling the vehicle next_grab = -dp; } // Make sure the mass and pivot point are correct grabbed_vehicle->invalidate_mass(); //vehicle movement: strength check int mc = 0; int str_req = ( grabbed_vehicle->total_mass() / 25_kilogram ); //strength required to move vehicle. //if vehicle is rollable we modify str_req based on a function of movecost per wheel. // Vehicle just too big to grab & move; 41-45 lets folks have a bit of a window // (Roughly 1.1K kg = danger zone; cube vans are about the max) if( str_req > 45 ) { add_msg( m_info, _( "The %s is too bulky for you to move by hand." ), grabbed_vehicle->name ); return true; // No shoving around an RV. } const auto &wheel_indices = grabbed_vehicle->wheelcache; if( grabbed_vehicle->valid_wheel_config() ) { //determine movecost for terrain touching wheels const tripoint vehpos = grabbed_vehicle->global_pos3(); for( int p : wheel_indices ) { const tripoint wheel_pos = vehpos + grabbed_vehicle->parts[p].precalc[0]; const int mapcost = m.move_cost( wheel_pos, grabbed_vehicle ); mc += ( str_req / wheel_indices.size() ) * mapcost; } //set strength check threshold //if vehicle has many or only one wheel (shopping cart), it is as if it had four. if( wheel_indices.size() > 4 || wheel_indices.size() == 1 ) { str_req = mc / 4 + 1; } else { str_req = mc / wheel_indices.size() + 1; } } else { str_req++; //if vehicle has no wheels str_req make a noise. if( str_req <= u.get_str() ) { sounds::sound( grabbed_vehicle->global_pos3(), str_req * 2, sounds::sound_t::movement, _( "a scraping noise." ), true, "misc", "scraping" ); } } //final strength check and outcomes ///\EFFECT_STR determines ability to drag vehicles if( str_req <= u.get_str() ) { //calculate exertion factor and movement penalty ///\EFFECT_STR increases speed of dragging vehicles u.moves -= 100 * str_req / std::max( 1, u.get_str() ); const int ex = dice( 1, 3 ) - 1 + str_req; if( ex > u.get_str() ) { add_msg( m_bad, _( "You strain yourself to move the %s!" ), grabbed_vehicle->name ); u.moves -= 200; u.mod_pain( 1 ); } else if( ex == u.get_str() ) { u.moves -= 200; add_msg( _( "It takes some time to move the %s." ), grabbed_vehicle->name ); } } else { u.moves -= 100; add_msg( m_bad, _( "You lack the strength to move the %s" ), grabbed_vehicle->name ); return true; } std::string blocker_name = _( "errors in movement code" ); const auto get_move_dir = [&]( const tripoint & dir, const tripoint & from ) { tileray mdir; mdir.init( dir.x, dir.y ); grabbed_vehicle->turn( mdir.dir() - grabbed_vehicle->face.dir() ); grabbed_vehicle->face = grabbed_vehicle->turn_dir; grabbed_vehicle->precalc_mounts( 1, mdir.dir(), grabbed_vehicle->pivot_point() ); // Grabbed part has to stay at distance 1 to the player // and in roughly the same direction. const tripoint new_part_pos = grabbed_vehicle->global_pos3() + grabbed_vehicle->parts[ grabbed_part ].precalc[ 1 ]; const tripoint expected_pos = u.pos() + dp + from; const tripoint actual_dir = expected_pos - new_part_pos; // Set player location to illegal value so it can't collide with vehicle. const tripoint player_prev = u.pos(); u.setpos( tripoint_zero ); std::vector<veh_collision> colls; const bool failed = grabbed_vehicle->collision( colls, actual_dir, true ); u.setpos( player_prev ); if( !colls.empty() ) { blocker_name = colls.front().target_name; } return failed ? tripoint_zero : actual_dir; }; // First try the move as intended // But if that fails and the move is a zig-zag, try to recover: // Try to place the vehicle in the position player just left rather than "flattening" the zig-zag tripoint final_dp_veh = get_move_dir( dp_veh, next_grab ); if( final_dp_veh == tripoint_zero && zigzag ) { final_dp_veh = get_move_dir( -prev_grab, -dp ); next_grab = -dp; } if( final_dp_veh == tripoint_zero ) { add_msg( _( "The %s collides with %s." ), grabbed_vehicle->name, blocker_name ); u.grab_point = prev_grab; return true; } u.grab_point = next_grab; tripoint gp = grabbed_vehicle->global_pos3(); grabbed_vehicle = m.displace_vehicle( gp, final_dp_veh ); if( grabbed_vehicle == nullptr ) { debugmsg( "Grabbed vehicle disappeared" ); return false; } for( int p : wheel_indices ) { if( one_in( 2 ) ) { tripoint wheel_p = grabbed_vehicle->global_part_pos3( grabbed_part ); grabbed_vehicle->handle_trap( wheel_p, p ); } } return false; }
int main (int argc, char** argv) { if(argc != 4+1 ) { std::cerr<<" The function :"<<argv[0]<<" should be called with the following parameters: "<< std::endl <<" config_file Puddle_Height[eV] Puddle_Concentration[0-1] Puddle_Range[lat_unit]."<<std::endl; return 0; } //Read the name of the configuration file const std::string config_filename(argv[1]); const double puddHeight=atof(argv[2]); const double puddConcen=atof(argv[3]); const double puddRange =atof(argv[4]); std::ifstream config_file(config_filename.c_str()); ///Look for the line lattice_info std::string line; bool found=false; for (int line_num = 0; std::getline(config_file, line); ++line_num) if(line=="lattice_info") { found=true; break; } if(!found) { config_file.close(); std::cerr<<"The config file does not posses a disorder_info section. The simulation cannot proceed"<<std::endl; return 0; } std::string label; int OrbitalNum=0; int SpinNum=0; int coord=0; std::vector<int> SitesInDir(3); std::vector < std::vector<double> > lat(3); for(int i=0;i<3;i++) lat[i]=std::vector<double>(3,0); config_file >>label >>lat[0][0]>>lat[0][1]>>lat[0][2] >>lat[1][0]>>lat[1][1]>>lat[1][2] >>lat[2][0]>>lat[2][1]>>lat[2][2] >>SitesInDir[0]>>SitesInDir[1]>>SitesInDir[2] >>OrbitalNum>>SpinNum>>coord; const double Delta[] = { (lat[0][0]+lat[1][0])/3. , (lat[0][1]+lat[1][1])/3. , 0 }; // THIRD LATTICE VECTOR std::string output_name=label+"PuddleHeight"+ argv[2]+"eVConc"+argv[3]+ "Range"+argv[4]+"nm.dis"; std::ofstream output_file(output_name.c_str()); const double epsilon=std::numeric_limits<double>::epsilon(); if(puddHeight <= epsilon || puddConcen <= epsilon) { output_file.close(); return 0; } boost::random::mt19937 rng; // produces randomness out of thin air boost::random::uniform_01<double> dice; boost::random::uniform_real_distribution<double> RandomEnergy( -1.0 , 1.0); ///Number of orbitals in the simulation const int TotalOfOrbitals=SitesInDir[0]*SitesInDir[0]*SitesInDir[2]*OrbitalNum*SpinNum; ///Stimated number of impurities const int estImpNum= (1.3)*puddConcen * TotalOfOrbitals/SpinNum;//The two is to overstimate the value std::vector< std::vector< double > > impPosition; impPosition.reserve( estImpNum ); std::cout<<"The estimated number of puddles is set as: "<<estImpNum<<std::endl; std::vector< double > this_imp_pos(3); //a singl impurity position int impurity_count= 0; // first we go through all the possible positions for(int i2=0; i2 < SitesInDir[2] ; i2++ ) for(int i1=0; i1 < SitesInDir[1] ; i1++ ) for(int i0=0; i0 < SitesInDir[0] ; i0++ ) for(int io=0; io < OrbitalNum ; io++ ) if( dice(rng) < puddConcen ) { this_imp_pos[0]=lat[0][0]*i0 + lat[1][0]*i1 + lat[2][0]*i2 + io*Delta[0]; this_imp_pos[1]=lat[0][1]*i0 + lat[1][1]*i1 + lat[2][1]*i2 + io*Delta[1]; this_imp_pos[1]=lat[0][2]*i0 + lat[1][2]*i1 + lat[2][2]*i2 + io*Delta[2]; impPosition.push_back(this_imp_pos); } int counter=0; std::vector<double> diagElements( TotalOfOrbitals ,0 ); //We incorporate the onsite energy of the puddles. for(int i2=0; i2 < SitesInDir[2] ; i2++ ) for(int i1=0; i1 < SitesInDir[1] ; i1++ ) for(int i0=0; i0 < SitesInDir[0] ; i0++ ) for(int io=0; io < OrbitalNum ; io++ ) { for(int it0=-1; it0 <= 1; it0++ ) // tile index 1 for(int it1=-1; it1 <= 1; it1++ ) // tile index 2. This is used to take into account the periodic boundary condition { const int I0=i0+SitesInDir[0]*it0 ; const int I1=i1+SitesInDir[1]*it1 ; const double r[2]={ lat[0][0]*I0+lat[1][0]*I1+io*Delta[0], lat[0][1]*I0+lat[1][1]*I1+io*Delta[1] }; for(int imp=0;imp< impPosition.size(); imp++) { const double dist = ( r[0]-impPosition[imp][0] )*( r[0]-impPosition[imp][0] ) + ( r[1]-impPosition[imp][1] )*( r[1]-impPosition[imp][1] ); const double Ei=RandomEnergy(rng)*puddHeight*exp(-0.5*dist/puddRange/puddRange); //Created for periodic puddles if( std::abs(Ei) > epsilon ) for(int is=0; is <SpinNum ; is++ ) { //compute the site where the energy is going to be changed const int k0= IndexesToIndex( I0, SitesInDir[0], I1, SitesInDir[1], i2, SitesInDir[2], io, OrbitalNum, is, SpinNum); diagElements[k0]=diagElements[k0]+Ei; } } } ++counter; const int checkpoint= int((double)TotalOfOrbitals/SpinNum/100); if(counter%checkpoint == 0 ) std::cout<<"Setting the puddles, there are :"<<counter<<"/"<<TotalOfOrbitals/SpinNum<<" left."<<std::endl; } // std::cout<<"Setting the hamiltonian in the sparse matrix"<<std::endl; double mean=0; double stdDev=0; for(int k0=0;k0<diagElements.size();k0++) if(std::abs(diagElements[k0])>epsilon) { output_file<<k0<<" "<<diagElements[k0]<<std::endl; mean=mean+diagElements[k0]; stdDev= stdDev +diagElements[k0]*diagElements[k0]; } mean=mean/diagElements.size(); stdDev=stdDev/diagElements.size(); stdDev=sqrt(stdDev- mean*mean); /* for(int k0=0;k0<diagElements.size();k0++) if(std::abs(diagElements[k0])>epsilon) stdDev=stdDev+pow(mean-diagElements[k0],2.); stdDev=sqrt(stdDev/diagElements.size()); */ std::cout<<"The puddle have mean:"<<mean<<" StdDev: "<<stdDev<<std::endl; output_file.close(); return 0;}
void weather_change(void) { int diff = 0, sky_change, temp_change, i, grainlevel = 0, gsnowlevel = 0, icelevel = 0, snowdec = 0, raincast = 0, snowcast = 0, avg_day_temp, avg_week_temp, cweather_type = 0, temp; weather_info.hours_go++; if (weather_info.hours_go > HOURS_PER_DAY) { weather_info.press_last_day = weather_info.press_last_day * (HOURS_PER_DAY - 1) / HOURS_PER_DAY; weather_info.temp_last_day = weather_info.temp_last_day * (HOURS_PER_DAY - 1) / HOURS_PER_DAY; } // Average pressure and temperature per 24 hours weather_info.press_last_day += weather_info.pressure; weather_info.temp_last_day += weather_info.temperature; if (weather_info.hours_go > (DAYS_PER_WEEK * HOURS_PER_DAY)) { weather_info.press_last_week = weather_info.press_last_week * (DAYS_PER_WEEK * HOURS_PER_DAY - 1) / (DAYS_PER_WEEK * HOURS_PER_DAY); weather_info.temp_last_week = weather_info.temp_last_week * (DAYS_PER_WEEK * HOURS_PER_DAY - 1) / (DAYS_PER_WEEK * HOURS_PER_DAY); } // Average pressure and temperature per week weather_info.press_last_week += weather_info.pressure; weather_info.temp_last_week += weather_info.temperature; avg_day_temp = average_day_temp(); avg_week_temp = average_week_temp(); calc_basic(weather_info.weather_type, weather_info.sky, &grainlevel, &gsnowlevel); // Ice and show change by temperature if (!(time_info.hours % 6) && weather_info.hours_go) { if (avg_day_temp < -15) icelevel += 4; else if (avg_day_temp < -10) icelevel += 3; else if (avg_day_temp < -5) icelevel += 2; else if (avg_day_temp < -1) icelevel += 1; else if (avg_day_temp < 1) { icelevel += 0; gsnowlevel -= 1; snowdec += 1; } else if (avg_day_temp < 5) { icelevel -= 1; gsnowlevel -= 2; snowdec += 2; } else if (avg_day_temp < 10) { icelevel -= 2; gsnowlevel -= 3; snowdec += 3; } else { icelevel -= 3; gsnowlevel -= 4; snowdec += 4; } } weather_info.icelevel = MAX(0, MIN(100, weather_info.icelevel + icelevel)); if (gsnowlevel < -1 && weather_info.rainlevel < 20) weather_info.rainlevel -= (gsnowlevel / 2); weather_info.snowlevel = MAX(0, MIN(120, weather_info.snowlevel + gsnowlevel)); weather_info.rainlevel = MAX(0, MIN(80, weather_info.rainlevel + grainlevel)); // Change some values for world for (i = FIRST_ROOM; i <= top_of_world; i++) { raincast = snowcast = 0; if (ROOM_FLAGGED(i, ROOM_NOWEATHER)) continue; if (world[i]->weather.duration) { calc_basic(world[i]->weather.weather_type, world[i]->weather.sky, &raincast, &snowcast); snowcast -= snowdec; } else { raincast = grainlevel; snowcast = gsnowlevel; } if (world[i]->weather.duration <= 0) world[i]->weather.duration = 0; else world[i]->weather.duration--; world[i]->weather.icelevel = MAX(0, MIN(100, world[i]->weather.icelevel + icelevel)); if (snowcast < -1 && world[i]->weather.rainlevel < 20) world[i]->weather.rainlevel -= (snowcast / 2); world[i]->weather.snowlevel = MAX(0, MIN(120, world[i]->weather.snowlevel + snowcast)); world[i]->weather.rainlevel = MAX(0, MIN(80, world[i]->weather.rainlevel + raincast)); } switch (time_info.month) { case 0: // Jan diff = (weather_info.pressure > 985 ? -2 : 2); break; case 1: // Feb diff = (weather_info.pressure > 985 ? -2 : 2); break; case 2: // Mar diff = (weather_info.pressure > 985 ? -2 : 2); break; case 3: // Apr diff = (weather_info.pressure > 985 ? -2 : 2); break; case 4: // May diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 5: // Jun diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 6: // Jul diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 7: // Aug diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 8: // Sep diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 9: // Oct diff = (weather_info.pressure > 1015 ? -2 : 2); break; case 10: // Nov diff = (weather_info.pressure > 985 ? -2 : 2); break; case 11: // Dec diff = (weather_info.pressure > 985 ? -2 : 2); break; default: break; } // if ((time_info.month >= 9) && (time_info.month <= 16)) // diff = (weather_info.pressure > 985 ? -2 : 2); // else // diff = (weather_info.pressure > 1015 ? -2 : 2); weather_info.change += (dice(1, 4) * diff + dice(2, 6) - dice(2, 6)); weather_info.change = MIN(weather_info.change, 12); weather_info.change = MAX(weather_info.change, -12); weather_info.pressure += weather_info.change; weather_info.pressure = MIN(weather_info.pressure, 1040); weather_info.pressure = MAX(weather_info.pressure, 960); if (time_info.month == MONTH_MAY) weather_info.season = SEASON_SPRING; else if (time_info.month >= MONTH_JUNE && time_info.month <= MONTH_AUGUST) weather_info.season = SEASON_SUMMER; else if (time_info.month >= MONTH_SEPTEMBER && time_info.month <= MONTH_OCTOBER) weather_info.season = SEASON_AUTUMN; else if (time_info.month >= MONTH_DECEMBER || time_info.month <= MONTH_FEBRUARY) weather_info.season = SEASON_WINTER; switch (weather_info.season) { case SEASON_WINTER: if ((time_info.month == MONTH_MART && avg_week_temp > 5 && weather_info.snowlevel == 0) || (time_info.month == MONTH_APRIL && weather_info.snowlevel == 0)) weather_info.season = SEASON_SPRING; case SEASON_AUTUMN: if (time_info.month == MONTH_NOVEMBER && (avg_week_temp < 2 || weather_info.snowlevel >= 5)) weather_info.season = SEASON_WINTER; } sky_change = 0; temp_change = 0; switch (weather_info.sky) { case SKY_CLOUDLESS: if (weather_info.pressure < 990) sky_change = 1; else if (weather_info.pressure < 1010) if (dice(1, 4) == 1) sky_change = 1; break; case SKY_CLOUDY: if (weather_info.pressure < 970) sky_change = 2; else if (weather_info.pressure < 990) { if (dice(1, 4) == 1) sky_change = 2; else sky_change = 0; } else if (weather_info.pressure > 1030) if (dice(1, 4) == 1) sky_change = 3; break; case SKY_RAINING: if (weather_info.pressure < 970) { if (dice(1, 4) == 1) sky_change = 4; else sky_change = 0; } else if (weather_info.pressure > 1030) sky_change = 5; else if (weather_info.pressure > 1010) if (dice(1, 4) == 1) sky_change = 5; break; case SKY_LIGHTNING: if (weather_info.pressure > 1010) sky_change = 6; else if (weather_info.pressure > 990) if (dice(1, 4) == 1) sky_change = 6; break; default: sky_change = 0; weather_info.sky = SKY_CLOUDLESS; break; } switch (sky_change) { case 1: // CLOUDLESS -> CLOUDY if (time_info.month >= MONTH_MAY && time_info.month <= MONTH_AUGUST) { if (time_info.hours >= 8 && time_info.hours <= 16) temp_change += number(-3, -1); else if (time_info.hours >= 5 && time_info.hours <= 20) temp_change += number(-1, 0); } else temp_change += number(-2, + 2); break; case 2: // CLOUDY -> RAINING if (time_info.month >= MONTH_MAY && time_info.month <= MONTH_AUGUST) temp_change += number(-1, + 1); else temp_change += number(-2, + 2); break; case 3: // CLOUDY -> CLOUDLESS if (time_info.month >= MONTH_MAY && time_info.month <= MONTH_AUGUST) { if (time_info.hours >= 7 && time_info.hours <= 19) temp_change += number( + 1, + 2); else temp_change += number(0, + 1); } else temp_change += number(-1, + 1); break; case 4: // RAINING -> LIGHTNING if (time_info.month >= MONTH_MAY && time_info.month <= MONTH_AUGUST) { if (time_info.hours >= 10 && time_info.hours <= 18) temp_change += number( + 1, + 3); else temp_change += number(0, + 2); } else temp_change += number(-3, + 3); break; case 5: // RAINING -> CLOUDY if (time_info.month >= MONTH_JUNE && time_info.month <= MONTH_AUGUST) temp_change += number(0, + 1); else temp_change += number(-1, + 1); break; case 6: // LIGHTNING -> RAINING if (time_info.month >= MONTH_MAY && time_info.month <= MONTH_AUGUST) { if (time_info.hours >= 10 && time_info.hours <= 17) temp_change += number(-3, + 1); else temp_change += number(-1, + 2); } else temp_change += number( + 1, + 3); break; case 0: default: if (dice(1, 4) == 1) temp_change += number(-1, + 1); break; } temp_change += day_temp_change[time_info.hours][weather_info.sky]; if (time_info.day >= 22) { temp = weather_info.temperature + (time_info.month >= MONTH_DECEMBER ? year_temp[0].med : year_temp[time_info.month + 1].med); temp /= 2; } else if (time_info.day <= 8) { temp = weather_info.temperature + year_temp[time_info.month].med; temp /= 2; } else temp = weather_info.temperature; temp += temp_change; cweather_type = 0; *buf = '\0'; if (weather_info.temperature - temp > 6) { strcat(buf, "Резкое похолодание.\r\n"); SET_BIT(cweather_type, WEATHER_QUICKCOOL); } else if (weather_info.temperature - temp < -6) { strcat(buf, "Резкое потепление.\r\n"); SET_BIT(cweather_type, WEATHER_QUICKHOT); } weather_info.temperature = MIN(year_temp[time_info.month].max, MAX(year_temp[time_info.month].min, temp)); if (weather_info.change >= 10 || weather_info.change <= -10) { strcat(buf, "Сильный ветер.\r\n"); SET_BIT(cweather_type, WEATHER_BIGWIND); } else if (weather_info.change >= 6 || weather_info.change <= -6) { strcat(buf, "Умеренный ветер.\r\n"); SET_BIT(cweather_type, WEATHER_MEDIUMWIND); } else if (weather_info.change >= 2 || weather_info.change <= -2) { strcat(buf, "Слабый ветер.\r\n"); SET_BIT(cweather_type, WEATHER_LIGHTWIND); } else if (IS_SET(weather_info.weather_type, WEATHER_BIGWIND | WEATHER_MEDIUMWIND | WEATHER_LIGHTWIND)) { strcat(buf, "Ветер утих.\r\n"); if (IS_SET(weather_info.weather_type, WEATHER_BIGWIND)) SET_BIT(cweather_type, WEATHER_MEDIUMWIND); else if (IS_SET(weather_info.weather_type, WEATHER_MEDIUMWIND)) SET_BIT(cweather_type, WEATHER_LIGHTWIND); } switch (sky_change) { case 1: // CLOUDLESS -> CLOUDY strcat(buf, "Небо затянуло тучами.\r\n"); weather_info.sky = SKY_CLOUDY; break; case 2: // CLOUDY -> RAINING switch (time_info.month) { case MONTH_MAY: case MONTH_JUNE: case MONTH_JULY: case MONTH_AUGUST: case MONTH_SEPTEMBER: strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 30, 40, 30); break; case MONTH_DECEMBER: case MONTH_JANUARY: case MONTH_FEBRUARY: strcat(buf, "Пошел снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 30, 40, 30); break; case MONTH_OCTOBER: case MONTH_APRIL: if (IS_SET(cweather_type, WEATHER_QUICKCOOL) && weather_info.temperature <= 5) { strcat(buf, "Пошел снег.\r\n"); SET_BIT(cweather_type, WEATHER_LIGHTSNOW); } else { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 40, 60, 0); } break; case MONTH_NOVEMBER: if (avg_day_temp <= 3 || IS_SET(cweather_type, WEATHER_QUICKCOOL)) { strcat(buf, "Пошел снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 40, 60, 0); } else { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 40, 60, 0); } break; case MONTH_MART: if (avg_day_temp >= 3 || IS_SET(cweather_type, WEATHER_QUICKHOT)) { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 80, 20, 0); } else { strcat(buf, "Пошел снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 60, 30, 10); } break; } weather_info.sky = SKY_RAINING; break; case 3: // CLOUDY -> CLOUDLESS strcat(buf, "Начало проясняться.\r\n"); weather_info.sky = SKY_CLOUDLESS; break; case 4: // RAINING -> LIGHTNING strcat(buf, "Налетевший ветер разогнал тучи.\r\n"); weather_info.sky = SKY_LIGHTNING; break; case 5: // RAINING -> CLOUDY if (IS_SET(weather_info.weather_type, WEATHER_LIGHTRAIN | WEATHER_MEDIUMRAIN | WEATHER_BIGRAIN)) strcat(buf, "Дождь прекратился.\r\n"); else if (IS_SET(weather_info.weather_type, WEATHER_LIGHTSNOW | WEATHER_MEDIUMSNOW | WEATHER_BIGSNOW)) strcat(buf, "Снегопад прекратился.\r\n"); else if (IS_SET(weather_info.weather_type, WEATHER_GRAD)) strcat(buf, "Град прекратился.\r\n"); weather_info.sky = SKY_CLOUDY; break; case 6: // LIGHTNING -> RAINING switch (time_info.month) { case MONTH_MAY: case MONTH_JUNE: case MONTH_JULY: case MONTH_AUGUST: case MONTH_SEPTEMBER: if (IS_SET(cweather_type, WEATHER_QUICKCOOL)) { strcat(buf, "Начался град.\r\n"); SET_BIT(cweather_type, WEATHER_GRAD); } else { strcat(buf, "Полил дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 10, 40, 50); } break; case MONTH_DECEMBER: case MONTH_JANUARY: case MONTH_FEBRUARY: strcat(buf, "Повалил снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 10, 40, 50); break; case MONTH_OCTOBER: case MONTH_APRIL: if (IS_SET(cweather_type, WEATHER_QUICKCOOL) && weather_info.temperature <= 5) { strcat(buf, "Повалил снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 40, 60, 0); } else { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 40, 60, 0); } break; case MONTH_NOVEMBER: if (avg_day_temp <= 3 || IS_SET(cweather_type, WEATHER_QUICKCOOL)) { strcat(buf, "Повалил снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 40, 60, 0); } else { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 40, 60, 0); } break; case MONTH_MART: if (avg_day_temp >= 3 || IS_SET(cweather_type, WEATHER_QUICKHOT)) { strcat(buf, "Начался дождь.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 80, 20, 0); } else { strcat(buf, "Пошел снег.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 60, 30, 10); } break; } weather_info.sky = SKY_RAINING; break; case 0: default: if (IS_SET(weather_info.weather_type, WEATHER_GRAD)) { strcat(buf, "Град прекратился.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 10, 40, 50); } else if (IS_SET(weather_info.weather_type, WEATHER_BIGRAIN)) { if (weather_info.change >= 5) { strcat(buf, "Дождь утих.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 20, 80, 0); } else SET_BIT(cweather_type, WEATHER_BIGRAIN); } else if (IS_SET(weather_info.weather_type, WEATHER_MEDIUMRAIN)) { if (weather_info.change <= -5) { strcat(buf, "Дождь усилился.\r\n"); SET_BIT(cweather_type, WEATHER_BIGRAIN); } else if (weather_info.change >= 5) { strcat(buf, "Дождь утих.\r\n"); SET_BIT(cweather_type, WEATHER_LIGHTRAIN); } else SET_BIT(cweather_type, WEATHER_MEDIUMRAIN); } else if (IS_SET(weather_info.weather_type, WEATHER_LIGHTRAIN)) { if (weather_info.change <= -5) { strcat(buf, "Дождь усилился.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTRAIN, 0, 70, 30); } else SET_BIT(cweather_type, WEATHER_LIGHTRAIN); } else if (IS_SET(weather_info.weather_type, WEATHER_BIGSNOW)) { if (weather_info.change >= 5) { strcat(buf, "Снегопад утих.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 20, 80, 0); } else SET_BIT(cweather_type, WEATHER_BIGSNOW); } else if (IS_SET(weather_info.weather_type, WEATHER_MEDIUMSNOW)) { if (weather_info.change <= -5) { strcat(buf, "Снегопад усилился.\r\n"); SET_BIT(cweather_type, WEATHER_BIGSNOW); } else if (weather_info.change >= 5) { strcat(buf, "Снегопад утих.\r\n"); SET_BIT(cweather_type, WEATHER_LIGHTSNOW); } else SET_BIT(cweather_type, WEATHER_MEDIUMSNOW); } else if (IS_SET(weather_info.weather_type, WEATHER_LIGHTSNOW)) { if (weather_info.change <= -5) { strcat(buf, "Снегопад усилился.\r\n"); create_rainsnow(&cweather_type, WEATHER_LIGHTSNOW, 0, 70, 30); } else SET_BIT(cweather_type, WEATHER_LIGHTSNOW); } break; } if (*buf) send_to_outdoor(buf, WEATHER_CONTROL); weather_info.weather_type = cweather_type; }
void monster::hit_player(game *g, player &p, bool can_grab) { moves -= 100; if (type->melee_dice == 0) // We don't attack, so just return { return; } add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect(ME_RUN, 4); } bool is_npc = p.is_npc(); bool u_see = (!is_npc || g->u_see(p.posx, p.posy)); std::string you = (is_npc ? p.name : "you"); std::string You = (is_npc ? p.name : "You"); std::string your = (is_npc ? p.name + "'s" : "your"); std::string Your = (is_npc ? p.name + "'s" : "Your"); body_part bphit; int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0; int side = random_side(bphit); //110*e^(-.3*[melee skill of monster]) = % chance to miss. *100 to track .01%'s //Returns ~80% at 1, drops quickly to 33% at 4, then slowly to 5% at 10 and 1% at 16 if (rng(0, 10000) < 11000 * exp(-.3 * type->melee_skill)) { if (u_see) { g->add_msg(_("The %s misses."), name().c_str()); } } else { if (!g->u.uncanny_dodge()) { //Reduce player's ability to dodge by monster's ability to hit int dodge_ii = p.dodge(g) - rng(0, type->melee_skill); if (dodge_ii < 0) { dodge_ii = 0; } // 100/(1+99*e^(-.6*[dodge() return modified by monster's skill])) = % chance to dodge // *100 to track .01%'s // 1% minimum, scales slowly to 16% at 5, then rapidly to 80% at 10, // then returns less with each additional point, reaching 99% at 16 if (rng(0, 10000) < 10000/(1 + 99 * exp(-.6 * dodge_ii))) { if (is_npc) { if(u_see) { g->add_msg(_("%1$s dodges the %2$s."), p.name.c_str(), name().c_str()); } } else { g->add_msg(_("You dodge the %s."), name().c_str()); } p.practice(g->turn, "dodge", type->melee_skill * 2); //Better monster = more skill gained } //Successful hit with damage else if (dam > 0) { p.practice(g->turn, "dodge", type->melee_skill); if(!p.block_hit(g, this, NULL, bphit, side, dam, cut, stab) && u_see) { if (is_npc) { if( u_see ) { g->add_msg(_("The %1$s hits %2$s's %3$s."), name().c_str(), p.name.c_str(), body_part_name(bphit, side).c_str()); } } else { g->add_msg(_("The %1$s hits your %2$s."), name().c_str(), body_part_name(bphit, side).c_str()); } } // Attempt defensive moves if (!is_npc) { if (g->u.activity.type == ACT_RELOAD) { g->add_msg(_("You stop reloading.")); } else if (g->u.activity.type == ACT_READ) { g->add_msg(_("You stop reading.")); } else if (g->u.activity.type == ACT_CRAFT || g->u.activity.type == ACT_LONGCRAFT) { g->add_msg(_("You stop crafting.")); g->u.activity.type = ACT_NULL; } } if (p.has_active_bionic("bio_ods")) { if (!is_npc) { g->add_msg(_("Your offensive defense system shocks it!"), p.name.c_str()); } else if (u_see) { g->add_msg(_("%s's offensive defense system shocks it!"), p.name.c_str()); } hurt(rng(10, 40)); } if (p.encumb(bphit) == 0 &&(p.has_trait("SPINES") || p.has_trait("QUILLS"))) { int spine = rng(1, (p.has_trait("QUILLS") ? 20 : 8)); if (is_npc) { if( u_see ) { g->add_msg(_("%1$s's %2$s puncture it!"), p.name.c_str(), (g->u.has_trait("QUILLS") ? _("quills") : _("spines"))); } } else { g->add_msg(_("Your %s puncture it!"), (g->u.has_trait("QUILLS") ? _("quills") : _("spines"))); } hurt(spine); } if (dam + cut <= 0) { return; // Defensive technique canceled damage. } //Hallucinations don't actually hurt the player, but do produce the message if(is_hallucination()) { //~14% chance of vanishing after hitting the player if(one_in(7)) { die(g); return; } } else { //Hurt the player dam = p.hit(g, bphit, side, dam, cut); //Monster effects if (dam > 0 && has_flag(MF_VENOM)) { g->add_msg_if_player(&p, _("You're poisoned!")); p.add_disease("poison", 30); } else if (dam > 0 && has_flag(MF_BADVENOM)) { g->add_msg_if_player(&p, _("You feel poison flood your body, wracking you with pain...")); p.add_disease("badpoison", 40); } else if (dam > 0 && has_flag(MF_PARALYZE)) { g->add_msg_if_player(&p, _("You feel poison enter your body!")); p.add_disease("paralyzepoison", 100, false, 1, 20, 100); } if (has_flag(MF_BLEED) && dam > 6 && cut > 0) { g->add_msg_if_player(&p, _("You're Bleeding!")); p.add_disease("bleed", 60, false, 1, 3, 120, 1, bphit, side, true); } //Same as monster's chance to not miss if (can_grab && has_flag(MF_GRABS) && (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill))) { g->add_msg(_("The %s grabs you!"), name().c_str()); if (p.has_grab_break_tec() && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10)) { g->add_msg_if_player(&p, _("You break the grab!")); } else { hit_player(g, p, false); //We grabed, so hit them again } } } // TODO: readd with counter mechanic } } } // if dam > 0 if (is_npc) { if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0) { npc* tmp = dynamic_cast<npc*>(&p); tmp->die(g); int index = g->npc_at(p.posx, p.posy); if (index != -1 && index < g->active_npc.size()) { g->active_npc.erase(g->active_npc.begin() + index); } plans.clear(); } } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust += 15; } if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){ morale_adjust -= 15; } if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
void Person::threadRun() { { std::unique_lock<std::mutex> lock(cout_mutex); ++threads_ready; start_condition.wait(lock); } while (!areTasksDone()) { if (_timeLeftInShop != 0) { _timeLeftInShop--; } if (_currentShop) { bool canEnter = false; bool doneShopping = false; bool justLeft = false; if (_shops.size() != 0 && _shops[0]->enterShop(this)) { canEnter = true; } else if (_shops.size() == 0) { doneShopping = true; } { std::lock_guard<std::mutex> lock(cout_mutex); if (canEnter) { std::cout << *this << " makes a reservation at the " << _shops[0]->getName() << " shop." << std::endl; std::cout << *this << " leaves the " << _currentShop->getName() << " shop to apparate to the next destination." << std::endl; justLeft = true; } else if (doneShopping) { std::cout << *this << " is done with shopping, so he leaves the " << _currentShop->getName() << " shop." << std::endl; justLeft = true; } else if (_timeLeftInShop == 0) { std::cout << *this << " is bored talking to the salesperson, so he leaves the " << _currentShop->getName() << " shop without a reservation for the next shop to go for a walk." << std::endl; justLeft = true; } } if (justLeft) { _currentShop->leaveShop(this); _currentShop = nullptr; if (canEnter) { _currentShop = _shops[0]; _shops.erase(_shops.begin()); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << *this << " enters the " << _currentShop->getName() << " shop." << std::endl; } } } } if (!_currentShop && _shops.size() != 0 && _shops[0]->enterShop(this)) { _currentShop = _shops[0]; { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << *this << " makes a reservation at the " << _currentShop->getName() << " shop." << std::endl; std::cout << *this << " enters the " << _currentShop->getName() << " shop." << std::endl; } _shops.erase(_shops.begin()); _timeLeftInShop = dice(); } std::this_thread::sleep_for(std::chrono::milliseconds(1000)); ++_second; } }
void weather_change(void) { int diff, change; if((time_info.month>=9)&&(time_info.month<=16)) diff=(weather_info.pressure>985 ? -2 : 2); else diff=(weather_info.pressure>1015? -2 : 2); weather_info.change += (dice(1,4)*diff+dice(2,6)-dice(2,6)); weather_info.change = MIN(weather_info.change,12); weather_info.change = MAX(weather_info.change,-12); weather_info.pressure += weather_info.change; weather_info.pressure = MIN(weather_info.pressure,1040); weather_info.pressure = MAX(weather_info.pressure,960); change = 0; switch(weather_info.sky){ case SKY_CLOUDLESS : { if (weather_info.pressure<990) change = 1; else if (weather_info.pressure<1010) if(dice(1,4)==1) change = 1; break; } case SKY_CLOUDY : { if (weather_info.pressure<970) change = 2; else if (weather_info.pressure<990) if(dice(1,4)==1) change = 2; else change = 0; else if (weather_info.pressure>1030) if(dice(1,4)==1) change = 3; break; } case SKY_RAINING : { if (weather_info.pressure<970) if(dice(1,4)==1) change = 4; else change = 0; else if (weather_info.pressure>1030) change = 5; else if (weather_info.pressure>1010) if(dice(1,4)==1) change = 5; break; } case SKY_LIGHTNING : { if (weather_info.pressure>1010) change = 6; else if (weather_info.pressure>990) if(dice(1,4)==1) change = 6; break; } default : { change = 0; weather_info.sky=SKY_CLOUDLESS; break; } } ChangeWeather(change); }
void make_land(void) { int x, y, temp; FILE *fp1, *fp2; byte land1, land2; char name1[] = "land1.dat", name2[] = "land2.dat"; if(!(fp1=fopen(name1,"wb")) || !(fp2=fopen(name2,"wb"))) { cprintf("\nCannot open file."); } else { for(x=0;x<MAXX;x++) { for(y=0;y<MAXY;y++) { temp = dice(2,1); if(temp == 1) { land1.bit0 = 1; temp = dice(2,1); if(temp == 1) land2.bit0 = 0; else land2.bit0 = 1; } else { land1.bit0 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit1 = 1; temp = dice(2,1); if(temp == 1) land2.bit1 = 0; else land2.bit1 = 1; } else { land1.bit1 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit2 = 1; temp = dice(2,1); if(temp == 1) land2.bit2 = 0; else land2.bit2 = 1; } else { land1.bit2 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit3 = 1; temp = dice(2,1); if(temp == 1) land2.bit3 = 0; else land2.bit3 = 1; } else { land1.bit3 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit4 = 1; temp = dice(2,1); if(temp == 1) land2.bit4 = 0; else land2.bit4 = 1; } else { land1.bit4 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit5 = 1; temp = dice(2,1); if(temp == 1) land2.bit5 = 0; else land2.bit5 = 1; } else { land1.bit5 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit6 = 1; temp = dice(2,1); if(temp == 1) land2.bit6 = 0; else land2.bit6 = 1; } else { land1.bit6 = 0; } temp = dice(2,1); if(temp == 1) { land1.bit7 = 1; temp = dice(2,1); if(temp == 1) land2.bit7 = 0; else land2.bit7 = 1; } else { land1.bit7 = 0; } fwrite(&land1,sizeof(byte),1,fp1); fwrite(&land2,sizeof(byte),1,fp2); } printf("."); } fclose(fp1); fclose(fp2); } }
int dogaze(void) { struct monst *mtmp; int looked = 0; char qbuf[QBUFSZ]; int i; uchar adtyp = 0; for (i = 0; i < NATTK; i++) { if (youmonst.data->mattk[i].aatyp == AT_GAZE) { adtyp = youmonst.data->mattk[i].adtyp; break; } } if (adtyp != AD_CONF && adtyp != AD_FIRE) { impossible("gaze attack %d?", adtyp); return 0; } if (Blind) { pline("You can't see anything to gaze at."); return 0; } if (u.uen < 15) { pline("You lack the energy to use your special gaze!"); return 0; } u.uen -= 15; iflags.botl = 1; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { looked++; if (Invis && !perceives(mtmp->data)) pline("%s seems not to notice your gaze.", Monnam(mtmp)); else if (mtmp->minvis && !See_invisible) pline("You can't see where to gaze at %s.", Monnam(mtmp)); else if (mtmp->m_ap_type == M_AP_FURNITURE || mtmp->m_ap_type == M_AP_OBJECT) { looked--; continue; } else if (flags.safe_dog && !Confusion && !Hallucination && mtmp->mtame) { pline("You avoid gazing at %s.", y_monnam(mtmp)); } else { if (flags.confirm && mtmp->mpeaceful && !Confusion && !Hallucination) { sprintf(qbuf, "Really %s %s?", (adtyp == AD_CONF) ? "confuse" : "attack", mon_nam(mtmp)); if (yn(qbuf) != 'y') continue; setmangry(mtmp); } if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping || !mtmp->mcansee || !haseyes(mtmp->data)) { looked--; continue; } /* No reflection check for consistency with when a monster * gazes at *you*--only medusa gaze gets reflected then. */ if (adtyp == AD_CONF) { if (!mtmp->mconf) pline("Your gaze confuses %s!", mon_nam(mtmp)); else pline("%s is getting more and more confused.", Monnam(mtmp)); mtmp->mconf = 1; } else if (adtyp == AD_FIRE) { int dmg = dice(2,6); pline("You attack %s with a fiery gaze!", mon_nam(mtmp)); if (resists_fire(mtmp)) { pline("The fire doesn't burn %s!", mon_nam(mtmp)); dmg = 0; } if ((int) u.ulevel > rn2(20)) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); if ((int) u.ulevel > rn2(20)) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); if ((int) u.ulevel > rn2(25)) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg; if (mtmp->mhp <= 0) killed(mtmp); } /* For consistency with passive() in uhitm.c, this only * affects you if the monster is still alive. */ if (!DEADMONSTER(mtmp) && (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) { if (!Free_action) { pline("You are frozen by %s gaze!", s_suffix(mon_nam(mtmp))); nomul((u.ulevel > 6 || rn2(4)) ? -dice((int)mtmp->m_lev+1, (int)mtmp->data->mattk[0].damd) : -200, "frozen by a monster's gaze"); return 1; } else pline("You stiffen momentarily under %s gaze.", s_suffix(mon_nam(mtmp))); } /* Technically this one shouldn't affect you at all because * the Medusa gaze is an active monster attack that only * works on the monster's turn, but for it to *not* have an * effect would be too weird. */ if (!DEADMONSTER(mtmp) && (mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) { pline( "Gazing at the awake %s is not a very good idea.", l_monnam(mtmp)); /* as if gazing at a sleeping anything is fruitful... */ pline("You turn to stone..."); killer_format = KILLED_BY; killer = "deliberately meeting Medusa's gaze"; done(STONING); } } } } if (!looked) pline("You gaze at no place in particular."); return 1; }
/* * Every spell that does damage comes through here. This calculates the * amount of damage, adds in any modifiers, determines what the saves are, * tests for save and calls damage(). * * -1 = dead, otherwise the amount of damage done. */ int mag_damage(int level, struct char_data *ch, struct char_data *victim, int spellnum, int savetype) { int dam = 0; if (victim == NULL || ch == NULL) return (0); switch (spellnum) { /* Mostly mages */ case SPELL_MAGIC_MISSILE: case SPELL_CHILL_TOUCH: /* chill touch also has an affect */ if (IS_MAGIC_USER(ch)) dam = dice(1, 8) + 1; else dam = dice(1, 6) + 1; break; case SPELL_BURNING_HANDS: if (IS_MAGIC_USER(ch)) dam = dice(3, 8) + 3; else dam = dice(3, 6) + 3; break; case SPELL_SHOCKING_GRASP: if (IS_MAGIC_USER(ch)) dam = dice(5, 8) + 5; else dam = dice(5, 6) + 5; break; case SPELL_LIGHTNING_BOLT: if (IS_MAGIC_USER(ch)) dam = dice(7, 8) + 7; else dam = dice(7, 6) + 7; break; case SPELL_COLOR_SPRAY: if (IS_MAGIC_USER(ch)) dam = dice(9, 8) + 9; else dam = dice(9, 6) + 9; break; case SPELL_FIREBALL: if (IS_MAGIC_USER(ch)) dam = dice(11, 8) + 11; else dam = dice(11, 6) + 11; break; /* Mostly clerics */ case SPELL_DISPEL_EVIL: dam = dice(6, 8) + 6; if (IS_EVIL(ch)) { victim = ch; dam = GET_HIT(ch) - 1; } else if (IS_GOOD(victim)) { act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR); return (0); } break; case SPELL_DISPEL_GOOD: dam = dice(6, 8) + 6; if (IS_GOOD(ch)) { victim = ch; dam = GET_HIT(ch) - 1; } else if (IS_EVIL(victim)) { act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR); return (0); } break; case SPELL_CALL_LIGHTNING: dam = dice(7, 8) + 7; break; case SPELL_HARM: dam = dice(8, 8) + 8; break; case SPELL_ENERGY_DRAIN: if (GET_LEVEL(victim) <= 2) dam = 100; else dam = dice(1, 10); break; /* Area spells */ case SPELL_EARTHQUAKE: dam = dice(2, 8) + level; break; } /* switch(spellnum) */ /* divide damage by two if victim makes his saving throw */ if (mag_savingthrow(victim, savetype, 0)) dam /= 2; /* and finally, inflict the damage */ return (damage(ch, victim, dam, spellnum)); }
/** * 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); } 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 < 0.1 ) { message = _("Headshot!"); gmtSCTcolor = m_headshot; damage_mult *= rng_float(2.45, 3.35); bp_hit = bp_head; // headshot hits the head, of course } else if( goodhit < 0.2 ) { message = _("Critical!"); gmtSCTcolor = m_critical; damage_mult *= rng_float(1.75, 2.3); } else if( goodhit < 0.4 ) { message = _("Good hit!"); gmtSCTcolor = m_good; damage_mult *= rng_float(1, 1.5); } else if( goodhit < 0.6 ) { damage_mult *= rng_float(0.5, 1); } else if( goodhit < 0.8 ) { message = _("Grazing hit."); gmtSCTcolor = m_grazing; damage_mult *= rng_float(0, .25); } else { damage_mult *= 0; } 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( proj_effects.count("NOGIB") > 0 ) { impact.add_effect("NOGIB"); } if( damage_mult > 0.0f && proj_effects.count( "NO_DAMAGE_SCALING" ) ) { damage_mult = 1.0f; } impact.mult_damage(damage_mult); 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, 20)); } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) { add_effect( effect_onfire, rng(5, 10)); } } 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, 6)); } else if ( (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) && one_in(4) ) { add_effect( effect_onfire, rng(1, 4)); } } 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, rng(6, 6)); } else if (made_of( material_id( "flesh" ) ) || made_of( material_id( "iflesh" ) ) ) { add_effect( effect_onfire, rng(10, 10)); } } 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, 10 ) ); } if( proj_effects.count( "APPLY_SAP" ) ) { add_effect( effect_sap, 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, 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; }
int Creature::deal_projectile_attack(Creature *source, double missed_by, const projectile &proj, dealt_damage_instance &dealt_dam) { bool u_see_this = g->u_see(this); body_part bp_hit; int side = rng(0, 1); // do 10,speed because speed could potentially be > 10000 if (dodge_roll() >= dice(10, proj.speed)) { if (is_player()) add_msg(_("You dodge %s projectile!"), source->disp_name(true).c_str()); else if (u_see_this) add_msg(_("%s dodges %s projectile."), disp_name().c_str(), source->disp_name(true).c_str()); return 0; } // Bounce applies whether it does damage or not. if (proj.proj_effects.count("BOUNCE")) { add_effect("bounced", 1); } 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)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } double monster_speed_penalty = std::max(double(get_speed()) / 80., 1.0); double goodhit = missed_by / monster_speed_penalty; double damage_mult = 1.0; std::string message = ""; game_message_type gmtSCTcolor = m_neutral; if (goodhit <= .1) { message = _("Headshot!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_headshot; damage_mult *= rng_float(2.45, 3.35); bp_hit = bp_head; // headshot hits the head, of course } else if (goodhit <= .2) { message = _("Critical!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_critical; damage_mult *= rng_float(1.75, 2.3); } else if (goodhit <= .4) { message = _("Good hit!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_good; damage_mult *= rng_float(1, 1.5); } else if (goodhit <= .6) { damage_mult *= rng_float(0.5, 1); } else if (goodhit <= .8) { message = _("Grazing hit."); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_grazing; damage_mult *= rng_float(0, .25); } else { damage_mult *= 0; } // copy it, since we're mutating damage_instance impact = proj.impact; if( item(proj.ammo->id, 0).has_flag("NOGIB") ) { impact.add_effect("NOGIB"); } impact.mult_damage(damage_mult); dealt_dam = deal_damage(source, bp_hit, side, impact); dealt_dam.bp_hit = bp_hit; // Apply ammo effects to target. const std::string target_material = get_material(); if (proj.proj_effects.count("FLAME")) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood" ) ) { add_effect("onfire", rng(8, 20)); } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) { add_effect("onfire", rng(5, 10)); } } else if (proj.proj_effects.count("INCENDIARY") ) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood") ) { add_effect("onfire", rng(2, 6)); } else if ( (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) && one_in(4) ) { add_effect("onfire", rng(1, 4)); } } else if (proj.proj_effects.count("IGNITE")) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood") ) { add_effect("onfire", rng(6, 6)); } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) { add_effect("onfire", rng(10, 10)); } } int stun_strength = 0; if (proj.proj_effects.count("BEANBAG")) { stun_strength = 4; } if(proj.proj_effects.count("WHIP")) { stun_strength = rng(4, 10); } 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( "stunned", rng(stun_strength / 2, stun_strength) ); } if(u_see_this) { if (damage_mult == 0) { if(source != NULL) { add_msg(source->is_player() ? _("You miss!") : _("The shot misses!")); } } else if (dealt_dam.total_damage() == 0) { add_msg(_("The shot reflects off %s %s!"), disp_name(true).c_str(), skin_name().c_str()); } else if (source != NULL) { if (source->is_player()) { //player hits monster ranged nc_color color; std::string health_bar = ""; get_HP_Bar(dealt_dam.total_damage(), this->get_hp_max(), color, health_bar, true); SCT.add(this->xpos(), this->ypos(), direction_from(0, 0, this->xpos() - source->xpos(), this->ypos() - source->ypos()), health_bar, m_good, message, gmtSCTcolor); if (this->get_hp() > 0) { get_HP_Bar(this->get_hp(), this->get_hp_max(), color, health_bar, true); SCT.add(this->xpos(), this->ypos(), direction_from(0, 0, this->xpos() - source->xpos(), this->ypos() - source->ypos()), health_bar, m_good, "hp", m_neutral, "hp"); } else { SCT.removeCreatureHP(); } add_msg(m_good, _("You hit the %s for %d damage."), disp_name().c_str(), dealt_dam.total_damage()); } else if(this->is_player()) { //monster hits player ranged add_msg_if_player( m_bad, _( "You were hit in the %s for %d damage." ), body_part_name( bp_hit, side ).c_str( ), dealt_dam.total_damage( ) ); } else if( u_see_this ) { add_msg(_("%s shoots %s."), source->disp_name().c_str(), disp_name().c_str()); } } } return 0; }
void monster::melee_attack(Creature &target, bool, matec_id) { mod_moves(-100); if (type->melee_dice == 0) { // We don't attack, so just return return; } add_effect("hit_by_player", 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect("run", 4); } bool u_see_me = g->u_see(this); body_part bp_hit; //int highest_hit = 0; damage_instance damage; if(!is_hallucination()) { if (type->melee_dice > 0) { damage.add_damage(DT_BASH, dice(type->melee_dice,type->melee_sides)); } if (type->melee_cut > 0) { damage.add_damage(DT_CUT, type->melee_cut); } } dealt_damage_instance dealt_dam; int hitspread = target.deal_melee_attack(this, hit_roll()); if (hitspread >= 0) { target.deal_melee_hit(this, hitspread, false, damage, dealt_dam); } bp_hit = dealt_dam.bp_hit; if (hitspread < 0) { // a miss // TODO: characters practice dodge when a hit misses 'em if (target.is_player()) { if (u_see_me) { add_msg(_("You dodge %s."), disp_name().c_str()); } else { add_msg(_("You dodge an attack from an unseen source.")); } } else { if (u_see_me) { add_msg(_("The %1$s dodges %2$s attack."), name().c_str(), target.disp_name(true).c_str()); } } //Hallucinations always produce messages but never actually deal damage } else if (is_hallucination() || dealt_dam.total_damage() > 0) { if (target.is_player()) { if (u_see_me) { //~ 1$s is attacker name, 2$s is bodypart name in accusative. add_msg(m_bad, _("The %1$s hits your %2$s."), name().c_str(), body_part_name_accusative(bp_hit).c_str()); } else { //~ %s is bodypart name in accusative. add_msg(m_bad, _("Something hits your %s."), body_part_name_accusative(bp_hit).c_str()); } } else { if (u_see_me) { //~ 1$s is attacker name, 2$s is target name, 3$s is bodypart name in accusative. add_msg(_("The %1$s hits %2$s %3$s."), name().c_str(), target.disp_name(true).c_str(), body_part_name_accusative(bp_hit).c_str()); } } } else { if (target.is_player()) { if (u_see_me) { //~ 1$s is attacker name, 2$s is bodypart name in accusative, 3$s is armor name add_msg(_("The %1$s hits your %2$s, but your %3$s protects you."), name().c_str(), body_part_name_accusative(bp_hit).c_str(), target.skin_name().c_str()); } else { //~ 1$s is bodypart name in accusative, 2$s is armor name. add_msg(_("Something hits your %1$s, but your %2$s protects you."), body_part_name_accusative(bp_hit).c_str(), target.skin_name().c_str()); } } else { if (u_see_me) { //~ $1s is monster name, %2$s is that monster target name, //~ $3s is target bodypart name in accusative, 4$s is target armor name. add_msg(_("The %1$s hits %2$s %3$s but is stopped by %2$s %4$s."), name().c_str(), target.disp_name(true).c_str(), body_part_name_accusative(bp_hit).c_str(), target.skin_name().c_str()); } } } if (is_hallucination()) { if(one_in(7)) { die( nullptr ); } return; } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust += 15; } if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){ morale_adjust -= 15; } if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (size_t i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
int Creature::deal_projectile_attack(Creature *source, double missed_by, const projectile &proj, dealt_damage_instance &dealt_dam) { bool u_see_this = g->u_see(this); body_part bp_hit; int side = rng(0, 1); // do 10,speed because speed could potentially be > 10000 if (dodge_roll() >= dice(10, proj.speed)) { if (is_player()) g->add_msg(_("You dodge %s's projectile!"), skin_name().c_str()); else if (u_see_this) g->add_msg(_("%s dodges %s's projectile."), disp_name().c_str(), source->disp_name().c_str()); return 0; } 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)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } double monster_speed_penalty = std::max(double(get_speed()) / 80., 1.0); double goodhit = missed_by / monster_speed_penalty; double damage_mult = 1.0; if (goodhit <= .1) { g->add_msg_if_player(source, _("Headshot!")); damage_mult *= rng_float(2.45, 3.35); bp_hit = bp_head; // headshot hits the head, of course } else if (goodhit <= .2) { g->add_msg_if_player(source, _("Critical!")); damage_mult *= rng_float(1.75, 2.3); } else if (goodhit <= .4) { g->add_msg_if_player(source, _("Good hit!")); damage_mult *= rng_float(1, 1.5); } else if (goodhit <= .6) { damage_mult *= rng_float(0.5, 1); } else if (goodhit <= .8) { g->add_msg_if_player(source, _("Grazing hit.")); damage_mult *= rng_float(0, .25); } else { damage_mult *= 0; } // copy it, since we're mutating damage_instance impact = proj.impact; impact.mult_damage(damage_mult); dealt_dam = deal_damage(source, bp_hit, side, impact); dealt_dam.bp_hit = bp_hit; if(u_see_this) { if (damage_mult == 0) { if(source != NULL) { g->add_msg(source->is_player() ? _("You miss!") : _("The shot misses!")); } } else if (dealt_dam.total_damage() == 0) { g->add_msg(_("The shot reflects off the %s!"), skin_name().c_str()); } else if (source != NULL) { if (source->is_player()) { g->add_msg(_("You hit the %s for %d damage."), disp_name().c_str(), dealt_dam.total_damage()); } else if( this->is_player() && g->u.has_trait("SELFAWARE")) { g->add_msg_if_player( this, _( "You were hit in the %s for %d damage." ), body_part_name( bp_hit, side ).c_str( ), dealt_dam.total_damage( ) ); } else if( u_see_this ) { g->add_msg(_("%s shoots %s."), source->disp_name().c_str(), disp_name().c_str()); } } } return 0; }
void CItem::InitStats(int iGenLevel) { DWORD statType, statValue, stat2Type, stat2Value; int iResult; switch(m_sItemEffectType) { default: return; case ITEMEFFECTTYPE_ATTACK_MANASAVE: case ITEMEFFECTTYPE_ATTACK: if(m_sItemEffectType == ITEMEFFECTTYPE_ATTACK_MANASAVE) { statType = ITEMSTAT_CASTPROB; m_cItemColor = 5; }else { iResult = dice(1,10000); if ((iResult >= 1) && (iResult <= 299)) { statType = ITEMSTAT_LIGHT; m_cItemColor = 2; } else if ((iResult >= 300) && (iResult <= 999)) { statType = ITEMSTAT_STRONG; m_cItemColor = 3; } else if ((iResult >= 1000) && (iResult <= 2499)) { statType = ITEMSTAT_CRITICAL; m_cItemColor = 5; } else if ((iResult >= 2500) && (iResult <= 4499)) { statType = ITEMSTAT_AGILE; m_cItemColor = 1; } else if ((iResult >= 4500) && (iResult <= 6499)) { statType = ITEMSTAT_RIGHTEOUS; m_cItemColor = 7; } else if ((iResult >= 6500) && (iResult <= 8099)) { statType = ITEMSTAT_POISONING; m_cItemColor = 4; } else if ((iResult >= 8100) && (iResult <= 9699)) { statType = ITEMSTAT_SHARP; m_cItemColor = 6; } else if ((iResult >= 9700) && (iResult <= 10000)) { statType = ITEMSTAT_ANCIENT; m_cItemColor = 8; } } iResult = dice(1, 30000); if ((iResult >= 1) && (iResult < 10000)) statValue = 1; // 10000/29348 = 34% else if ((iResult >= 10000) && (iResult < 17400)) statValue = 2; // 6600/29348 = 22.4% else if ((iResult >= 17400) && (iResult < 22400)) statValue = 3; // 4356/29348 = 14.8% else if ((iResult >= 22400) && (iResult < 25400)) statValue = 4; // 2874/29348 = 9.7% else if ((iResult >= 25400) && (iResult < 27400)) statValue = 5; // 1897/29348 = 6.4% else if ((iResult >= 27400) && (iResult < 28400)) statValue = 6; // 1252/29348 = 4.2% else if ((iResult >= 28400) && (iResult < 28900)) statValue = 7; // 826/29348 = 2.8% else if ((iResult >= 28900) && (iResult < 29300)) statValue = 8; // 545/29348 = 1.85% else if ((iResult >= 29300) && (iResult < 29600)) statValue = 9; // 360/29348 = 1.2% else if ((iResult >= 29600) && (iResult < 29800)) statValue = 10; // 237/29348 = 0.8% else if ((iResult >= 29800) && (iResult < 29900)) statValue = 11; // 156/29348 = 0.5% else if ((iResult >= 29900) && (iResult < 29970)) statValue = 12; // 103/29348 = 0.3% else if ((iResult >= 29970) && (iResult <= 30000)) statValue = 13; // 68/29348 = 0.1% else statValue = 1; switch (statType) { case ITEMSTAT_CRITICAL: if (statValue <= 5) statValue = 5; break; case ITEMSTAT_POISONING: if (statValue <= 4) statValue = 4; break; case ITEMSTAT_LIGHT: if (statValue <= 4) statValue = 4; break; case ITEMSTAT_STRONG: if (statValue <= 2) statValue = 2; break; } if ((iGenLevel <= 2) && (statValue > 7)) statValue = 7; SetNibble(m_dwAttribute, 5, statType); SetNibble(m_dwAttribute, 4, statValue); if (dice(1,10000) >= 6000) { iResult = dice(1,10000); if ((iResult >= 1) && (iResult <= 4999)) stat2Type = ITEMSTAT2_HITPROB; else if ((iResult >= 5000) && (iResult <= 8499)) stat2Type = ITEMSTAT2_CAD; else if ((iResult >= 8500) && (iResult <= 9499)) stat2Type = ITEMSTAT2_GOLD; else if ((iResult >= 9500) && (iResult <= 10000)) stat2Type = ITEMSTAT2_EXP; iResult = dice(1, 30000); if ((iResult >= 1) && (iResult < 10000)) stat2Value = 1; // 33.33% else if ((iResult >= 10000) && (iResult < 17400)) stat2Value = 2; // 24.67% else if ((iResult >= 17400) && (iResult < 22400)) stat2Value = 3; // 16.67% else if ((iResult >= 22400) && (iResult < 25400)) stat2Value = 4; // 10.00% else if ((iResult >= 25400) && (iResult < 27100)) stat2Value = 5; // 5.67% else if ((iResult >= 27100) && (iResult < 28200)) stat2Value = 6; // 3.67% else if ((iResult >= 28200) && (iResult < 28900)) stat2Value = 7; // 2.34% else if ((iResult >= 28900) && (iResult < 29400)) stat2Value = 8; // 1.67% else if ((iResult >= 29400) && (iResult < 29720)) stat2Value = 9; // 1.07% else if ((iResult >= 29720) && (iResult < 29900)) stat2Value = 10; // 0.60% else if ((iResult >= 29900) && (iResult < 29970)) stat2Value = 11; // 0.24% else if ((iResult >= 29970) && (iResult < 29994)) stat2Value = 12; // 0.08% else if ((iResult >= 29994) && (iResult <= 30000)) stat2Value = 13; // 0.02% else stat2Value = 1; switch (stat2Type) { case 2: if (stat2Value <= 3) stat2Value = 3; break; case 10: if (stat2Value > 7) stat2Value = 7; break; case 11: stat2Value = 2; break; case 12: stat2Value = 5; break; } if ((iGenLevel <= 2) && (stat2Value > 7)) stat2Value = 7; SetNibble(m_dwAttribute, 3, stat2Type); SetNibble(m_dwAttribute, 2, stat2Value); } break; case ITEMEFFECTTYPE_DEFENSE: iResult = dice(1,10000); if ((iResult >= 1) && (iResult <= 5999)) statType = ITEMSTAT_STRONG; else if ((iResult >= 6000) && (iResult <= 8999)) statType = ITEMSTAT_LIGHT; else if ((iResult >= 9000) && (iResult <= 9554)) statType = ITEMSTAT_MANACONV; else if ((iResult >= 9555) && (iResult <= 10000)) statType = ITEMSTAT_CRITICAL2; iResult = dice(1, 30000); if ((iResult >= 1) && (iResult < 10000)) statValue = 1; else if ((iResult >= 10000) && (iResult < 17400)) statValue = 2; else if ((iResult >= 17400) && (iResult < 22400)) statValue = 3; else if ((iResult >= 22400) && (iResult < 25400)) statValue = 4; else if ((iResult >= 25400) && (iResult < 27400)) statValue = 5; else if ((iResult >= 27400) && (iResult < 28400)) statValue = 6; else if ((iResult >= 28400) && (iResult < 28900)) statValue = 7; else if ((iResult >= 28900) && (iResult < 29300)) statValue = 8; else if ((iResult >= 29300) && (iResult < 29600)) statValue = 9; else if ((iResult >= 29600) && (iResult < 29800)) statValue = 10; else if ((iResult >= 29800) && (iResult < 29900)) statValue = 11; else if ((iResult >= 29900) && (iResult < 29970)) statValue = 12; else if ((iResult >= 29970) && (iResult <= 30000)) statValue = 13; else statValue = 1; switch (statType) { case ITEMSTAT_LIGHT: if (statValue <= 4) statValue = 4; break; case ITEMSTAT_STRONG: if (statValue <= 2) statValue = 2; break; case ITEMSTAT_MANACONV: case ITEMSTAT_CRITICAL2: statValue = (statValue+1) / 2; if (statValue < 1) statValue = 1; if ((iGenLevel <= 3) && (statValue > 2)) statValue = 2; break; } if ((iGenLevel <= 2) && (statValue > 7)) statValue = 7; SetNibble(m_dwAttribute, 5, statType); SetNibble(m_dwAttribute, 4, statValue); if (dice(1,10000) <= 8500) { iResult = dice(1,10000); if (iResult <= 1000) stat2Type = ITEMSTAT2_PSNRES; // 10 else if (iResult <= 2200) stat2Type = ITEMSTAT2_DEF; // 12 else if (iResult <= 3800) stat2Type = ITEMSTAT2_SPREC; // 16 else if (iResult <= 6100) stat2Type = ITEMSTAT2_HPREC; // 23 else if (iResult <= 8400) stat2Type = ITEMSTAT2_MPREC; // 23 else if (iResult <= 9600) stat2Type = ITEMSTAT2_MR; // 12 else if (iResult <= 9900) stat2Type = ITEMSTAT2_PA; // 3 else if (iResult <= 10000) stat2Type = ITEMSTAT2_MA; // 1 iResult = dice(1, 30017); if ((iResult >= 1) && (iResult < 11800)) stat2Value = 1; else if ((iResult >= 11800) && (iResult < 19800)) stat2Value = 2; else if ((iResult >= 19800) && (iResult < 24300)) stat2Value = 3; else if ((iResult >= 24300) && (iResult < 27000)) stat2Value = 4; else if ((iResult >= 27000) && (iResult < 28500)) stat2Value = 5; else if ((iResult >= 28500) && (iResult < 29250)) stat2Value = 6; else if ((iResult >= 29250) && (iResult < 29650)) stat2Value = 7; else if ((iResult >= 29650) && (iResult < 29850)) stat2Value = 8; else if ((iResult >= 29850) && (iResult < 29950)) stat2Value = 9; else if ((iResult >= 29950) && (iResult < 29995)) stat2Value = 10; else if ((iResult >= 29995) && (iResult < 30011)) stat2Value = 11; else if ((iResult >= 30011) && (iResult < 30016)) stat2Value = 12; else if ((iResult >= 30016) && (iResult <= 30017)) stat2Value = 13; else stat2Value = 1; switch (stat2Type) { case ITEMSTAT2_PSNRES: case ITEMSTAT2_DEF: case ITEMSTAT2_MR: case ITEMSTAT2_PA: case ITEMSTAT2_MA: if (stat2Value <= 3) stat2Value = 3; break; } if ((iGenLevel <= 2) && (stat2Value > 7)) stat2Value = 7; SetNibble(m_dwAttribute, 3, stat2Type); SetNibble(m_dwAttribute, 2, stat2Value); #ifdef LOG_ARMORSTATS wsprintf(g_cTxt, "[I] Armor Drop: %s %s+%u (%u)", m_cName, itemStats2[stat2Type].desc, stat2Value * itemStats2[stat2Type].mult, stat2Value); PutLogList(g_cTxt); #endif } break; case ITEMEFFECTTYPE_JEWELRY: iResult = dice(1,10000); if ((iResult >= 1) && (iResult <= 4999)) statType = ITEMSTAT_LIGHT; else if ((iResult >= 5000) && (iResult <= 7999)) statType = ITEMSTAT_MANACONV; else if ((iResult >= 8000) && (iResult <= 10000)) statType = ITEMSTAT_CRITICAL2; uint32 bonus = m_sItemEffectValue1; iResult = dice(1, 30000-bonus) + bonus; if ((iResult >= 1) && (iResult < 10000)) statValue = 1; else if ((iResult >= 10000) && (iResult < 17400)) statValue = 2; else if ((iResult >= 17400) && (iResult < 22400)) statValue = 3; else if ((iResult >= 22400) && (iResult < 25400)) statValue = 4; else if ((iResult >= 25400) && (iResult < 27400)) statValue = 5; else if ((iResult >= 27400) && (iResult < 28400)) statValue = 6; else if ((iResult >= 28400) && (iResult < 28900)) statValue = 7; else if ((iResult >= 28900) && (iResult < 29300)) statValue = 8; else if ((iResult >= 29300) && (iResult < 29600)) statValue = 9; else if ((iResult >= 29600) && (iResult < 29800)) statValue = 10; else if ((iResult >= 29800) && (iResult < 29900)) statValue = 11; else if ((iResult >= 29900) && (iResult < 29970)) statValue = 12; else if ((iResult >= 29970) && (iResult <= 30000)) statValue = 13; else statValue = 1; switch (statType) { case ITEMSTAT_LIGHT: if (statValue <= 4) statValue = 4; break; case ITEMSTAT_STRONG: if (statValue <= 2) statValue = 2; break; case ITEMSTAT_MANACONV: case ITEMSTAT_CRITICAL2: statValue = (statValue+1) / 2; if (statValue < 1) statValue = 1; if ((iGenLevel <= 3) && (statValue > 2)) statValue = 2; break; } if ((iGenLevel <= 2) && (statValue > 7)) statValue = 7; SetNibble(m_dwAttribute, 5, statType); SetNibble(m_dwAttribute, 4, statValue); if (dice(1,10000) <= 8000) { iResult = dice(1,13000); if (iResult <= 1000) stat2Type = ITEMSTAT2_PSNRES; // 10 else if (iResult <= 2000) stat2Type = ITEMSTAT2_DEF; // 10 else if (iResult <= 3000) stat2Type = ITEMSTAT2_SPREC; // 16 else if (iResult <= 5400) stat2Type = ITEMSTAT2_HPREC; // 24 else if (iResult <= 7800) stat2Type = ITEMSTAT2_MPREC; // 24 else if (iResult <= 9000) stat2Type = ITEMSTAT2_MR; // 12 else if (iResult <= 11000) stat2Type = ITEMSTAT2_EXP; else if (iResult <= 13000) stat2Type = ITEMSTAT2_GOLD; iResult = dice(1, 29980 - bonus) + bonus; if ((iResult >= 1) && (iResult < 15000)) stat2Value = 1; else if ((iResult >= 15000) && (iResult < 22400)) stat2Value = 2; else if ((iResult >= 23000) && (iResult < 26100)) stat2Value = 3; else if ((iResult >= 26100) && (iResult < 27700)) stat2Value = 4; else if ((iResult >= 27700) && (iResult < 28700)) stat2Value = 5; else if ((iResult >= 28700) && (iResult < 29200)) stat2Value = 6; else if ((iResult >= 29200) && (iResult < 29450)) stat2Value = 7; else if ((iResult >= 29450) && (iResult < 29649)) stat2Value = 8; else if ((iResult >= 29649) && (iResult < 29793)) stat2Value = 9; else if ((iResult >= 29793) && (iResult < 29888)) stat2Value = 10; else if ((iResult >= 29888) && (iResult < 29935)) stat2Value = 11; else if ((iResult >= 29935) && (iResult < 29967)) stat2Value = 12; else if ((iResult >= 29967) && (iResult <= 29980)) stat2Value = 13; else stat2Value = 1; switch (stat2Type) { case ITEMSTAT2_PSNRES: case ITEMSTAT2_DEF: case ITEMSTAT2_MR: if (stat2Value <= 3) stat2Value = 3; break; } if (iGenLevel <= 2 && stat2Value > 7) stat2Value = 7; SetNibble(m_dwAttribute, 3, stat2Type); SetNibble(m_dwAttribute, 2, stat2Value); } break; } AdjustByStat(); }
void monster::hit_player(game *g, player &p, bool can_grab) { moves -= 100; if (type->melee_dice == 0) // We don't attack, so just return { return; } add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect(ME_RUN, 4); } bool is_npc = p.is_npc(); bool u_see = (!is_npc || g->u_see(p.posx, p.posy)); std::string you = (is_npc ? p.name : "you"); std::string You = (is_npc ? p.name : "You"); std::string your = (is_npc ? p.name + "'s" : "your"); std::string Your = (is_npc ? p.name + "'s" : "Your"); body_part bphit; int side = rng(0, 1); int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0; technique_id tech = p.pick_defensive_technique(g, this, NULL); p.perform_defensive_technique(tech, g, this, NULL, bphit, side, dam, cut, stab); //110*e^(-.3*[melee skill of monster]) = % chance to miss. *100 to track .01%'s //Returns ~80% at 1, drops quickly to 33% at 4, then slowly to 5% at 10 and 1% at 16 if (rng(0, 10000) < 11000 * exp(-.3 * type->melee_skill)) { g->add_msg("The %s misses.", name().c_str()); } else { if (!g->u.uncanny_dodge()) { //Reduce player's ability to dodge by monster's ability to hit int dodge_ii = p.dodge(g) - rng(0, type->melee_skill); if (dodge_ii < 0) { dodge_ii = 0; } // 100/(1+99*e^(-.6*[dodge() return modified by monster's skill])) = % chance to dodge // *100 to track .01%'s // 1% minimum, scales slowly to 16% at 5, then rapidly to 80% at 10, // then returns less with each additional point, reaching 99% at 16 if (rng(0, 10000) < 10000/(1 + 99 * exp(-.6 * dodge_ii))) { g->add_msg("%s dodge the %s.", You.c_str(), name().c_str()); p.practice(g->turn, "dodge", type->melee_skill * 2); //Better monster = more skill gained } //Successful hit with damage else if (dam > 0) { p.practice(g->turn, "dodge", type->melee_skill); if (u_see && tech != TEC_BLOCK) { g->add_msg("The %s hits %s %s.", name().c_str(), your.c_str(), body_part_name(bphit, side).c_str()); } // Attempt defensive moves if (!is_npc) { if (g->u.activity.type == ACT_RELOAD) { g->add_msg("You stop reloading."); } else if (g->u.activity.type == ACT_READ) { g->add_msg("You stop reading."); } else if (g->u.activity.type == ACT_CRAFT || g->u.activity.type == ACT_LONGCRAFT) { g->add_msg("You stop crafting."); g->u.activity.type = ACT_NULL; } } if (p.has_active_bionic("bio_ods")) { if (u_see) { g->add_msg("%s offensive defense system shocks it!", Your.c_str()); } if (hurt(rng(10, 40))) die(g); } if (p.encumb(bphit) == 0 &&(p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) { int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8)); g->add_msg("%s %s puncture it!", Your.c_str(), (g->u.has_trait(PF_QUILLS) ? "quills" : "spines")); if (hurt(spine)) die(g); } if (dam + cut <= 0) { return; // Defensive technique canceled damage. } //Hurt the player dam = p.hit(g, bphit, side, dam, cut); //Monster effects if (dam > 0 && has_flag(MF_VENOM)) { if (!is_npc) { g->add_msg("You're poisoned!"); } p.add_disease("poison", 30); } else if (dam > 0 && has_flag(MF_BADVENOM)) { if (!is_npc) { g->add_msg("You feel poison flood your body, wracking you with pain..."); } p.add_disease("badpoison", 40); } if (has_flag(MF_BLEED) && dam > 6 && cut > 0) { if (!is_npc) { g->add_msg("You're Bleeding!"); } p.add_disease("bleed", 60); } //Same as monster's chance to not miss if (can_grab && has_flag(MF_GRABS) && (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill))) { if (!is_npc) { g->add_msg("The %s grabs you!", name().c_str()); } if (p.weapon.has_technique(TEC_BREAK, &p) && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10)) { if (!is_npc) { g->add_msg("You break the grab!"); } } else hit_player(g, p, false); //We grabed, so hit them again } //Counter-attack? if (tech == TEC_COUNTER && !is_npc) { // A counterattack is a free action to avoid stunlocking the player. int player_moves = p.moves; hurt( p.hit_mon(g, this) ); p.moves = player_moves; } } } } // if dam > 0 if (is_npc) { if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0) { npc* tmp = dynamic_cast<npc*>(&p); tmp->die(g); int index = g->npc_at(p.posx, p.posy); if (index != -1 && index < g->active_npc.size()) { g->active_npc.erase(g->active_npc.begin() + index); } plans.clear(); } } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; for (int i = 0; i < type->anger.size(); i++) { if (type->anger[i] == MTRIG_FRIEND_ATTACKED) { anger_adjust += 15; } } for (int i = 0; i < type->placate.size(); i++) { if (type->placate[i] == MTRIG_FRIEND_ATTACKED) { anger_adjust -= 15; } } for (int i = 0; i < type->fear.size(); i++) { if (type->fear[i] == MTRIG_FRIEND_ATTACKED) { morale_adjust -= 15; } } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->z.size(); i++) { g->z[i].morale += morale_adjust; g->z[i].anger += anger_adjust; } } }
int Creature::deal_melee_attack(Creature *source, int hitroll, bool critical_hit, const damage_instance &dam, dealt_damage_instance &dealt_dam) { int dodgeroll = dodge_roll(); int hit_spread = hitroll - dodgeroll; bool missed = hit_spread <= 0; damage_instance d = dam; // copy, since we will mutate in block_hit if (missed) { return hit_spread; } //bool critical_hit = hit_spread > 30; //scored_crit(dodgeroll); body_part bp_hit; int side = rng(0, 1); int hit_value = hit_spread + dice(10, 6) - 35; if (hit_value >= 40) { bp_hit = bp_eyes; } else if (hit_value >= 30) { bp_hit = bp_head; } else if (hit_value >= 5) { bp_hit = bp_torso; } else if (one_in(4)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } // Bashing crit if (critical_hit) { int turns_stunned = (d.type_damage(DT_BASH) + hit_spread) / 20; if (turns_stunned > 6) { turns_stunned = 6; } if (turns_stunned > 0) { add_effect("stunned", turns_stunned); } } // Stabbing effects int stab_moves = rng(d.type_damage(DT_STAB) / 2, d.type_damage(DT_STAB) * 1.5); if (critical_hit) { stab_moves *= 1.5; } if (stab_moves >= 150) { if (is_player()) { // can the player force their self to the ground? probably not. g->add_msg_if_npc(source, _("<npcname> forces you to the ground!")); } else { g->add_msg_player_or_npc(source, _("You force %s to the ground!"), _("<npcname> forces %s to the ground!"), disp_name().c_str() ); } add_effect("downed", 1); mod_moves(-stab_moves / 2); } else { mod_moves(-stab_moves); } block_hit(bp_hit, side, d); on_gethit(source, bp_hit, d); // trigger on-gethit events dealt_dam = deal_damage(source, bp_hit, side, d); dealt_dam.bp_hit = bp_hit; /* TODO: add grabs n shit back in if (allow_special && technique.grabs) { // TODO: make this depend on skill (through grab_resist stat) again if (t.get_grab_resist() > 0 && dice(t.get_dex() , 12) > dice(get_dex(), 10)) { g->add_msg_player_or_npc(&t, _("You break the grab!"), _("<npcname> breaks the grab!")); } else if (!unarmed_attack()) { // Move our weapon to a temp slot, if it's not unarmed item tmpweap = remove_weapon(); melee_attack(t, false); // False means a second grab isn't allowed weapon = tmpweap; } else melee_attack(t, false); // False means a second grab isn't allowed } */ return hit_spread; }
technique_id player::pick_technique(game *g, monster *z, player *p, bool crit, bool allowgrab) { if (z == NULL && p == NULL) return TEC_NULL; std::vector<technique_id> possible; bool downed = ((z && !z->has_effect(ME_DOWNED)) || (p && !p->has_disease(DI_DOWNED)) ); int base_str_req = 0; if (z) base_str_req = z->type->size; else if (p) base_str_req = 1 + (2 + p->str_cur) / 4; if (allowgrab) { // Check if grabs AREN'T REALLY ALLOWED if (z && z->has_flag(MF_PLASTIC)) allowgrab = false; } if (crit) { // Some are crit-only if (weapon.has_technique(TEC_SWEEP, this) && (!z || !z->has_flag(MF_FLIES)) && !downed) possible.push_back(TEC_SWEEP); if (weapon.has_technique(TEC_PRECISE, this)) possible.push_back(TEC_PRECISE); if (weapon.has_technique(TEC_BRUTAL, this) && !downed && str_cur + skillLevel("melee") >= 4 + base_str_req) possible.push_back(TEC_BRUTAL); } if (possible.empty()) { // Use non-crits only if any crit-onlies aren't used if (weapon.has_technique(TEC_DISARM, this) && !z && p->weapon.typeId() != "null" && !p->weapon.has_flag(IF_UNARMED_WEAPON) && dice( dex_cur + skillLevel("unarmed"), 8) > dice(p->dex_cur + p->skillLevel("melee"), 10)) possible.push_back(TEC_DISARM); if (weapon.has_technique(TEC_GRAB, this) && allowgrab) possible.push_back(TEC_GRAB); if (weapon.has_technique(TEC_RAPID, this)) possible.push_back(TEC_RAPID); if (weapon.has_technique(TEC_THROW, this) && !downed && str_cur + skillLevel("melee") >= 4 + base_str_req * 4 + rng(-4, 4)) possible.push_back(TEC_THROW); if (weapon.has_technique(TEC_WIDE, this)) { // Count monsters int enemy_count = 0; for (int x = posx - 1; x <= posx + 1; x++) { for (int y = posy - 1; y <= posy + 1; y++) { int mondex = g->mon_at(x, y); if (mondex != -1) { if (g->z[mondex].friendly == 0) enemy_count++; else enemy_count -= 2; } int npcdex = g->npc_at(x, y); if (npcdex != -1) { if (g->active_npc[npcdex].attitude == NPCATT_KILL) enemy_count++; else enemy_count -= 2; } } } if (enemy_count >= (possible.empty() ? 2 : 3)) { possible.push_back(TEC_WIDE); } } } // if (possible.empty()) if (possible.empty()) return TEC_NULL; possible.push_back(TEC_NULL); // Always a chance to not use any technique return possible[ rng(0, possible.size() - 1) ]; }