diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index 01529fe7dbb35..ad631de770c96 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -366,6 +366,16 @@ } ] }, + { + "id":"SEC_SELECT", + "name":"Select", + "bindings":[ + { + "input_method":"mouse", + "key":"MOUSE_RIGHT" + } + ] + }, { "id":"SELECT", "name":"Select", @@ -396,5 +406,15 @@ "key":"f" } ] + }, + { + "id":"TOGGLE_SNAP_TO_TARGET", + "name":"Toggle Snap to Target", + "bindings":[ + { + "input_method":"keyboard", + "key":"*" + } + ] } ] diff --git a/src/action.cpp b/src/action.cpp index 67619f182dab2..bc5e64c6cfbd0 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -758,3 +758,23 @@ std::string press_x(action_id act, std::string act_desc) } } +action_id get_movement_direction_from_delta(const int dx, const int dy) +{ + if (dx == 0 && dy == -1) { + return ACTION_MOVE_N; + } else if (dx == 1 && dy == -1) { + return ACTION_MOVE_NE; + } else if (dx == 1 && dy == 0) { + return ACTION_MOVE_E; + } else if (dx == 1 && dy == 1) { + return ACTION_MOVE_SE; + } else if (dx == 0 && dy == 1) { + return ACTION_MOVE_S; + } else if (dx == -1 && dy == 1) { + return ACTION_MOVE_SW; + } else if (dx == -1 && dy == 0) { + return ACTION_MOVE_W; + } else { + return ACTION_MOVE_NW; + } +} diff --git a/src/action.h b/src/action.h index e288f5192f364..fb9d366b0c92e 100644 --- a/src/action.h +++ b/src/action.h @@ -120,5 +120,7 @@ std::string press_x(action_id act, std::string key_bound_pre, // ('Z'ing|zing) (X( or Y))) std::string press_x(action_id act, std::string act_desc); +// Helper function to convert co-ordinate delta to a movement direction +action_id get_movement_direction_from_delta(const int dx, const int dy); #endif diff --git a/src/game.cpp b/src/game.cpp index 80c389de33fe2..3e35d4a8d6847 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1179,6 +1179,10 @@ bool game::cancel_activity_query(const char* message, ...) std::string stop_message = s + stop_phrase[u.activity.type]; if (ACT_NULL == u.activity.type) { + if (u.has_destination()) { + add_msg(_("You were hurt. Auto-move cancelled")); + u.clear_destination(); + } doit = false; } else if (query_yn(stop_message.c_str())) { doit = true; @@ -1733,15 +1737,15 @@ void game::hide_mouseview() } } - -bool game::handle_action() +input_context game::get_player_input(std::string &action) { input_context ctxt("DEFAULTMODE"); ctxt.register_directions(); ctxt.register_action("ANY_INPUT"); ctxt.register_action("COORDINATE"); ctxt.register_action("MOUSE_MOVE"); - std::string action; + ctxt.register_action("SELECT"); + ctxt.register_action("SEC_SELECT"); char cGlyph = ','; nc_color colGlyph = c_ltblue; @@ -1862,44 +1866,166 @@ bool game::handle_action() while (handle_mouseview(ctxt, action)) {;} } + return ctxt; +} + +bool game::handle_action() +{ + std::string action; + input_context ctxt; + action_id act = ACTION_NULL; - int ch = ctxt.get_raw_input().get_first_input(); - // Hack until new input system is fully implemented - if (ch == KEY_UP) { - act = ACTION_MOVE_N; - } else if (ch == KEY_RIGHT) { - act = ACTION_MOVE_E; - } else if (ch == KEY_DOWN) { - act = ACTION_MOVE_S; - } else if (ch == KEY_LEFT) { - act = ACTION_MOVE_W; - } else if (ch == KEY_NPAGE) { - act = ACTION_MOVE_DOWN; - } else if (ch == KEY_PPAGE) { - act = ACTION_MOVE_UP; + // Check if we have an auto-move destination + if (u.has_destination()) { + act = u.get_next_auto_move_direction(); + if (act == ACTION_NULL) { + add_msg(_("Auto-move cancelled")); + u.clear_destination(); + return false; + } } else { - if (keymap.find(ch) == keymap.end()) { - if (ch != ' ' && ch != '\n') { - add_msg(_("Unknown command: '%c'"), ch); + // No auto-move, ask player for input + ctxt = get_player_input(action); + } + + int veh_part; + vehicle *veh = m.veh_at(u.posx, u.posy, veh_part); + bool veh_ctrl = veh && veh->player_in_control (&u); + + // If performing an action with right mouse button, co-ordinates + // of location clicked. + int mouse_action_x = -1, mouse_action_y = -1; + + if (act == ACTION_NULL) { + if (action == "SELECT" || action == "SEC_SELECT") { + // Mouse button click + if (veh_ctrl) { + // No mouse use in vehicle + return false; + } + + int mx, my; + if (!ctxt.get_coordinates(w_terrain, mx, my) || !u_see(mx, my)) { + // Not clicked in visible terrain + return false; + } + + if (action == "SELECT") { + bool new_destination = true; + if (destination_preview.size() > 0) { + point final_destination = destination_preview.back(); + if (final_destination.x == mx && final_destination.y == my) { + // Second click + new_destination = false; + u.set_destination(destination_preview); + destination_preview.clear(); + act = u.get_next_auto_move_direction(); + if (act == ACTION_NULL) { + // Something went wrong + u.clear_destination(); + return false; + } + } + } + + if (new_destination) { + destination_preview = m.route(u.posx, u.posy, mx, my, false); + return false; + } + } else { + // Right mouse button + + bool had_destination_to_clear = destination_preview.size() > 0; + u.clear_destination(); + destination_preview.clear(); + + if (had_destination_to_clear) { + return false; + } + + mouse_action_x = mx; + mouse_action_y = my; + int mouse_selected_mondex = mon_at(mx, my); + if (mouse_selected_mondex != -1) { + monster &z = _active_monsters[mouse_selected_mondex]; + if (!u_see(&z)) { + add_msg(_("Nothing relevant here.")); + return false; + } + + if (!u.weapon.is_gun()) { + add_msg(_("You are not wielding a ranged weapon.")); + return false; + } + + //TODO: Add weapon range check. This requires weapon to be reloaded. + + act = ACTION_FIRE; + } else if (m.close_door(mx, my, !m.is_outside(mx, my), true)) { + act = ACTION_CLOSE; + } else { + int dx = abs(u.posx - mx); + int dy = abs(u.posy - my); + if (dx < 2 && dy < 2) { + if (dy == 0 && dx == 0) { + // Clicked on self + act = ACTION_PICKUP; + } else { + // Clicked adjacent tile + act = ACTION_EXAMINE; + } + } else { + add_msg(_("Nothing relevant here.")); + return false; + } + } } - return false; } + } + + if (act == ACTION_NULL) { + // No auto-move action, no mouse clicks. + u.clear_destination(); + destination_preview.clear(); - act = keymap[ch]; + int ch = ctxt.get_raw_input().get_first_input(); + // Hack until new input system is fully implemented + if (ch == KEY_UP) { + act = ACTION_MOVE_N; + } else if (ch == KEY_RIGHT) { + act = ACTION_MOVE_E; + } else if (ch == KEY_DOWN) { + act = ACTION_MOVE_S; + } else if (ch == KEY_LEFT) { + act = ACTION_MOVE_W; + } else if (ch == KEY_NPAGE) { + act = ACTION_MOVE_DOWN; + } else if (ch == KEY_PPAGE) { + act = ACTION_MOVE_UP; + } else { + if (keymap.find(ch) == keymap.end()) { + if (ch != ' ' && ch != '\n') { + add_msg(_("Unknown command: '%c'"), ch); + } + return false; + } + + act = keymap[ch]; + } } // This has no action unless we're in a special game mode. gamemode->pre_action(this, act); - int veh_part; - vehicle *veh = m.veh_at(u.posx, u.posy, veh_part); - bool veh_ctrl = veh && veh->player_in_control (&u); - int soffset = (int)OPTIONS["MOVE_VIEW_OFFSET"]; int soffsetr = 0 - soffset; int before_action_moves = u.moves; + // Use to track if auto-move should be cancelled due to a failed + // move or obstacle + bool continue_auto_move = false; + switch (act) { case ACTION_PAUSE: @@ -1916,7 +2042,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(0, -1); else - plmove(0, -1); + continue_auto_move = plmove(0, -1); break; case ACTION_MOVE_NE: @@ -1925,7 +2051,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(1, -1); else - plmove(1, -1); + continue_auto_move = plmove(1, -1); break; case ACTION_MOVE_E: @@ -1934,7 +2060,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(1, 0); else - plmove(1, 0); + continue_auto_move = plmove(1, 0); break; case ACTION_MOVE_SE: @@ -1943,7 +2069,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(1, 1); else - plmove(1, 1); + continue_auto_move = plmove(1, 1); break; case ACTION_MOVE_S: @@ -1952,7 +2078,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(0, 1); else - plmove(0, 1); + continue_auto_move = plmove(0, 1); break; case ACTION_MOVE_SW: @@ -1961,7 +2087,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(-1, 1); else - plmove(-1, 1); + continue_auto_move = plmove(-1, 1); break; case ACTION_MOVE_W: @@ -1970,7 +2096,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(-1, 0); else - plmove(-1, 0); + continue_auto_move = plmove(-1, 0); break; case ACTION_MOVE_NW: @@ -1979,7 +2105,7 @@ bool game::handle_action() if (veh_ctrl) pldrive(-1, -1); else - plmove(-1, -1); + continue_auto_move = plmove(-1, -1); break; case ACTION_MOVE_DOWN: @@ -2038,7 +2164,7 @@ bool game::handle_action() break; case ACTION_CLOSE: - close(); + close(mouse_action_x, mouse_action_y); break; case ACTION_SMASH: @@ -2049,7 +2175,7 @@ bool game::handle_action() break; case ACTION_EXAMINE: - examine(); + examine(mouse_action_x, mouse_action_y); break; case ACTION_ADVANCEDINV: @@ -2175,11 +2301,11 @@ bool game::handle_action() break; case ACTION_FIRE: - plfire(false); + plfire(false, mouse_action_x, mouse_action_y); break; case ACTION_FIRE_BURST: - plfire(true); + plfire(true, mouse_action_x, mouse_action_y); break; case ACTION_SELECT_FIRE_MODE: @@ -2415,6 +2541,10 @@ bool game::handle_action() break; } + if (!continue_auto_move) { + u.clear_destination(); + } + gamemode->post_action(this, act); u.movecounter = before_action_moves - u.moves; @@ -3940,6 +4070,12 @@ void game::draw_ter(int posx, int posy) } } + if (destination_preview.size() > 0) { + // Draw auto-move preview trail + point final_destination = destination_preview.back(); + draw_line(final_destination.x, final_destination.y, destination_preview); + } + wrefresh(w_terrain); if (u.has_disease("visuals") || (u.has_disease("hot_head") && @@ -6154,11 +6290,13 @@ void game::open() } } -void game::close() +void game::close(int closex, int closey) { - int closex, closey; - if (!choose_adjacent(_("Close where?"), closex, closey)) - return; + if (closex == -1) { + if (!choose_adjacent(_("Close where?"), closex, closey)) { + return; + } + } bool didit = false; @@ -6198,7 +6336,7 @@ void game::close() } else if (m.has_furn(closex, closey) && m.furn_at(closex, closey).close.size() == 0 ) { add_msg(_("There's a %s in the way!"), m.furnname(closex, closey).c_str()); } else - didit = m.close_door(closex, closey, true); + didit = m.close_door(closex, closey, true, false); if (didit) u.moves -= 90; @@ -6696,11 +6834,13 @@ void game::control_vehicle() } } -void game::examine() +void game::examine(int examx, int examy) { - int examx, examy; - if (!choose_adjacent(_("Examine where?"), examx, examy)) - return; + if (examx == -1) { + if (!choose_adjacent(_("Examine where?"), examx, examy)) { + return; + } + } int veh_part = 0; vehicle *veh = m.veh_at (examx, examy, veh_part); @@ -9255,7 +9395,7 @@ void game::plthrow(char chInput) throw_item(u, x, y, thrown, trajectory); } -void game::plfire(bool burst) +void game::plfire(bool burst, int default_target_x, int default_target_y) { char reload_invlet = 0; if (!u.weapon.is_gun()) @@ -9349,7 +9489,8 @@ void game::plfire(bool burst) if(rl_dist( u.posx, u.posy, z.posx(), z.posy() ) <= range) { mon_targets.push_back(z); targetindices.push_back(i); - if (i == last_target) { + bool is_default_target = default_target_x == z.posx() && default_target_y == z.posy(); + if (is_default_target || (passtarget == -1 && i == last_target)) { passtarget = mon_targets.size() - 1; } } @@ -10159,14 +10300,14 @@ void game::pldrive(int x, int y) { } } -void game::plmove(int dx, int dy) +bool game::plmove(int dx, int dy) { if (run_mode == 2) { // Monsters around and we don't wanna run add_msg(_("Monster spotted--safe mode is on! \ (%s to turn it off or %s to ignore monster.)"), press_x(ACTION_TOGGLE_SAFEMODE).c_str(), from_sentence_case(press_x(ACTION_IGNORE_ENEMY)).c_str()); - return; + return false; } int x = 0; int y = 0; @@ -10193,12 +10334,17 @@ void game::plmove(int dx, int dy) if (mondex != -1) { monster &z = zombie(mondex); if (z.friendly == 0 && !(z.type->has_flag(MF_VERMIN))) { + if (u.has_destination()) { + add_msg(_("Monster in the way. Auto-move canceled. Click directly on monster to attack.")); + u.clear_destination(); + return false; + } int udam = u.hit_mon(this, &z); if (z.hurt(udam) || z.is_hallucination()) { kill_mon(mondex, true); } draw_hit_mon(x,y,z,z.dead); - return; + return false; } else { displace = true; } @@ -10206,6 +10352,7 @@ void game::plmove(int dx, int dy) // If not a monster, maybe there's an NPC there int npcdex = npc_at(x, y); if (npcdex != -1) { + bool force_attack = false; if(!active_npc[npcdex]->is_enemy()){ if (!query_yn(_("Really attack %s?"), active_npc[npcdex]->name.c_str())) { if (active_npc[npcdex]->is_friend()) { @@ -10213,20 +10360,27 @@ void game::plmove(int dx, int dy) active_npc[npcdex]->move_away_from(this, u.posx, u.posy); } - return; // Cancel the attack + return false; // Cancel the attack } else { //The NPC knows we started the fight, used for morale penalty. active_npc[npcdex]->hit_by_player = true; + force_attack = true; } } + if (u.has_destination() && !force_attack) { + add_msg(_("NPC in the way. Auto-move canceled. Click directly on NPC to attack.")); + u.clear_destination(); + return false; + } + u.hit_player(this, *active_npc[npcdex]); active_npc[npcdex]->make_angry(); if (active_npc[npcdex]->hp_cur[hp_head] <= 0 || active_npc[npcdex]->hp_cur[hp_torso] <= 0 ) { active_npc[npcdex]->die(this, true); } - return; + return false; } // Otherwise, actual movement, zomg @@ -10246,7 +10400,7 @@ void game::plmove(int dx, int dy) } if (newdist > curdist) { add_msg(_("You cannot pull yourself away from the faultline...")); - return; + return false; } } @@ -10254,7 +10408,7 @@ void game::plmove(int dx, int dy) if (rng(0, 40) > u.str_cur + int(u.dex_cur / 2)) { add_msg(_("You try to escape the pit, but slip back in.")); u.moves -= 100; - return; + return false; } else { add_msg(_("You escape the pit!")); u.rem_disease("in_pit"); @@ -10264,12 +10418,12 @@ void game::plmove(int dx, int dy) if (rng(0, 40) > u.dex_cur + int(u.str_cur / 2)) { add_msg(_("You struggle to stand.")); u.moves -= 100; - return; + return false; } else { add_msg(_("You stand up.")); u.rem_disease("downed"); u.moves -= 100; - return; + return false; } } @@ -10319,13 +10473,13 @@ void game::plmove(int dx, int dy) if (query_yn(_("Dive from moving vehicle?"))) { moving_vehicle_dismount(x, y); } - return; + return false; } else if (veh1 != veh0) { add_msg(_("There is another vehicle in the way.")); - return; + return false; } else if (veh1->part_with_feature(vpart1, "BOARDABLE") < 0) { add_msg(_("That part of the vehicle is currently unsafe.")); - return; + return false; } } @@ -10360,7 +10514,7 @@ void game::plmove(int dx, int dy) break; } if ((dangerous) && !query_yn(_("Really step into that %s?"), cur->name().c_str())) { - return; + return false; } } @@ -10368,7 +10522,7 @@ void game::plmove(int dx, int dy) u.per_cur - u.encumb(bp_eyes) >= traps[m.tr_at(x, y)]->visibility){ if ( !traps[m.tr_at(x, y)]->is_benign() && !query_yn(_("Really step onto that %s?"),traps[m.tr_at(x, y)]->name.c_str())){ - return; + return false; } } @@ -10381,13 +10535,13 @@ void game::plmove(int dx, int dy) if( NULL != grabbed_vehicle ) { if( grabbed_vehicle == veh0 ) { add_msg(_("You can't move %s while standing on it!"), grabbed_vehicle->name.c_str()); - return; + return false; } drag_multiplier += (float)(grabbed_vehicle->total_mass() * 1000) / (float)(u.weight_capacity() * 5); if( drag_multiplier > 2.0 ) { add_msg(_("The %s is too heavy for you to budge!"), grabbed_vehicle->name.c_str()); - return; + return false; } tileray mdir; @@ -10435,7 +10589,7 @@ void game::plmove(int dx, int dy) u.posy = player_prev_y; u.grab_point.x = prev_grab_x; u.grab_point.y = prev_grab_y; - return; + return false; } u.posx = player_prev_x; u.posy = player_prev_y; @@ -10495,15 +10649,15 @@ void game::plmove(int dx, int dy) if ( ! canmove ) { add_msg( _("The %s collides with something."), furntype.name.c_str() ); u.moves -= 50; // "oh was that your foot? Sorry :-O" - return; + return false; } else if ( ! m.can_move_furniture( fpos.x, fpos.y, &u ) ) { add_msg(_("The %s is too heavy for you to budge!"), furntype.name.c_str() ); u.moves -= 100; // time spent straining and going 'hnngh!' - return; // furniture and or obstacle wins. + return false; // furniture and or obstacle wins. } else if ( ! src_item_ok && dst_items > 0 ) { add_msg( _("There's stuff in the way.") ); u.moves -= 50; // "oh was that your stuffed parrot? Sorry :-O" - return; + return false; } if ( pulling_furniture ) { // normalize movecost for pulling: furniture moves into our current square -then- we move away @@ -10547,7 +10701,7 @@ void game::plmove(int dx, int dy) u.grab_point = point (0, 0); u.grab_type = OBJECT_NONE; } - return; // We moved furniture but stayed still. + return false; // We moved furniture but stayed still. } else if ( pushing_furniture && m.move_cost(x, y) <= 0 ) { // Not sure how that chair got into a wall, but don't let player follow. add_msg( _("You let go of the %s as it slides past %s"), furntype.name.c_str(), m.ter_at(x,y).name.c_str() ); u.grab_point = point (0, 0); @@ -10626,10 +10780,10 @@ void game::plmove(int dx, int dy) u.moves -= 100; m.spawn_item(x, y, "bot_turret", 1, 0, turn); } - return; + return false; } else { add_msg(_("You can't displace your %s."), z.name().c_str()); - return; + return false; } } z.move_to(this, u.posx, u.posy, true); // Force the movement even though the player is there right now. @@ -10822,6 +10976,7 @@ void game::plmove(int dx, int dy) else //or you couldn't tunnel due to lack of energy { u.power_level -= 10; //failure is expensive! + return false; } } else if (veh_closed_door) { // move_cost <= 0 @@ -10854,7 +11009,10 @@ void game::plmove(int dx, int dy) u.moves -= 80; add_msg(_("You rattle the bars but the door is locked!")); } + return false; } + + return true; } void game::plswim(int x, int y) @@ -11310,8 +11468,10 @@ void game::update_map(int &x, int &y) { set_adjacent_overmaps(); // Shift monsters if we're actually shifting - if(shiftx || shifty) + if (shiftx || shifty) { despawn_monsters(shiftx, shifty); + u.shift_destination(-shiftx * SEEX, -shifty * SEEY); + } // Shift NPCs for (int i = 0; i < active_npc.size(); i++) { diff --git a/src/game.h b/src/game.h index 55272f375e077..b8e08e8903c60 100644 --- a/src/game.h +++ b/src/game.h @@ -199,7 +199,7 @@ class game void revive_corpse(int x, int y, item *it); // revives a corpse by item pointer, caller handles item deletion // hit_monster_with_flags processes ammo flags (e.g. incendiary, etc) void hit_monster_with_flags(monster &z, const std::set &effects); - void plfire(bool burst); // Player fires a gun (target selection)... + void plfire(bool burst, int default_target_x = -1, int default_target_y = -1); // Player fires a gun (target selection)... // ... a gun is fired, maybe by an NPC (actual damage, etc.). void fire(player &p, int tarx, int tary, std::vector &trajectory, bool burst); @@ -444,10 +444,12 @@ class game void mutation_wish(); // Mutate void pldrive(int x, int y); // drive vehicle - void plmove(int x, int y); // Standard movement; handles attacks, traps, &c + // Standard movement; handles attacks, traps, &c. Returns false if auto move + // should be canceled + bool plmove(int dx, int dy); void wait(); // Long wait (player action) '^' void open(); // Open a door 'o' - void close(); // Close a door 'c' + void close(int closex = -1, int closey = -1); // Close a door 'c' void smash(); // Smash terrain void craft(); // See crafting.cpp void recraft(); // See crafting.cpp @@ -478,7 +480,7 @@ class game bool vehicle_near (); void handbrake (); void control_vehicle(); // Use vehicle controls '^' - void examine();// Examine nearby terrain 'e' + void examine(int examx = -1, int examy = -1);// Examine nearby terrain 'e' void advanced_inv(); // open vehicle interaction screen void exam_vehicle(vehicle &veh, int examx, int examy, int cx=0, int cy=0); @@ -516,6 +518,7 @@ class game void handle_multi_item_info(int lx, int ly, WINDOW* w_look, const int column, int &line, bool mouse_hover); void get_lookaround_dimensions(int &lookWidth, int &begin_y, int &begin_x) const; + input_context get_player_input(std::string &action); // Target is an interactive function which allows the player to choose a nearby // square. It display information on any monster/NPC on that square, and also // returns a Bresenham line to that square. It is called by plfire() and @@ -585,8 +588,8 @@ class game signed char last_target; // The last monster targeted int run_mode; // 0 - Normal run always; 1 - Running allowed, but if a new + // monsters spawns, go to 2 - No movement allowed std::vector new_seen_mon; - // monsters spawns, go to 2 - No movement allowed int mostseen; // # of mons seen last turn; if this increases, run_mode++ bool autosafemode; // is autosafemode enabled? int turnssincelastmon; // needed for auto run mode @@ -614,6 +617,9 @@ class game int moveCount; //Times the player has moved (not pause, sleep, etc) const int lookHeight; // Look Around window height + // Preview for auto move route + std::vector destination_preview; + bool is_hostile_within(int distance); }; diff --git a/src/map.cpp b/src/map.cpp index 809e8f18c4808..73ca861e42543 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2064,7 +2064,7 @@ void map::translate_radius(const ter_id from, const ter_id to, float radi, int u } } -bool map::close_door(const int x, const int y, const bool inside) +bool map::close_door(const int x, const int y, const bool inside, const bool check_only) { const std::string terid = get_ter(x,y); const std::string furnid = furnlist[furn(x,y)].id; @@ -2076,7 +2076,9 @@ bool map::close_door(const int x, const int y, const bool inside) if ( has_flag("OPENCLOSE_INSIDE", x, y) && inside == false ) { return false; } - ter_set(x, y, termap[ terid ].close ); + if (!check_only) { + ter_set(x, y, termap[ terid ].close ); + } return true; } else if ( furnmap[ furnid ].close.size() > 0 && furnmap[ furnid ].close != "t_null" ) { if ( furnmap.find( furnmap[ furnid ].close ) == furnmap.end() ) { @@ -2086,7 +2088,9 @@ bool map::close_door(const int x, const int y, const bool inside) if ( has_flag("OPENCLOSE_INSIDE", x, y) && inside == false ) { return false; } - furn_set(x, y, furnmap[ furnid ].close ); + if (!check_only) { + furn_set(x, y, furnmap[ furnid ].close ); + } return true; } return false; diff --git a/src/map.h b/src/map.h index 89fa55d493dc9..1553f87de24a8 100644 --- a/src/map.h +++ b/src/map.h @@ -255,7 +255,7 @@ class map void translate_radius(const std::string terfrom, const std::string terto, const float radi, const int uX, const int uY); void translate(const ter_id from, const ter_id to); // Change all instances of $from->$to void translate_radius(const ter_id from, const ter_id to, const float radi, const int uX, const int uY); - bool close_door(const int x, const int y, const bool inside); + bool close_door(const int x, const int y, const bool inside, const bool check_only); bool open_door(const int x, const int y, const bool inside); // bash: if res pointer is supplied, res will contain absorbed impact or -1 bool bash(const int x, const int y, const int str, std::string &sound, int *res = 0); diff --git a/src/player.cpp b/src/player.cpp index 3cef433e18710..efe608710e4aa 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -140,6 +140,8 @@ player::player() : name("") sight_boost = 0; sight_boost_cap = 0; lastrecipe = NULL; + next_expected_position.x = -1; + next_expected_position.y = -1; for (std::map::iterator iter = traits.begin(); iter != traits.end(); ++iter) { my_traits.erase(iter->first); @@ -8992,4 +8994,69 @@ void player::environmental_revert_effect() recalc_sight_limits(); } + +void player::set_destination(const std::vector &route) +{ + auto_move_route = route; +} + +void player::clear_destination() +{ + auto_move_route.clear(); + next_expected_position.x = -1; + next_expected_position.y = -1; +} + +bool player::has_destination() const +{ + return auto_move_route.size() > 0; +} + +std::vector &player::get_auto_move_route() +{ + return auto_move_route; +} + +action_id player::get_next_auto_move_direction() +{ + if (!has_destination()) { + return ACTION_NULL; + } + + if (next_expected_position.x != -1) { + if (posx != next_expected_position.x || posy != next_expected_position.y) { + // We're off course, possibly stumbling or stuck, cancel auto move + return ACTION_NULL; + } + } + + next_expected_position = auto_move_route.front(); + auto_move_route.erase(auto_move_route.begin()); + + int dx = next_expected_position.x - posx; + int dy = next_expected_position.y - posy; + + if (abs(dx) > 1 || abs(dy) > 1) { + // Should never happen, but check just in case + return ACTION_NULL; + } + + return get_movement_direction_from_delta(dx, dy); +} + +void player::shift_destination(int shiftx, int shifty) +{ + if (next_expected_position.x != -1) { + next_expected_position.x += shiftx; + next_expected_position.y += shifty; + } + + for (std::vector::iterator it = auto_move_route.begin(); it != auto_move_route.end(); it++) { + it->x += shiftx; + it->y += shifty; + } +} + + + // --- End --- diff --git a/src/player.h b/src/player.h index 2c54edbf72397..4cc96996a4b4b 100644 --- a/src/player.h +++ b/src/player.h @@ -16,6 +16,7 @@ #include "martialarts.h" #include "json.h" +#include "action.h" #include #include #include @@ -431,6 +432,14 @@ class player : public JsonSerializer, public JsonDeserializer bool studied_all_recipes(it_book *book); bool try_study_recipe(game *g, it_book *book); + // Auto move methods + void set_destination(const std::vector &route); + void clear_destination(); + bool has_destination() const; + std::vector &get_auto_move_route(); + action_id get_next_auto_move_direction(); + void shift_destination(int shiftx, int shifty); + // Library functions double logistic(double t); double logistic_range(int min, int max, int pos); @@ -556,6 +565,10 @@ class player : public JsonSerializer, public JsonDeserializer bool has_fire(const int quantity); void use_fire(const int quantity); + std::vector auto_move_route; + // Used to make sure auto move is canceled if we stumble off course + point next_expected_position; + int id; // A unique ID number, assigned by the game class private so it cannot be overwritten and cause save game corruptions. //NPCs also use this ID value. Values should never be reused. }; diff --git a/src/ranged.cpp b/src/ranged.cpp index 04b1a2424b87e..68dc5f1930875 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -854,8 +854,15 @@ std::vector game::target(int &x, int &y, int lowx, int lowy, int hix, // Our coordinates will either be determined by coordinate input(mouse), // by a direction key, or by the previous value. if (action == "SELECT" && ctxt.get_coordinates(g->w_terrain, tarx, tary)) { - tarx = tarx - x; - tary = tary - y; + if (!OPTIONS["USE_TILES"] && snap_to_target) { + // Snap to target doesn't currently work with tiles. + tarx += x - u.posx; + tary += y - u.posy; + } + tarx -= x; + tary -= y; + + } else { ctxt.get_direction(tarx, tary, action); if(tarx == -2) {