Logo Search packages:      
Sourcecode: widelands version File versions  Download package

interactive_base.cc

/*
 * Copyright (C) 2002-2004, 2006-2007 by the Widelands Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "interactive_base.h"

#include "cmd_queue.h"
#include "constants.h"
#include "error.h"
#include "font_handler.h"
#include "game.h"
#include "graphic.h"
#include "immovable.h"
#include "interactive_player.h"
#include "keycodes.h"
#include "maptriangleregion.h"
#include "mapviewpixelconstants.h"
#include "mapviewpixelfunctions.h"
#include "minimap.h"
#include "mapviewpixelconstants.h"
#include "mapviewpixelfunctions.h"
#include "overlay_manager.h"
#include "player.h"
#include "profile.h"
#include "transport.h"
#include "wlapplication.h"

Interactive_Base::Interactive_Base(Editor_Game_Base & the_egbase)
:
Map_View(0, 0, 0, get_xres(), get_yres(), *this),
m_egbase(the_egbase)
{
      warpview.set(this, &Interactive_Player::mainview_move);

      {
            Section & s = *g_options.pull_section("global");
            set_border_snap_distance(s.get_int("border_snap_distance", 0));
            set_panel_snap_distance (s.get_int("panel_snap_distance",  0));
            set_snap_windows_only_when_overlapping
                  (s.get_bool("snap_windows_only_when_overlapping", false));
            set_dock_windows_to_edges(s.get_bool("dock_windows_to_edges", false));

            // Switch to the new graphics system now, if necessary
            WLApplication::get()->init_graphics
                  (get_xres(), get_yres(),
                   s.get_int("depth", 16), s.get_bool("fullscreen", false));

            m_show_workarea_preview = s.get_bool("workareapreview", false);
      }

      //  Having this in the initializer list (before Sys_InitGraphics) will given
      //  funny results.
      m_sel.pic = g_gr->get_picture(PicMod_Game, "pics/fsel.png"),

   m_display_flags = 0;

#ifdef DEBUG
   // Not in releases
      m_display_flags = dfDebug;
#endif

      m_lastframe = WLApplication::get()->get_time();
      m_frametime = 0;
      m_avg_usframetime = 0;

   m_mm=0;

   m_road_buildhelp_overlay_jobid = Overlay_Manager::Job_Id::Null();
   m_jobid = Overlay_Manager::Job_Id::Null();
      m_buildroad = false;
   m_road_build_player=0;
}

/*
===============
Interactive_Base::~Interactive_Base

cleanups
===============
*/
Interactive_Base::~Interactive_Base()
{
      if (m_buildroad)
            abort_build_road();
}


void Interactive_Base::set_sel_pos(const Node_and_Triangle<> center)
{
      Map & map = egbase().map();
      Overlay_Manager & overlay_manager = map.overlay_manager();

      // Remove old sel pointer
      if (not m_sel.jobid.isNull()) overlay_manager.remove_overlay(m_sel.jobid);
      const Overlay_Manager::Job_Id jobid =
            m_sel.jobid = overlay_manager.get_a_job_id();

      m_sel.pos = center;

   // register sel overlay position
      if (m_sel.triangles) {
            assert(center.triangle.t == TCoords<>::D or center.triangle.t == TCoords<>::R);
            MapTriangleRegion<> mr
                  (map, Area<TCoords<> >(center.triangle, m_sel.radius));
            do overlay_manager.register_overlay
                  (mr.location(), m_sel.pic, 7, Point::invalid(), jobid);
            while (mr.advance(map));
      } else {
            MapRegion<> mr(map, Area<>(center.node, m_sel.radius));
            do overlay_manager.register_overlay
                  (mr.location(), m_sel.pic, 7, Point::invalid(), jobid);
            while (mr.advance(map));
      }

}

/*
 * Set the current sel selection radius.
 */
void Interactive_Base::set_sel_radius(const uint n) {
      if (n != m_sel.radius) {
            m_sel.radius = n;
            set_sel_pos(get_sel_pos()); //  redraw
      }
}

/*
 *  [ protected functions ]
 *
 * Set/Unset sel picture
 */
void Interactive_Base::set_sel_picture(const char * const file) {
   m_sel.pic = g_gr->get_picture(PicMod_Game, file);
   set_sel_pos(get_sel_pos()); //  redraw
}
void Interactive_Base::unset_sel_picture() {
   set_sel_picture("pics/fsel.png");
}


/*
===============
Interactive_Base::get_xres [static]
Interactive_Base::get_yres [static]

Retrieve in-game resolution from g_options.
===============
*/
int Interactive_Base::get_xres()
{
      return g_options.pull_section("global")->get_int("xres", 640);
}

int Interactive_Base::get_yres()
{
      return g_options.pull_section("global")->get_int("yres", 480);
}


