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 (plans.size() > 0) plans.erase(plans.begin()); if (has_flag(MF_SWIMS) && g->m.has_flag(swimmable, x, y)) moves += 50; if (!has_flag(MF_DIGS) && !has_flag(MF_FLIES) && (!has_flag(MF_SWIMS) || !g->m.has_flag(swimmable, x, y))) moves -= (g->m.move_cost(x, y) - 2) * 50; posx = x; posy = y; footsteps(g, x, y); 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, 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(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); } 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); }
void monster::friendly_move(game *g) { point next; bool moved = false; moves -= 100; if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) && (can_move_to(g->m, plans[0].x, plans[0].y) || (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){ next = plans[0]; plans.erase(plans.begin()); moved = true; } else stumble(g, moved); if (moved) { int mondex = g->mon_at(next.x, next.y); int npcdex = g->npc_at(next.x, next.y); if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0) hit_monster(g, mondex); else if (npcdex != -1 && type->melee_dice > 0) hit_player(g, g->active_npc[g->npc_at(next.x, next.y)]); else if (mondex == -1 && npcdex == -1 && can_move_to(g->m, next.x, next.y)) move_to(g, next.x, next.y); else if ((!can_move_to(g->m, next.x, next.y) || one_in(3)) && g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) { std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug! int bashskill = int(type->melee_dice * type->melee_sides); g->m.bash(next.x, next.y, bashskill, bashsound); g->sound(next.x, next.y, 18, bashsound); } else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) { g->m.destroy(g, next.x, next.y, true); moves -= 250; } } }
bool monster::attack_at( const tripoint &p ) { if( p.z != posz() ) { return false; // TODO: Remove this } if( has_effect( "pacified" ) ) { return false; } if( p == g->u.pos3() ) { melee_attack( g->u, true ); return true; } const int mondex = g->mon_at( p ); if( mondex != -1 ) { monster &mon = g->zombie( mondex ); // Don't attack yourself. if( &mon == this ) { return false; } // Special case: Target is hallucination if( mon.is_hallucination() ) { mon.die( nullptr ); // We haven't actually attacked anything, i.e. we can still do things. // Hallucinations(obviously) shouldn't affect the way real monsters act. return false; } // With no melee dice, we can't attack, but we had to process until here // because hallucinations require no melee dice to destroy. if( type->melee_dice <= 0 ) { return false; } auto attitude = attitude_to( mon ); // MF_ATTACKMON == hulk behavior, whack everything in your way if( attitude == A_HOSTILE || has_flag( MF_ATTACKMON ) ) { hit_monster( mon ); return true; } return false; } const int npcdex = g->npc_at( p ); if( npcdex != -1 && type->melee_dice > 0 ) { // For now we're always attacking NPCs that are getting into our // way. This is consistent with how it worked previously, but // later on not hitting allied NPCs would be cool. melee_attack( *g->active_npc[npcdex], true ); return true; } // Nothing to attack. return false; }
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::attack_at(int x, int y) { if (has_effect("pacified")) return 0; int mondex = g->mon_at(x, y); int npcdex = g->npc_at(x, y); if(x == g->u.posx && y == g->u.posy) { melee_attack(g->u); return 1; } if(mondex != -1) { // Currently, there are only pro-player and anti-player groups, // this makes it easy for us. monster& mon = g->zombie(mondex); // Don't attack yourself. if(&mon == this) { return 0; } // Special case: Target is hallucination if(mon.is_hallucination()) { mon.die( nullptr ); // We haven't actually attacked anything, i.e. we can still do things. // Hallucinations(obviously) shouldn't affect the way real monsters act. return 0; } // With no melee dice, we can't attack, but we had to process until here // because hallucinations require no melee dice to destroy. if(type->melee_dice <= 0) { return 0; } bool is_enemy = mon.friendly != friendly; is_enemy = is_enemy || has_flag(MF_ATTACKMON); // I guess the flag means all monsters are enemies? if(is_enemy) { hit_monster(mon); return 1; } } else if(npcdex != -1 && type->melee_dice > 0) { // For now we're always attacking NPCs that are getting into our // way. This is consistent with how it worked previously, but // later on not hitting allied NPCs would be cool. melee_attack(*g->active_npc[npcdex]); return 1; } // Nothing to attack. return 0; }
/* Function: friendly_move The per-turn movement and action calculation of any friendly monsters. */ void monster::friendly_move(game *g) { point next; bool moved = false; //If we sucessfully calculated a plan in the generic monster movement function, begin executing it. if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) && (can_move_to(g, plans[0].x, plans[0].y) || (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){ next = plans[0]; plans.erase(plans.begin()); moved = true; } else { //Otherwise just stumble around randomly until we formulate a plan. moves -= 100; stumble(g, moved); } if (moved) { //We have a plan. int mondex = g->mon_at(next.x, next.y); int npcdex = g->npc_at(next.x, next.y); //If there is an unfriendly mosnter in the target square we want to move into, hit them if we have a melee attack. if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0){ hit_monster(g, mondex); } //If there is an npc (any npc?) we hit them assuming we have a melee attack. else if (npcdex != -1 && type->melee_dice > 0){ hit_player(g, *g->active_npc[g->npc_at(next.x, next.y)]); } //If no one is there and its a walkable square, walk there. else if (mondex == -1 && npcdex == -1 && can_move_to(g, next.x, next.y)){ move_to(g, next.x, next.y); } //If there is a bashable object in our way, bash it down. else if ((!can_move_to(g, next.x, next.y) || one_in(3)) && g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) { std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug! int bashskill = int(type->melee_dice * type->melee_sides); g->m.bash(next.x, next.y, bashskill, bashsound); g->sound(next.x, next.y, 18, bashsound); moves -= 100; } //If there is a destroyable object in our way, destroy it. else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) { g->m.destroy(g, next.x, next.y, true); moves -= 250; } //If all else fails in our plan (an issue with pathfinding maybe) stumble around instead. else { stumble(g, moved); moves -= 100; } } }
/* Attempt to attack or move in a given direction, depending on context */ void attempt_act(level_t* level, player_t* player, int deltaX, int deltaY) { int newX = player->x + deltaX; int newY = player->y + deltaY; if (newX < 0 || newX >= level->width || newY < 0 || newY >= level->height) return; if (level->mon_map[newX][newY]) { hit_monster(level->mon_map[newX][newY], level, player); } else if (level->terrain[newX][newY] == '.') { player->x = newX; player->y = newY; } }
void missile(int ydelta, int xdelta) { struct object *obj; struct linked_list *item, *nitem; /* * Get which thing we are hurling */ if ((item = get_item("throw", WEAPON)) == NULL) return; obj = (struct object *) ldata(item); if (!dropcheck(obj) || is_current(obj)) return; /* * Get rid of the thing. If it is a non-multiple item object, or * if it is the last thing, just drop it. Otherwise, create a new * item with a count of one. */ if (obj->o_count < 2) { detach(pack, item); inpack--; } else { obj->o_count--; if (obj->o_group == 0) inpack--; nitem = (struct linked_list *) new_item(sizeof *obj); obj = (struct object *) ldata(nitem); *obj = *((struct object *) ldata(item)); obj->o_count = 1; item = nitem; } do_motion(obj, ydelta, xdelta); /* * AHA! Here it has hit something. If it is a wall or a door, * or if it misses (combat) the mosnter, put it on the floor */ if (!ismons(CMVWINCH(mw, obj->o_pos.y, obj->o_pos.x)) || !hit_monster(unc(obj->o_pos), obj)) fall(item, TRUE); mvwaddrawch(cw, hero.y, hero.x, PLAYER); }
void missile(int ydelta, int xdelta) { THING *obj; /* * Get which thing we are hurling */ if ((obj = get_item("throw", WEAPON)) == NULL) return; if (!dropcheck(obj) || is_current(obj)) return; obj = leave_pack(obj, TRUE, FALSE); do_motion(obj, ydelta, xdelta); /* * AHA! Here it has hit something. If it is a wall or a door, * or if it misses (combat) the monster, put it on the floor */ if (moat(obj->o_pos.y, obj->o_pos.x) == NULL || !hit_monster(unc(obj->o_pos), obj)) fall(obj, TRUE); }
void missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp) { struct object *obj; struct linked_list *nitem; if (item == NULL) /* Get which thing we are hurling */ return; obj = OBJPTR(item); if (!dropcheck(obj) || is_current(obj)) return; /* * Get rid of the thing. If it is a non-multiple item object, or if * it is the last thing, just drop it. Otherwise, create a new item * with a count of one. */ if (obj->o_count < 2) { if (tp->t_pack == pack) rem_pack(obj); else detach(tp->t_pack, item); } else { obj->o_count--; nitem = (struct linked_list *) new_item(sizeof *obj); obj = OBJPTR(nitem); *obj = *(OBJPTR(item)); obj->o_count = 1; item = nitem; } switch (obj->o_type) { case ARTIFACT: has_artifact &= ~(1 << obj->o_which); break; case SCROLL: if (obj->o_which == S_SCARE && obj->o_flags & ISBLESSED) obj->o_flags &= ~ISBLESSED; else obj->o_flags |= ISCURSED; } updpack(); obj->o_pos = do_motion(obj->o_type, ydelta, xdelta, tp); /* * AHA! Here it has hit something. If it is a wall or a door, or if * it misses (combat) the monster, put it on the floor */ if (!hit_monster(obj->o_pos.y, obj->o_pos.x, obj, tp)) { if (obj->o_type == WEAPON && obj->o_which == GRENADE) { hearmsg("BOOOM!"); aggravate(); if (ntraps + 1 < 2 * MAXTRAPS && fallpos(obj->o_pos, &traps[ntraps].tr_pos)) { mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x, TRAPDOOR); traps[ntraps].tr_type = TRAPDOOR; traps[ntraps].tr_flags = ISFOUND; traps[ntraps].tr_show = TRAPDOOR; ntraps++; light(&hero); } discard(item); } else if (obj->o_flags & ISLOST) { if (obj->o_type == WEAPON) addmsg("The %s", weaps[obj->o_which].w_name); else addmsg(inv_name(obj, LOWERCASE)); msg(" vanishes in a puff of greasy smoke."); discard(item); } else { fall(&player, item, TRUE, TRUE); if (obj->o_flags & CANRETURN) msg("You have %s.", inv_name(obj, LOWERCASE)); } } else if (obj->o_flags & ISOWNED) { add_pack(item, NOMESSAGE); msg("You have %s.", inv_name(obj, LOWERCASE)); } mvwaddch(cw, hero.y, hero.x, PLAYER); }
// General movement. // Currently, priority goes: // 1) Special Attack // 2) Sight-based tracking // 3) Scent-based tracking // 4) Sound-based tracking void monster::move(game *g) { // We decrement wandf no matter what. We'll save our wander_to plans until // after we finish out set_dest plans, UNLESS they time out first. if (wandf > 0) wandf--; // First, use the special attack, if we can! if (sp_timeout > 0) sp_timeout--; if (sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL))) { mattack ma; (ma.*type->sp_attack)(g, this); } if (moves < 0) return; if (has_flag(MF_IMMOBILE)) { moves = 0; return; } if (has_effect(ME_STUNNED)) { stumble(g, false); moves = 0; return; } if (has_effect(ME_DOWNED)) { moves = 0; return; } if (has_effect(ME_BOULDERING)){ moves -= 20; if (moves < 0) moves = 0; return; } if (friendly != 0) { if (friendly > 0) friendly--; friendly_move(g); return; } bool moved = false; point next; int mondex = (plans.size() > 0 ? g->mon_at(plans[0].x, plans[0].y) : -1); monster_attitude current_attitude = attitude(); if (friendly == 0) current_attitude = attitude(&(g->u)); // If our plans end in a player, set our attitude to consider that player if (plans.size() > 0) { if (plans.back().x == g->u.posx && plans.back().y == g->u.posy) current_attitude = attitude(&(g->u)); else { for (int i = 0; i < g->active_npc.size(); i++) { if (plans.back().x == g->active_npc[i]->posx && plans.back().y == g->active_npc[i]->posy) current_attitude = attitude((g->active_npc[i])); } } } if (current_attitude == MATT_IGNORE || (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) { moves -= 100; stumble(g, false); return; } if (plans.size() > 0 && !is_fleeing(g->u) && (mondex == -1 || g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON)) && (can_move_to(g, plans[0].x, plans[0].y) || (plans[0].x == g->u.posx && plans[0].y == g->u.posy) || (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){ // CONCRETE PLANS - Most likely based on sight next = plans[0]; moved = true; } else if (has_flag(MF_SMELLS)) { // No sight... or our plans are invalid (e.g. moving through a transparent, but // solid, square of terrain). Fall back to smell if we have it. point tmp = scent_move(g); if (tmp.x != -1) { next = tmp; moved = true; } } if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound point tmp = sound_move(g); if (tmp.x != posx || tmp.y != posy) { next = tmp; moved = true; } } // Finished logic section. By this point, we should have chosen a square to // move to (moved = true). if (moved) { // Actual effects of moving to the square we've chosen mondex = g->mon_at(next.x, next.y); int npcdex = g->npc_at(next.x, next.y); if (next.x == g->u.posx && next.y == g->u.posy && type->melee_dice > 0) hit_player(g, g->u); else if (mondex != -1 && g->z[mondex].type->species == species_hallu) { g->kill_mon(mondex); moves -= 100; } else if (mondex != -1 && type->melee_dice > 0 && this != &(g->z[mondex]) && (g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON))) hit_monster(g, mondex); else if (npcdex != -1 && type->melee_dice > 0) hit_player(g, *g->active_npc[npcdex]); else if ((!can_move_to(g, next.x, next.y) || one_in(3)) && g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) { std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug! int bashskill = int(type->melee_dice * type->melee_sides); g->m.bash(next.x, next.y, bashskill, bashsound); g->sound(next.x, next.y, 18, bashsound); moves -= 100; } else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) { g->m.destroy(g, next.x, next.y, true); moves -= 250; } else if (can_move_to(g, next.x, next.y) && g->is_empty(next.x, next.y)) move_to(g, next.x, next.y); else moves -= 100; } else moves -= 100; // If we're close to our target, we get focused and don't stumble if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) || !moved) stumble(g, moved); }
/* * missile: * Fire a missile in a given direction */ void missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp) { register struct object *obj ; register struct linked_list *nitem ; /* * Get which thing we are hurling */ if (item == NULL) { return ; } obj = (struct object *) ldata(item) ; if (!dropcheck(obj) || is_current(obj)) { return ; } /* * Get rid of the thing. If it is a non-multiple item object, or * if it is the last thing, just drop it. Otherwise, create a new * item with a count of one. */ if (obj->o_count < 2) { detach(tp->t_pack, item) ; if (tp->t_pack == pack) { inpack-- ; freeletter(item); } } else { obj->o_count-- ; nitem = (struct linked_list *) new_item(sizeof *obj) ; obj = (struct object *) ldata(nitem) ; *obj = *((struct object *) ldata(item)) ; obj->o_count = 1 ; item = nitem ; } if (obj->o_type == ARTIFACT) has_artifact &= ~(1 << obj->o_which); if (obj->o_type == SCROLL && obj->o_which == S_SCARE) { if (obj->o_flags & ISBLESSED) obj->o_flags &= ~ISBLESSED; else obj->o_flags |= ISCURSED; } updpack (FALSE); do_motion(obj, ydelta, xdelta, tp) ; /* * AHA! Here it has hit something. If it is a wall or a door, * or if it misses (combat) the monster, put it on the floor */ if (!hit_monster(unc(obj->o_pos), obj, tp)) { if (obj->o_type == WEAPON && obj->o_which == GRENADE) { register struct room *rp; static coord fpos; msg("BOOOM!"); aggravate(); rp = roomin(&obj->o_pos); if (ntraps + 1 < MAXTRAPS + MAXTRAPS && fallpos(&obj->o_pos, &fpos, TRUE)) { mvaddch(fpos.y, fpos.x, TRAPDOOR); traps[ntraps].tr_type = TRAPDOOR; traps[ntraps].tr_flags = ISFOUND; traps[ntraps].tr_show = TRAPDOOR; traps[ntraps].tr_pos.y = fpos.y; traps[ntraps++].tr_pos.x = fpos.x; light(&hero); } discard(item); } else if (obj->o_flags & ISLOST) { if (obj->o_type == WEAPON) addmsg("The %s", weaps[obj->o_which].w_name); else addmsg(inv_name(obj, TRUE)); msg(" vanishes in a puff of greasy smoke."); discard(item); } else { if (fall(item, TRUE)) if (obj->o_flags & CANRETURN) msg("You have %s.", inv_name(obj, TRUE)); } } else if (obj->o_flags & ISOWNED) { add_pack(item, TRUE); msg("You have %s.", inv_name(obj, TRUE)); } mvwaddch(cw, hero.y, hero.x, PLAYER) ; }