int monster::turns_to_reach(int x, int y) { std::vector<point> path = g->m.route(posx(), posy(), x, y, has_flag(MF_BASHES)); if (path.size() == 0) return 999; double turns = 0.; for (int i = 0; i < path.size(); i++) { if (g->m.move_cost(path[i].x, path[i].y) == 0) // We have to bash through turns += 5; else if (i == 0) turns += double(calc_movecost(posx(), posy(), path[i].x, path[i].y)) / speed; else turns += double(calc_movecost(path[i-1].x, path[i-1].y, path[i].x, path[i].y)) / speed; } return int(turns + .9); // Round up }
void monster::move_to(game *g, int x, int y) { int mondex = g->mon_at(x, y); if (mondex == -1) { //...assuming there's no monster there if (has_effect(ME_BEARTRAP)) { moves = 0; return; } if (plans.size() > 0) plans.erase(plans.begin()); moves -= calc_movecost(g, posx, posy, x, y); if (has_flag(MF_SLUDGETRAIL)){ g->m.add_field(g, posx, posy, fd_sludge, 3); g->m.add_field(g, posx+1, posy, fd_sludge, 2); g->m.add_field(g, posx, posy+1, fd_sludge, 2); g->m.add_field(g, posx-1, posy, fd_sludge, 2); g->m.add_field(g, posx, posy-1, fd_sludge, 2); } posx = x; posy = y; footsteps(g, x, y); 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 (!has_flag(MF_DIGS) && !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 (has_flag(MF_DIGS)){ 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, 1); } if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx, posy, fd_acid, 1); } if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx, posy, fd_acid, 1); } } else if (has_flag(MF_ATTACKMON) || g->z[mondex].friendly != 0) // If there IS a monster there, and we fight monsters, fight it! hit_monster(g, mondex); }
int monster::turns_to_reach(int x, int y) { std::vector<point> path = g->m.route(posx(), posy(), x, y, false); if (path.empty()) return 999; double turns = 0.; for (size_t i = 0; i < path.size(); i++) { if (g->m.move_cost(path[i].x, path[i].y) == 0) { // We have to bash through turns += 5; } else if (i == 0) { turns += double(calc_movecost(posx(), posy(), path[i].x, path[i].y)) / get_speed(); } else { turns += double(calc_movecost(path[i-1].x, path[i-1].y, path[i].x, path[i].y)) / get_speed(); } } return int(turns + .9); // Round up }
/* Random walking even when we've moved * To simulate zombie stumbling and ineffective movement * Note that this is sub-optimal; stumbling may INCREASE a zombie's speed. * Most of the time (out in the open) this effect is insignificant compared to * the negative effects, but in a hallway it's perfectly even */ void monster::stumble(bool moved) { // don't stumble every turn. every 3rd turn, or 8th when walking. if((moved && !one_in(8)) || !one_in(3)) { return; } std::vector <point> valid_stumbles; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { const int nx = posx() + i; const int ny = posy() + j; if ((i || j) && can_move_to(nx, ny) && /* Don't ever stumble into impassable terrain, even if we normally could * smash it, as this is uncoordinated movement (and is forced). */ g->m.move_cost(nx, ny) != 0 && //Stop zombies and other non-breathing monsters wandering INTO water //(Unless they can swim/are aquatic) //But let them wander OUT of water if they are there. !(has_flag(MF_NO_BREATHE) && !has_flag(MF_SWIMS) && !has_flag(MF_AQUATIC) && g->m.has_flag("SWIMMABLE", nx, ny) && !g->m.has_flag("SWIMMABLE", posx(), posy())) && (g->u.posx != nx || g->u.posy != ny) && (g->mon_at(nx, ny) == -1)) { point tmp(nx, ny); valid_stumbles.push_back(tmp); } } } if (valid_stumbles.size() == 0) //nowhere to stumble? { return; } int choice = rng(0, valid_stumbles.size() - 1); int cx = valid_stumbles[choice].x; int cy = valid_stumbles[choice].y; moves -= calc_movecost(posx(), posy(), cx, cy); setpos(cx, cy); // Here we have to fix our plans[] list, // acquiring a new path to the previous target. // target == either end of current plan, or the player. int tc; if (plans.size() > 0) { if (g->m.sees(posx(), posy(), plans.back().x, plans.back().y, -1, tc)) set_dest(plans.back().x, plans.back().y, tc); else if (sees_player( tc )) set_dest(g->u.posx, g->u.posy, tc); else //durr, i'm suddenly calm. what was i doing? plans.clear(); } }
int monster::turns_to_reach( int x, int y ) { // This function is a(n old) temporary hack that should soon be removed auto path = g->m.route( pos(), tripoint( x, y, posz() ), get_pathfinding_settings() ); if( path.empty() ) { return 999; } double turns = 0.; for( size_t i = 0; i < path.size(); i++ ) { const tripoint &next = path[i]; if( g->m.impassable( next ) ) { // No bashing through, it looks stupid when you go back and find // the doors intact. return 999; } else if( i == 0 ) { turns += double( calc_movecost( pos(), next ) ) / get_speed(); } else { turns += double( calc_movecost( path[i - 1], next ) ) / get_speed(); } } return int( turns + .9 ); // Halve (to get turns) and round up }
/* Random walking even when we've moved * To simulate zombie stumbling and ineffective movement * Note that this is sub-optimal; stumbling may INCREASE a zombie's speed. * Most of the time (out in the open) this effect is insignificant compared to * the negative effects, but in a hallway it's perfectly even */ void monster::stumble(game *g, bool moved) { // don't stumble every turn. every 3rd turn, or 8th when walking. if((moved && !one_in(8)) || !one_in(3)) { return; } std::vector <point> valid_stumbles; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (can_move_to(g, posx + i, posy + j) && (g->u.posx != posx + i || g->u.posy != posy + j) && (g->mon_at(posx + i, posy + j) == -1 || (i == 0 && j == 0))) { point tmp(posx + i, posy + j); valid_stumbles.push_back(tmp); } } } if (valid_stumbles.size() == 0) //nowhere to stumble? { return; } int choice = rng(0, valid_stumbles.size() - 1); int cx = valid_stumbles[choice].x; int cy = valid_stumbles[choice].y; moves -= calc_movecost(g, posx, posy, cx, cy); posx = cx; posy = cy; // Here we have to fix our plans[] list, // acquiring a new path to the previous target. // target == either end of current plan, or the player. int tc; if (plans.size() > 0) { if (g->m.sees(posx, posy, plans.back().x, plans.back().y, -1, tc)) set_dest(plans.back().x, plans.back().y, tc); else if (g->sees_u(posx, posy, tc)) set_dest(g->u.posx, g->u.posy, tc); else //durr, i'm suddenly calm. what was i doing? plans.clear(); } }
int monster::move_to(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(x, y)) { return 0; } if (has_effect("beartrap")) { moves = 0; return 0; } if (plans.size() > 0) { plans.erase(plans.begin()); } if (!force) { moves -= calc_movecost(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(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))(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(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(posx() + dx, posy() + dy, fd_sludge, fstr); } } } } return 1; }
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); 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); } } } } //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 (!has_flag(MF_DIGS) && !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 (has_flag(MF_DIGS)){ g->m.ter_set(posx(), posy(), t_dirtmound); } // Acid trail monsters leave... a trail of acid //Why is this being done three times? if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx(), posy(), fd_acid, 1); } if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx(), posy(), fd_acid, 1); } if (has_flag(MF_ACIDTRAIL)){ g->m.add_field(g, posx(), posy(), fd_acid, 1); } return 1; }
int monster::move_to(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(x, y)) { return 0; } if (!plans.empty()) { plans.erase(plans.begin()); } if (!force) { moves -= calc_movecost(posx(), posy(), x, y); } //Check for moving into/out of water bool was_water = g->m.is_divable(posx(), posy()); bool will_be_water = !has_flag( MF_FLIES ) && can_submerge() && g->m.is_divable(x, y); if(was_water && !will_be_water && g->u.sees(x, y)) { //Use more dramatic messages for swimming monsters add_msg(m_warning, _("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.sees(x, y)) { add_msg(m_warning, _("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(x, y); underwater = will_be_water; 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)) { apply_damage( nullptr, bp_torso, rng( 2, 3 ) ); } if (type->size != MS_TINY && g->m.has_flag("ROUGH", posx(), posy()) && one_in(6)) { apply_damage( nullptr, bp_torso, rng( 1, 2 ) ); } if (g->m.has_flag("UNSTABLE", x, y)) { add_effect("bouldering", 1, num_bp, true); } else if (has_effect("bouldering")) { remove_effect("bouldering"); } if (!digging() && !has_flag(MF_FLIES) && g->m.tr_at(posx(), posy()) != tr_null) { // Monster stepped on a trap! trap* tr = traplist[g->m.tr_at(posx(), posy())]; if (dice(3, type->sk_dodge + 1) < dice(3, tr->get_avoidance())) { tr->trigger(this, posx(), posy()); } } if( !will_be_water && ( has_flag(MF_DIGS) || has_flag(MF_CAN_DIG) ) ) { underwater = g->m.has_flag("DIGGABLE", 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(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(posx() + dx, posy() + dy, fd_sludge, fstr); } } } } if (has_flag(MF_LEAKSGAS)){ if (one_in(6)){ g->m.add_field(posx() + rng(-1,1), posy() + rng(-1, 1), fd_toxic_gas, 3); } } return 1; }
bool monster::move_to( const tripoint &p, bool force, const float stagger_adjustment ) { const bool digs = digging(); const bool flies = has_flag( MF_FLIES ); const bool on_ground = !digs && !flies; const bool climbs = has_flag( MF_CLIMBS ) && g->m.has_flag( TFLAG_NO_FLOOR, p ); // Allows climbing monsters to move on terrain with movecost <= 0 Creature *critter = g->critter_at( p, is_hallucination() ); if( g->m.has_flag( "CLIMBABLE", p ) ) { if( g->m.impassable( p ) && critter == nullptr ) { if( flies ) { moves -= 100; force = true; if( g->u.sees( *this ) ) { add_msg( _( "The %1$s flies over the %2$s." ), name().c_str(), g->m.has_flag_furn( "CLIMBABLE", p ) ? g->m.furnname( p ).c_str() : g->m.tername( p ).c_str() ); } } else if( has_flag( MF_CLIMBS ) ) { moves -= 150; force = true; if( g->u.sees( *this ) ) { add_msg( _( "The %1$s climbs over the %2$s." ), name().c_str(), g->m.has_flag_furn( "CLIMBABLE", p ) ? g->m.furnname( p ).c_str() : g->m.tername( p ).c_str() ); } } } } if( critter != nullptr && !force ) { return false; } // Make sure that we can move there, unless force is true. if( !force && !can_move_to( p ) ) { return false; } if( !force ) { // This adjustment is to make it so that monster movement speed relative to the player // is consistent even if the monster stumbles, // and the same regardless of the distance measurement mode. const int cost = stagger_adjustment * ( float )( climbs ? calc_climb_cost( pos(), p ) : calc_movecost( pos(), p ) ); if( cost > 0 ) { moves -= cost; } else { return false; } } //Check for moving into/out of water bool was_water = g->m.is_divable( pos() ); bool will_be_water = on_ground && can_submerge() && g->m.is_divable( p ); if( was_water && !will_be_water && g->u.sees( p ) ) { //Use more dramatic messages for swimming monsters add_msg( m_warning, _( "A %1$s %2$s from the %3$s!" ), name().c_str(), has_flag( MF_SWIMS ) || has_flag( MF_AQUATIC ) ? _( "leaps" ) : _( "emerges" ), g->m.tername( pos() ).c_str() ); } else if( !was_water && will_be_water && g->u.sees( p ) ) { add_msg( m_warning, _( "A %1$s %2$s into the %3$s!" ), name().c_str(), has_flag( MF_SWIMS ) || has_flag( MF_AQUATIC ) ? _( "dives" ) : _( "sinks" ), g->m.tername( p ).c_str() ); } setpos( p ); footsteps( p ); underwater = will_be_water; if( is_hallucination() ) { //Hallucinations don't do any of the stuff after this point return true; } // TODO: Make tanks stop taking damage from rubble, because it's just silly if( type->size != MS_TINY && on_ground ) { if( g->m.has_flag( "SHARP", pos() ) && !one_in( 4 ) ) { apply_damage( nullptr, bp_torso, rng( 1, 10 ) ); } if( g->m.has_flag( "ROUGH", pos() ) && one_in( 6 ) ) { apply_damage( nullptr, bp_torso, rng( 1, 2 ) ); } } if( g->m.has_flag( "UNSTABLE", p ) && on_ground ) { add_effect( effect_bouldering, 1, num_bp, true ); } else if( has_effect( effect_bouldering ) ) { remove_effect( effect_bouldering ); } g->m.creature_on_trap( *this ); if( !will_be_water && ( has_flag( MF_DIGS ) || has_flag( MF_CAN_DIG ) ) ) { underwater = g->m.has_flag( "DIGGABLE", pos() ); } // 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( one_in( factor ) ) { g->m.ter_set( pos(), t_dirtmound ); } } // Acid trail monsters leave... a trail of acid if( has_flag( MF_ACIDTRAIL ) ) { g->m.add_field( pos(), fd_acid, 3, 0 ); } if( has_flag( MF_SLUDGETRAIL ) ) { for( const tripoint &sludge_p : g->m.points_in_radius( pos(), 1 ) ) { const int fstr = 3 - ( abs( sludge_p.x - posx() ) + abs( sludge_p.y - posy() ) ); if( fstr >= 2 ) { g->m.add_field( sludge_p, fd_sludge, fstr, 0 ); } } } return true; }