/**
 * Called by \ref Game::postload at the end of the game loading
 * sequence.
 *
 * Default implementation does nothing.
 */
00181 void Interactive_Base::postload()
{
}


/*
===============
Interactive_Base::think

Called once per frame by the UI code
===============
*/
00193 void Interactive_Base::think()
{
      // Timing
      uint curframe = WLApplication::get()->get_time();

      m_frametime = curframe - m_lastframe;
      m_avg_usframetime = ((m_avg_usframetime * 15) + (m_frametime * 1000)) / 16;
      m_lastframe = curframe;

   // If one of the arrow keys is pressed,
   // scroll here
   const uint scrollval = 10;

   WLApplication *app=WLApplication::get();

      if (keyboard_free()) {
            if (app->get_key_state(KEY_UP))
                  set_rel_viewpoint(Point(0, -scrollval));
            if (app->get_key_state(KEY_DOWN))
                  set_rel_viewpoint(Point(0,  scrollval));
            if (app->get_key_state(KEY_LEFT))
                  set_rel_viewpoint(Point(-scrollval, 0));
            if (app->get_key_state(KEY_RIGHT))
                  set_rel_viewpoint(Point (scrollval, 0));
      }

   // Call game logic here
   // The game advances
      egbase().think();

   // Update everythink so and so many milliseconds, to make sure the whole
   // screen is synced (another user may have done something, and the screen was
   // not redrawn)
   if (curframe & 1023) // % 1024
      need_complete_redraw();

      // The entire screen needs to be redrawn (unit movement, tile animation, etc...)
      g_gr->update_fullscreen();

      // some of the UI windows need to think()
      UI::Panel::think();
}


/*
===============
Interactive_Base::draw_overlay

Draw debug overlay when appropriate.
===============
*/
void Interactive_Base::draw_overlay(RenderTarget & dst) {
      if
            (get_display_flag(dfDebug)
             or
             not dynamic_cast<const Game *>(&egbase()))
      {
      // Show sel coordinates
      char buf[100];

            snprintf(buf, sizeof(buf), "%3i %3i", m_sel.pos.node.x, m_sel.pos.node.y);
            g_fh->draw_string
                  (dst, UI_FONT_BIG, UI_FONT_BIG_CLR, Point(5, 5), buf, Align_Left);
         assert(m_sel.pos.triangle.t < 2);
         const char * const triangle_string[] = {"down", "right"};
            snprintf
                  (buf, sizeof(buf),
                   "%3i %3i %s",
                   m_sel.pos.triangle.x, m_sel.pos.triangle.y,
                   triangle_string[m_sel.pos.triangle.t]);
            g_fh->draw_string
                  (dst, UI_FONT_BIG, UI_FONT_BIG_CLR, Point(5, 25), buf, Align_Left);
      }

   if (get_display_flag(dfDebug))
   {
      // Show FPS
      char buf[100];
            sprintf(buf, "%5.1f fps (avg: %5.1f fps)",
                        1000.0 / m_frametime, 1000.0 / (m_avg_usframetime / 1000));
            g_fh->draw_string
                  (dst, UI_FONT_BIG, UI_FONT_BIG_CLR, Point(85, 5), buf, Align_Left);
      }
}


/** Interactive_Base::mainview_move(int x, int y)
 *
 * Signal handler for the main view's warpview updates the mini map's
 * viewpos marker position
 */
00284 void Interactive_Base::mainview_move(int x, int y)
{
      if (m_minimap.window) {
            const Map & map = egbase().map();
            const int maxx = MapviewPixelFunctions::get_map_end_screen_x(map);
            const int maxy = MapviewPixelFunctions::get_map_end_screen_y(map);

            x += get_w() >> 1;
      if (x >= maxx) x -= maxx;
            y += get_h() >> 1;
      if (y >= maxy) y -= maxy;


      m_mm->set_view_pos(x, y);
      }
}


/*
===============
Interactive_Base::minimap_warp

Called whenever the player clicks on a location on the minimap.
Warps the main mapview position to the clicked location.
===============
*/
void Interactive_Base::minimap_warp(int x, int y)
{
      x -= get_w() >> 1;
      y -= get_h() >> 1;
      const Map & map = egbase().map();
      if (x < 0) x += map.get_width () * TRIANGLE_WIDTH;
      if (y < 0) y += map.get_height() * TRIANGLE_HEIGHT;
      set_viewpoint(Point(x, y));
}


/*
===============
Interactive_Base::move_view_to

Move the mainview to the given position (in field coordinates)
===============
*/
void Interactive_Base::move_view_to(const Coords c)
{
      int x = c.x * TRIANGLE_WIDTH;
      int y = c.y * TRIANGLE_HEIGHT;

      if (m_minimap.window) m_mm->set_view_pos(x, y);

      minimap_warp(x, y);
}


/*
===============
Interactive_Base::move_view_to_point

Center the mainview on the given position (in pixels)
===============
*/
void Interactive_Base::move_view_to_point(Point pos)
{
      if (m_minimap.window) m_mm->set_view_pos(pos.x, pos.y);

      set_viewpoint(pos - Point(get_w() / 2, get_h() / 2));
}


/*
===========
Interactive_Base::toggle_minimap()

Open the minimap or close it if it's open
===========
*/
void Interactive_Base::toggle_minimap() {
      if (m_minimap.window) {
            delete m_minimap.window;
      }
      else {
            m_mm = new MiniMap(*this, &m_minimap);
            m_mm->warpview.set(this, &Interactive_Base::minimap_warp);

            // make sure the viewpos marker is at the right pos to start with
            const Point p = get_viewpoint();

            mainview_move(p.x, p.y);
      }
}


/**
 * Hide the minimap if it is currently shown; otherwise, do nothing.
 */
00380 void Interactive_Base::hide_minimap()
{
      if (m_minimap.window)
            delete m_minimap.window;
}


/*
===============
Interactive_Base::get_display_flags

Return display flags (dfXXX) that modify the view of the map.
===============
*/
uint Interactive_Base::get_display_flags()
{
      return m_display_flags;
}


/*
===============
Interactive_Base::set_display_flags

Change the display flags that modify the view of the map.
===============
*/
void Interactive_Base::set_display_flags(uint flags)
{
      m_display_flags = flags;
}


/*
===============
Interactive_Base::get_display_flag
Interactive_Base::set_display_flag

Get and set one individual flag of the display flags.
===============
*/
bool Interactive_Base::get_display_flag(uint flag)
{
      return m_display_flags & flag;
}

void Interactive_Base::set_display_flag(uint flag, bool on)
{
      m_display_flags &= ~flag;

      if (on)
            m_display_flags |= flag;
}

/*
===============
Interactive_Base::start_build_road

Begin building a road
===============
*/
void Interactive_Base::start_build_road(Coords _start, int player)
{
      // create an empty path
      assert(not m_buildroad);
      m_buildroad = new CoordPath(_start);

   m_road_build_player=player;

   // If we are a game, we obviously build for the Interactive Player
   assert
            (not dynamic_cast<const Game *>(&m_egbase)
             or
             player ==
             static_cast<const Interactive_Player *>(this)->
             get_player_number());

      roadb_add_overlay();
      need_complete_redraw();
}


/*
===============
Interactive_Base::abort_build_road

Stop building the road
===============
*/
void Interactive_Base::abort_build_road()
{
      assert(m_buildroad);

      roadb_remove_overlay();
      need_complete_redraw();

   m_road_build_player=0;

      delete m_buildroad;
      m_buildroad = 0;
}


/*
===============
Interactive_Base::finish_build_road

Finally build the road
===============
*/
void Interactive_Base::finish_build_road()
{
      assert(m_buildroad);

      roadb_remove_overlay();
      need_complete_redraw();

      if (m_buildroad->get_nsteps()) {
            // awkward... path changes ownership
            Path & path = *new Path(*m_buildroad);
            // Build the path as requested
            if (Game * const game = dynamic_cast<Game *>(&egbase()))
                  game->send_player_build_road (m_road_build_player, path);
            else {
                  egbase().get_player(m_road_build_player)->build_road(path);
                  delete &path;
            }
      }

      delete m_buildroad;
      m_buildroad = 0;
}


/*
===============
Interactive_Base::append_build_road

If field is on the path, remove tail of path.
Otherwise append if possible or return false.
===============
*/
bool Interactive_Base::append_build_road(Coords field)
{
      assert(m_buildroad);

      Map & map = egbase().map();
      const Player & player = *egbase().get_player(m_road_build_player);
      int idx = m_buildroad->get_index(field);

      if (idx >= 0) {
            roadb_remove_overlay();
            m_buildroad->truncate(idx);
            roadb_add_overlay();

            need_complete_redraw();
            return true;
      }

      {
            Path path;
            std::set<Coords, Coords::ordering_functor> forbidden_locations;
            const std::vector<Coords> & road_cp = m_buildroad->get_coords();
            const std::vector<Coords>::const_iterator road_cp_end = road_cp.end();
            for
                  (std::vector<Coords>::const_iterator it = road_cp.begin();
                   it != road_cp_end;
                   ++it)
                  forbidden_locations.insert(*it);
            CheckStepRoad cstep(player, MOVECAPS_WALK, &forbidden_locations);
      if
            (map.findpath
             (m_buildroad->get_end(), field, 0, path, cstep, Map::fpBidiCost)
             <
             0)
            return false; // couldn't find a path
            m_buildroad->append(map, path);
      }

      roadb_remove_overlay();
      roadb_add_overlay();
      need_complete_redraw();

      return true;
}

/*
===============
Interactive_Base::get_build_road_start

Return the current road-building startpoint
===============
*/
Coords Interactive_Base::get_build_road_start() const throw () {
      assert(m_buildroad);

      return m_buildroad->get_start();
}

/*
===============
Interactive_Base::get_build_road_end

Return the current road-building endpoint
===============
*/
Coords Interactive_Base::get_build_road_end() const throw () {
      assert(m_buildroad);

      return m_buildroad->get_end();
}

/*
===============
Interactive_Base::get_build_road_end_dir

Return the direction of the last step
===============
*/
Direction Interactive_Base::get_build_road_end_dir() const throw () {
      assert(m_buildroad);

      if (!m_buildroad->get_nsteps())
            return 0;

      return (*m_buildroad)[m_buildroad->get_nsteps() - 1];
}

/*
===============
Interactive_Base::roadb_add_overlay

Add road building data to the road overlay
===============
*/
void Interactive_Base::roadb_add_overlay()
{
      assert(m_buildroad);

      //log("Add overlay\n");

      Map & map = egbase().map();
      Overlay_Manager & overlay_manager = map.overlay_manager();

      // preview of the road
      assert(m_jobid.isNull());
      m_jobid = overlay_manager.get_a_job_id();
      const CoordPath::Step_Vector::size_type nr_steps = m_buildroad->get_nsteps();
      for (CoordPath::Step_Vector::size_type idx = 0; idx < nr_steps; ++idx) {
            Direction dir = (*m_buildroad)[idx];
            Coords c = m_buildroad->get_coords()[idx];

            if (dir < Map_Object::WALK_E || dir > Map_Object::WALK_SW) {
                  map.get_neighbour(c, dir, &c);
                  dir = get_reverse_dir(dir);
            }

            int shift = 2*(dir - Map_Object::WALK_E);

            uchar set_to = overlay_manager.get_road_overlay(c);
      set_to|=  Road_Normal << shift;
            overlay_manager.register_road_overlay(c, set_to, m_jobid);
      }

      // build hints
      FCoords endpos = map.get_fcoords(m_buildroad->get_end());

      assert(m_road_buildhelp_overlay_jobid.isNull());
      m_road_buildhelp_overlay_jobid = overlay_manager.get_a_job_id();
      for (int dir = 1; dir <= 6; dir++) {
            FCoords neighb;
            int caps;

            map.get_neighbour(endpos, dir, &neighb);
            caps = egbase().player(m_road_build_player).get_buildcaps(neighb);

            if (!(caps & MOVECAPS_WALK))
                  continue; // need to be able to walk there

            //  can't build on robusts
            BaseImmovable * const imm = map.get_immovable(neighb);
            if (imm && imm->get_size() >= BaseImmovable::SMALL) {
                  if
                        (not
                         (dynamic_cast<const Flag *>(imm)
                          or
                          (dynamic_cast<const Road *>(imm) and caps & BUILDCAPS_FLAG)))
                        continue;
            }

            if (m_buildroad->get_index(neighb) >= 0)
                  continue; // the road can't cross itself

            int slope = abs(endpos.field->get_height() - neighb.field->get_height());
            int icon;

            if (slope < 2)
                  icon = 1;
            else if (slope < 4)
                  icon = 2;
            else
                  icon = 3;

      std::string name="";
            switch (icon) {
            case 1: name = "pics/roadb_green.png";  break;
            case 2: name = "pics/roadb_yellow.png"; break;
            case 3: name = "pics/roadb_red.png";    break;
            };

      assert(name!="");

            egbase().map().overlay_manager().register_overlay
                  (neighb,
                   g_gr->get_picture(PicMod_Game, name.c_str()),
                   7,
                   Point::invalid(),
                   m_road_buildhelp_overlay_jobid);
      }
}

/*
===============
Interactive_Base::roadb_remove_overlay

Remove road building data from road overlay
===============
*/
void Interactive_Base::roadb_remove_overlay()
{
      assert(m_buildroad);

      //log("Remove overlay\n");

   // preview of the road
      Overlay_Manager & overlay_manager = egbase().map().overlay_manager();
      if (not m_jobid.isNull()) overlay_manager.remove_road_overlay(m_jobid);
      m_jobid = Overlay_Manager::Job_Id::Null();

      // build hints
      if (not m_road_buildhelp_overlay_jobid.isNull())
            overlay_manager.remove_overlay(m_road_buildhelp_overlay_jobid);
      m_road_buildhelp_overlay_jobid = Overlay_Manager::Job_Id::Null();
}

Generated by  Doxygen 1.6.0   Back to index