mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-22 10:03:53 +00:00
2269 lines
56 KiB
C++
2269 lines
56 KiB
C++
/*
|
|
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
|
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
#include "actions.hpp"
|
|
#include "display.hpp"
|
|
#include "font.hpp"
|
|
#include "game_config.hpp"
|
|
#include "hotkeys.hpp"
|
|
#include "language.hpp"
|
|
#include "log.hpp"
|
|
#include "menu.hpp"
|
|
#include "sdl_utils.hpp"
|
|
#include "sound.hpp"
|
|
#include "util.hpp"
|
|
|
|
#include "SDL_image.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
std::map<gamemap::location,double> display::debugHighlights_;
|
|
|
|
namespace {
|
|
const double DefaultZoom = 70.0;
|
|
|
|
display::Pixel alpha_blend_pixels(display::Pixel p1, display::Pixel p2,
|
|
const SDL_PixelFormat* fmt, double alpha)
|
|
{
|
|
const int r1 = ((p1&fmt->Rmask) >> fmt->Rshift) << fmt->Rloss;
|
|
const int g1 = ((p1&fmt->Gmask) >> fmt->Gshift) << fmt->Gloss;
|
|
const int b1 = ((p1&fmt->Bmask) >> fmt->Bshift) << fmt->Bloss;
|
|
|
|
const int r2 = ((p2&fmt->Rmask) >> fmt->Rshift) << fmt->Rloss;
|
|
const int g2 = ((p2&fmt->Gmask) >> fmt->Gshift) << fmt->Gloss;
|
|
const int b2 = ((p2&fmt->Bmask) >> fmt->Bshift) << fmt->Bloss;
|
|
|
|
int r = int(r1*alpha);
|
|
int g = int(g1*alpha);
|
|
int b = int(b1*alpha);
|
|
|
|
if(alpha < 1.0) {
|
|
r += int(r2*(1.0-alpha));
|
|
g += int(g2*(1.0-alpha));
|
|
b += int(b2*(1.0-alpha));
|
|
} else {
|
|
if(r > r2)
|
|
r = r2;
|
|
|
|
if(g > g2)
|
|
g = g2;
|
|
|
|
if(b > b2)
|
|
b = b2;
|
|
}
|
|
|
|
return ((r >> fmt->Rloss) << fmt->Rshift) |
|
|
((g >> fmt->Gloss) << fmt->Gshift) |
|
|
((b >> fmt->Bloss) << fmt->Bshift);
|
|
}
|
|
}
|
|
|
|
display::display(unit_map& units, CVideo& video, const gamemap& map,
|
|
const gamestatus& status, const std::vector<team>& t)
|
|
: screen_(video), xpos_(0.0), ypos_(0.0),
|
|
zoom_(DefaultZoom), map_(map), units_(units),
|
|
energy_bar_count_(-1,-1), minimap_(NULL),
|
|
minimapDecorationsDrawn_(false),
|
|
pathsList_(NULL), status_(status),
|
|
teams_(t), lastDraw_(0), drawSkips_(0),
|
|
invalidateAll_(true), invalidateUnit_(true),
|
|
invalidateGameStatus_(true), sideBarBgDrawn_(false),
|
|
lastTimeOfDay_(-1), currentTeam_(0), updatesLocked_(0),
|
|
turbo_(false), grid_(false)
|
|
{
|
|
gameStatusRect_.w = 0;
|
|
unitDescriptionRect_.w = 0;
|
|
unitProfileRect_.w = 0;
|
|
|
|
|
|
//clear the screen contents
|
|
SDL_Surface* const disp = screen_.getSurface();
|
|
const int length = disp->w*disp->h;
|
|
|
|
short* const pixels = reinterpret_cast<short*>(disp->pixels);
|
|
std::fill(pixels,pixels+length,0);
|
|
update_whole_screen();
|
|
}
|
|
|
|
void clear_surfaces(std::map<std::string,SDL_Surface*>& surfaces)
|
|
{
|
|
for(std::map<std::string,SDL_Surface*>::iterator i = surfaces.begin();
|
|
i != surfaces.end(); ++i) {
|
|
SDL_FreeSurface(i->second);
|
|
}
|
|
|
|
surfaces.clear();
|
|
}
|
|
|
|
display::~display()
|
|
{
|
|
clear_surfaces(images_);
|
|
clear_surfaces(scaledImages_);
|
|
clear_surfaces(greyedImages_);
|
|
clear_surfaces(brightenedImages_);
|
|
SDL_FreeSurface(minimap_);
|
|
}
|
|
|
|
int display::x() const { return screen_.getx(); }
|
|
int display::mapx() const { return x() - 140; }
|
|
int display::y() const { return screen_.gety(); }
|
|
|
|
void display::select_hex(gamemap::location hex)
|
|
{
|
|
invalidate(selectedHex_);
|
|
selectedHex_ = hex;
|
|
invalidate(selectedHex_);
|
|
invalidate_unit();
|
|
}
|
|
|
|
void display::highlight_hex(gamemap::location hex)
|
|
{
|
|
const int has_unit = units_.count(mouseoverHex_) + units_.count(hex);
|
|
|
|
invalidate(mouseoverHex_);
|
|
mouseoverHex_ = hex;
|
|
invalidate(mouseoverHex_);
|
|
invalidate_game_status();
|
|
|
|
if(has_unit)
|
|
invalidate_unit();
|
|
}
|
|
|
|
gamemap::location display::hex_clicked_on(int xclick, int yclick)
|
|
{
|
|
if(xclick > mapx())
|
|
return gamemap::location();
|
|
|
|
const double xtile = xpos_/(zoom_*0.75) +
|
|
static_cast<double>(xclick)/(zoom_*0.75) - 0.25;
|
|
const double ytile = ypos_/zoom_ + static_cast<double>(yclick)/zoom_
|
|
+ (((static_cast<int>(xtile)%2) == 1) ? -0.5:0.0);
|
|
|
|
return gamemap::location(static_cast<int>(xtile),static_cast<int>(ytile));
|
|
}
|
|
|
|
gamemap::location display::minimap_location_on(int x, int y)
|
|
{
|
|
const SDL_Rect rect =
|
|
get_minimap_location(mapx()+30,35,(this->x()-mapx())-60,120);
|
|
|
|
if(x < rect.x || y < rect.y ||
|
|
x >= rect.x + rect.w || y >= rect.y + rect.h) {
|
|
return gamemap::location();
|
|
}
|
|
|
|
const int xdiv = rect.w / map_.x();
|
|
const int ydiv = rect.h / map_.y();
|
|
|
|
return gamemap::location((x - rect.x)/xdiv,(y-rect.y)/ydiv);
|
|
}
|
|
|
|
display::Pixel display::rgb(int r, int g, int b) const
|
|
{
|
|
return SDL_MapRGB(const_cast<display*>(this)->video().getSurface()->format,
|
|
r,g,b);
|
|
}
|
|
|
|
void display::scroll(double xmove, double ymove)
|
|
{
|
|
const double orig_x = xpos_;
|
|
const double orig_y = ypos_;
|
|
xpos_ += xmove;
|
|
ypos_ += ymove;
|
|
bounds_check_position();
|
|
|
|
//only invalidate if we've actually moved
|
|
if(orig_x != xpos_ || orig_y != ypos_)
|
|
invalidate_all();
|
|
}
|
|
|
|
void display::zoom(double amount)
|
|
{
|
|
clear_surfaces(scaledImages_);
|
|
clear_surfaces(greyedImages_);
|
|
clear_surfaces(brightenedImages_);
|
|
energy_bar_count_ = std::pair<int,int>(-1,-1);
|
|
|
|
xpos_ /= zoom_;
|
|
ypos_ /= zoom_;
|
|
|
|
const double max_zoom = 200.0;
|
|
zoom_ += amount;
|
|
if(zoom_ > max_zoom)
|
|
zoom_ = max_zoom;
|
|
|
|
xpos_ *= zoom_;
|
|
ypos_ *= zoom_;
|
|
|
|
xpos_ += amount*2;
|
|
ypos_ += amount*2;
|
|
|
|
bounds_check_position();
|
|
|
|
invalidate_all();
|
|
}
|
|
|
|
void display::default_zoom()
|
|
{
|
|
zoom(DefaultZoom - zoom_);
|
|
}
|
|
|
|
void display::scroll_to_tile(int x, int y, SCROLL_TYPE scroll_type)
|
|
{
|
|
if(update_locked())
|
|
return;
|
|
|
|
const double xpos = static_cast<double>(x)*zoom_*0.75 - xpos_;
|
|
const double ypos = static_cast<double>(y)*zoom_ - ypos_ +
|
|
((x % 2) == 1 ? zoom_/2.0 : 0.0);
|
|
|
|
const double speed = 50.0;
|
|
|
|
const double desiredxpos = this->mapx()/2.0 - zoom_/2.0;
|
|
const double desiredypos = this-> y()/2.0 - zoom_/2.0;
|
|
|
|
const double xmove = xpos - desiredxpos;
|
|
const double ymove = ypos - desiredypos;
|
|
|
|
int num_moves = static_cast<int>((fabs(xmove) > fabs(ymove) ?
|
|
fabs(xmove):fabs(ymove))/speed);
|
|
|
|
if(scroll_type == WARP || turbo())
|
|
num_moves = 1;
|
|
|
|
const double divisor = static_cast<double>(num_moves);
|
|
|
|
double xdiff = 0.0, ydiff = 0.0;
|
|
|
|
for(int i = 0; i != num_moves; ++i) {
|
|
check_keys(*this);
|
|
xdiff += xmove/divisor;
|
|
ydiff += ymove/divisor;
|
|
|
|
//accelerate scroll rate if either shift key is held down
|
|
if((i%4) != 0 && i != num_moves-1 && turbo()) {
|
|
continue;
|
|
}
|
|
|
|
scroll(xdiff,ydiff);
|
|
draw();
|
|
|
|
xdiff = 0.0;
|
|
ydiff = 0.0;
|
|
}
|
|
|
|
invalidate_all();
|
|
draw();
|
|
}
|
|
|
|
void display::scroll_to_tiles(int x1, int y1, int x2, int y2,
|
|
SCROLL_TYPE scroll_type)
|
|
{
|
|
const double xpos1 = static_cast<double>(x1)*zoom_*0.75 - xpos_;
|
|
const double ypos1 = static_cast<double>(y1)*zoom_ - ypos_ +
|
|
((x1 % 2) == 1 ? zoom_/2.0 : 0.0);
|
|
const double xpos2 = static_cast<double>(x2)*zoom_*0.75 - xpos_;
|
|
const double ypos2 = static_cast<double>(y2)*zoom_ - ypos_ +
|
|
((x2 % 2) == 1 ? zoom_/2.0 : 0.0);
|
|
|
|
const double diffx = fabs(xpos1 - xpos2);
|
|
const double diffy = fabs(ypos1 - ypos2);
|
|
|
|
if(diffx > mapx()/(zoom_*0.75) || diffy > y()/zoom_) {
|
|
scroll_to_tile(x1,y1,scroll_type);
|
|
} else {
|
|
scroll_to_tile((x1+x2)/2,(y1+y2)/2,scroll_type);
|
|
}
|
|
}
|
|
|
|
void display::bounds_check_position()
|
|
{
|
|
const double min_zoom1 = static_cast<double>(mapx()/(map_.x()*0.75 + 0.25));
|
|
const double min_zoom2 = static_cast<double>(y()/map_.y());
|
|
const double min_zoom = min_zoom1 > min_zoom2 ? min_zoom1 : min_zoom2;
|
|
const double max_zoom = 200.0;
|
|
|
|
zoom_ = floor(zoom_);
|
|
|
|
if(zoom_ < min_zoom)
|
|
zoom_ = min_zoom;
|
|
|
|
if(zoom_ > max_zoom)
|
|
zoom_ = max_zoom;
|
|
|
|
const double xend = zoom_*map_.x()*0.75 + zoom_*0.25;
|
|
const double yend = zoom_*map_.y() + zoom_/2.0;
|
|
|
|
if(xpos_ + static_cast<double>(mapx()) > xend)
|
|
xpos_ -= xpos_ + static_cast<double>(mapx()) - xend;
|
|
|
|
if(ypos_ + static_cast<double>(y()) > yend)
|
|
ypos_ -= ypos_ + static_cast<double>(y()) - yend;
|
|
|
|
if(xpos_ < 0.0)
|
|
xpos_ = 0.0;
|
|
|
|
if(ypos_ < 0.0)
|
|
ypos_ = 0.0;
|
|
}
|
|
|
|
void display::redraw_everything()
|
|
{
|
|
lastTimeOfDay_ = gamestatus::NUM_TIMES;
|
|
sideBarBgDrawn_ = false;
|
|
minimapDecorationsDrawn_ = false;
|
|
invalidate_all();
|
|
draw(true,true);
|
|
}
|
|
|
|
void display::draw(bool update,bool force)
|
|
{
|
|
if(!sideBarBgDrawn_) {
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
SDL_Surface* const image = getImage("misc/rightside.png",UNSCALED);
|
|
if(image != NULL) {
|
|
SDL_Rect dstrect;
|
|
dstrect.x = mapx();
|
|
dstrect.y = 0;
|
|
dstrect.w = image->w;
|
|
dstrect.h = image->h;
|
|
|
|
if(dstrect.x + dstrect.w <= this->x() &&
|
|
dstrect.y + dstrect.h <= this->y()) {
|
|
SDL_BlitSurface(image,NULL,screen,&dstrect);
|
|
update_rect(dstrect);
|
|
} else {
|
|
std::cout << (dstrect.x+dstrect.w) << " > " << this->x() << " or " << (dstrect.y + dstrect.h) << " > " << this->y() << "\n";
|
|
}
|
|
} else {
|
|
std::cerr << "could not load 'misc/rightside.png'\n";
|
|
}
|
|
|
|
sideBarBgDrawn_ = true;
|
|
}
|
|
|
|
if(invalidateAll_) {
|
|
for(int x = -1; x <= map_.x(); ++x)
|
|
for(int y = -1; y <= map_.y(); ++y)
|
|
draw_tile(x,y);
|
|
invalidateAll_ = false;
|
|
draw_minimap(mapx()+30,35,(this->x()-mapx())-60,120);
|
|
} else {
|
|
for(std::set<gamemap::location>::const_iterator it =
|
|
invalidated_.begin(); it != invalidated_.end(); ++it) {
|
|
draw_tile(it->x,it->y);
|
|
}
|
|
|
|
invalidated_.clear();
|
|
}
|
|
|
|
draw_sidebar();
|
|
|
|
const int max_skips = 5;
|
|
const int time_between_draws = 20;
|
|
const int current_time = SDL_GetTicks();
|
|
const int wait_time = lastDraw_ + time_between_draws - current_time;
|
|
if(wait_time > 0) {
|
|
SDL_Delay(wait_time);
|
|
}
|
|
|
|
if(update) {
|
|
lastDraw_ = SDL_GetTicks();
|
|
|
|
if(wait_time >= 0 || drawSkips_ >= max_skips || force)
|
|
update_display();
|
|
else
|
|
drawSkips_++;
|
|
|
|
}
|
|
}
|
|
|
|
void display::update_display()
|
|
{
|
|
if(updatesLocked_ > 0)
|
|
return;
|
|
|
|
for(std::vector<SDL_Rect>::const_iterator i = updateRects_.begin();
|
|
i != updateRects_.end(); ++i) {
|
|
screen_.update(i->x,i->y,i->w,i->h);
|
|
}
|
|
|
|
updateRects_.clear();
|
|
}
|
|
|
|
void display::draw_sidebar()
|
|
{
|
|
|
|
if(invalidateUnit_) {
|
|
//we display the unit the mouse is over if it is over a unit
|
|
//otherwise we display the unit that is selected
|
|
std::map<gamemap::location,unit>::const_iterator i
|
|
= units_.find(mouseoverHex_);
|
|
if(i == units_.end())
|
|
i = units_.find(selectedHex_);
|
|
|
|
if(i != units_.end())
|
|
draw_unit_details(mapx()+2,390,selectedHex_,i->second,
|
|
unitDescriptionRect_,unitProfileRect_);
|
|
invalidateUnit_ = false;
|
|
}
|
|
|
|
if(invalidateGameStatus_) {
|
|
draw_game_status(mapx()+2,258);
|
|
invalidateGameStatus_ = false;
|
|
}
|
|
}
|
|
|
|
void display::draw_game_status(int x, int y)
|
|
{
|
|
if(teams_.empty())
|
|
return;
|
|
|
|
SDL_Rect rect;
|
|
rect.x = x;
|
|
rect.y = y;
|
|
rect.w = this->x() - x;
|
|
|
|
const int bgcolour = 0;
|
|
const int colour = 0xffffff;
|
|
|
|
gamestatus::TIME timeofday = status_.timeofday();
|
|
if(mouseoverHex_.valid()) {
|
|
timeofday = timeofday_at(status_,units_,mouseoverHex_);
|
|
}
|
|
|
|
if(lastTimeOfDay_ != (int)timeofday) {
|
|
lastTimeOfDay_ = (int)timeofday;
|
|
|
|
SDL_Surface* const tod_surface = getImage(
|
|
gamestatus::timeofdayDescription(timeofday) + ".png",
|
|
UNSCALED);
|
|
|
|
if(tod_surface != NULL) {
|
|
//hardcoded values as to where the time of day image appears
|
|
blit_surface(mapx() + 21,196,tod_surface);
|
|
SDL_Rect update_area;
|
|
update_area.x = mapx() + 21;
|
|
update_area.y = 196;
|
|
update_area.w = tod_surface->w;
|
|
update_area.h = tod_surface->h;
|
|
update_rect(update_area);
|
|
}
|
|
}
|
|
|
|
if(gameStatusRect_.w > 0) {
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
SDL_Surface* const background=getImage("misc/rightside.png",UNSCALED);
|
|
|
|
if(background == NULL)
|
|
return;
|
|
|
|
SDL_Rect srcrect = gameStatusRect_;
|
|
srcrect.x -= mapx();
|
|
SDL_BlitSurface(background,&srcrect,screen,&gameStatusRect_);
|
|
update_rect(gameStatusRect_);
|
|
}
|
|
|
|
int nunits = 0;
|
|
for(std::map<gamemap::location,unit>::const_iterator uit = units_.begin();
|
|
uit != units_.end(); ++uit) {
|
|
if(uit->second.side() == currentTeam_+1) {
|
|
++nunits;
|
|
}
|
|
}
|
|
|
|
const int income = teams_[currentTeam_].income() - nunits;
|
|
|
|
std::stringstream details;
|
|
details << string_table["turn"] << ": " << status_.turn() << "/"
|
|
<< status_.number_of_turns() << "\n" << string_table["gold"] << ": "
|
|
<< teams_[currentTeam_].gold() << "\n"
|
|
<< string_table["villages"] << ": "
|
|
<< teams_[currentTeam_].towers() << "\n"
|
|
<< string_table["units"] << ": " << nunits << "\n"
|
|
<< string_table["income"] << ": " << income << "\n";
|
|
|
|
if(map_.on_board(mouseoverHex_)) {
|
|
const gamemap::TERRAIN terrain = map_[mouseoverHex_.x][mouseoverHex_.y];
|
|
std::string name = map_.terrain_name(terrain);
|
|
|
|
if(terrain == gamemap::CASTLE &&
|
|
map_.is_starting_position(mouseoverHex_)) {
|
|
name = "keep";
|
|
}
|
|
|
|
const std::string& lang_name = string_table[name];
|
|
|
|
if(!lang_name.empty())
|
|
details << lang_name << "\n";
|
|
else
|
|
details << name << "\n";
|
|
|
|
const int xhex = mouseoverHex_.x + 1;
|
|
const int yhex = mouseoverHex_.y + 1;
|
|
|
|
details << xhex << ", " << yhex << "\n";
|
|
} else {
|
|
details << "\n";
|
|
}
|
|
|
|
SDL_Rect clipRect;
|
|
clipRect.x = mapx();
|
|
clipRect.y = 0;
|
|
clipRect.w = this->x() - mapx();
|
|
clipRect.h = this->y();
|
|
|
|
gameStatusRect_ = font::draw_text(this,clipRect,14,font::NORMAL_COLOUR,
|
|
details.str(),x,y);
|
|
|
|
update_rect(gameStatusRect_);
|
|
}
|
|
|
|
void display::draw_unit_details(int x, int y, const gamemap::location& loc,
|
|
const unit& u, SDL_Rect& description_rect, SDL_Rect& profile_rect)
|
|
{
|
|
SDL_Rect clipRect;
|
|
clipRect.x = this->mapx();
|
|
clipRect.y = 0;
|
|
clipRect.w = this->x();
|
|
clipRect.h = this->y();
|
|
SDL_Surface* const background = getImage("misc/rightside.png",UNSCALED);
|
|
|
|
if(background == NULL)
|
|
return;
|
|
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
|
|
if(description_rect.w > 0 && description_rect.x >= mapx()) {
|
|
SDL_Rect srcrect = description_rect;
|
|
srcrect.x -= mapx();
|
|
SDL_BlitSurface(background,&srcrect,screen,&description_rect);
|
|
update_rect(description_rect);
|
|
}
|
|
|
|
if(profile_rect.w > 0 && profile_rect.x >= mapx() && background != NULL) {
|
|
SDL_Rect srcrect = profile_rect;
|
|
srcrect.x -= this->mapx();
|
|
SDL_BlitSurface(background,&srcrect,screen,&profile_rect);
|
|
update_rect(profile_rect);
|
|
}
|
|
|
|
std::string status = string_table["healthy"];
|
|
if(map_.on_board(loc) &&
|
|
u.invisible(map_.underlying_terrain(map_[loc.x][loc.y]))) {
|
|
status = "@" + string_table["invisible"];
|
|
}
|
|
|
|
if(u.has_flag("slowed")) {
|
|
status = "#" + string_table["slowed"];
|
|
}
|
|
|
|
if(u.has_flag("poisoned")) {
|
|
status = "#" + string_table["poisoned"];
|
|
}
|
|
|
|
const std::string& lang_ability =
|
|
string_table["ability_" + u.type().ability()];
|
|
|
|
std::stringstream details;
|
|
details << "+" << u.description() << "\n"
|
|
<< "+" << u.type().language_name()
|
|
<< "\n-(" << string_table["level"] << ""
|
|
<< u.type().level() << ")\n"
|
|
<< status << "\n"
|
|
<< unit_type::alignment_description(u.type().alignment())
|
|
<< "\n"
|
|
<< u.traits_description() << "\n"
|
|
<< (lang_ability.empty() ? u.type().ability() : lang_ability)<<"\n"
|
|
<< string_table["hp"] << ": " << u.hitpoints()
|
|
<< "/" << u.max_hitpoints() << "\n"
|
|
<< string_table["xp"] << ": " << u.experience() << "/"
|
|
<< u.max_experience() << "\n"
|
|
<< string_table["moves"] << ": " << u.movement_left() << "/"
|
|
<< u.total_movement()
|
|
<< "\n";
|
|
|
|
const std::vector<attack_type>& attacks = u.attacks();
|
|
for(std::vector<attack_type>::const_iterator at_it = attacks.begin();
|
|
at_it != attacks.end(); ++at_it) {
|
|
|
|
const std::string& lang_weapon
|
|
= string_table["weapon_name_" + at_it->name()];
|
|
const std::string& lang_type
|
|
= string_table["weapon_type_" + at_it->type()];
|
|
const std::string& lang_special
|
|
= string_table["weapon_special_" + at_it->special()];
|
|
details << "\n"
|
|
<< (lang_weapon.empty() ? at_it->name():lang_weapon) << " ("
|
|
<< (lang_type.empty() ? at_it->type():lang_type) << ")\n"
|
|
<< (lang_special.empty() ? at_it->special():lang_special)<<"\n"
|
|
<< at_it->damage() << "-" << at_it->num_attacks() << " -- "
|
|
<< (at_it->range() == attack_type::SHORT_RANGE ?
|
|
string_table["short_range"] :
|
|
string_table["long_range"]) << "\n\n";
|
|
}
|
|
|
|
description_rect =
|
|
font::draw_text(this,clipRect,14,font::NORMAL_COLOUR,
|
|
details.str(),x,y);
|
|
|
|
update_rect(description_rect);
|
|
|
|
y += description_rect.h;
|
|
|
|
SDL_Surface* const profile = getImage(u.type().image_profile(),UNSCALED);
|
|
|
|
if(profile == NULL)
|
|
return;
|
|
|
|
blit_surface(x,y,profile);
|
|
|
|
profile_rect.x = x;
|
|
profile_rect.y = y;
|
|
profile_rect.w = profile->w;
|
|
profile_rect.h = profile->h;
|
|
|
|
update_rect(profile_rect);
|
|
}
|
|
|
|
SDL_Rect display::get_minimap_location(int x, int y, int w, int h)
|
|
{
|
|
SDL_Rect res = {0,0,0,0};
|
|
SDL_Surface* const surface = getMinimap(w,h);
|
|
if(surface == NULL)
|
|
return res;
|
|
|
|
if(w > surface->w) {
|
|
x += (w - surface->w)/2;
|
|
w = surface->w;
|
|
}
|
|
|
|
if(h > surface->h) {
|
|
y += (h - surface->h)/2;
|
|
h = surface->h;
|
|
}
|
|
|
|
if(w < surface->w || h < surface->h)
|
|
return res;
|
|
|
|
res.x = x;
|
|
res.y = y;
|
|
res.w = w;
|
|
res.h = h;
|
|
return res;
|
|
}
|
|
|
|
void display::draw_minimap(int x, int y, int w, int h)
|
|
{
|
|
SDL_Surface* const surface = getMinimap(w,h);
|
|
if(surface == NULL)
|
|
return;
|
|
|
|
SDL_Rect minimap_location = get_minimap_location(x,y,w,h);
|
|
x = minimap_location.x;
|
|
y = minimap_location.y;
|
|
w = minimap_location.w;
|
|
h = minimap_location.h;
|
|
|
|
//we only have to draw the map surroundings once per level
|
|
if(!minimapDecorationsDrawn_) {
|
|
minimapDecorationsDrawn_ = true;
|
|
|
|
//draw red borders around the map
|
|
const short border_colour = short(0xF000);
|
|
gui::draw_rectangle(x-1,y-1,w+2,h+2,border_colour,screen_.getSurface());
|
|
gui::draw_rectangle(x-3,y-3,w+6,h+6,border_colour,screen_.getSurface());
|
|
SDL_Surface* const north = getImage("misc/compass-north.png",UNSCALED);
|
|
SDL_Surface* const south = getImage("misc/compass-south.png",UNSCALED);
|
|
|
|
if(north != NULL && north->h < y) {
|
|
const int xloc = x + w/2 - north->w/2;
|
|
const int yloc = y - north->h;
|
|
|
|
blit_surface(xloc, yloc, north);
|
|
|
|
if(south != NULL) {
|
|
blit_surface(xloc, y+h, south);
|
|
}
|
|
|
|
//try to draw the little widgets on each corner of the map
|
|
SDL_Surface* const tl=getImage("misc/topleft-corner.png",UNSCALED);
|
|
SDL_Surface* const bl=getImage("misc/botleft-corner.png",UNSCALED);
|
|
SDL_Surface* const tr=getImage("misc/topright-corner.png",UNSCALED);
|
|
SDL_Surface* const br=getImage("misc/botright-corner.png",UNSCALED);
|
|
|
|
if(tl != NULL && bl != NULL && tr != NULL && br != NULL) {
|
|
const int adjust = 5;
|
|
int xloc = x - tl->w + adjust;
|
|
int yloc = y - tl->h + adjust;
|
|
|
|
blit_surface(xloc,yloc,tl);
|
|
|
|
yloc = y + h - adjust;
|
|
blit_surface(xloc,yloc,bl);
|
|
|
|
xloc = x + w - adjust;
|
|
blit_surface(xloc,yloc,br);
|
|
|
|
yloc = y - tl->h + adjust;
|
|
blit_surface(xloc,yloc,tr);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_BlitSurface(surface,NULL,screen_.getSurface(),&minimap_location);
|
|
|
|
const double scaling = static_cast<double>(surface->w/map_.x());
|
|
|
|
const int xbox = static_cast<int>(scaling*xpos_/(zoom_*0.75));
|
|
const int ybox = static_cast<int>(scaling*ypos_/zoom_);
|
|
|
|
const int wbox = static_cast<int>(scaling*mapx()/(zoom_*0.75) - scaling);
|
|
const int hbox = static_cast<int>(scaling*this->y()/zoom_ - scaling);
|
|
|
|
const Pixel boxcolour = Pixel(SDL_MapRGB(surface->format,0xFF,0xFF,0xFF));
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
short* const data = reinterpret_cast<short*>(screen->pixels);
|
|
short* const start_top = data + (y+ybox)*screen->w + (x+xbox);
|
|
short* const end_top = start_top + wbox;
|
|
|
|
std::fill(start_top,end_top,boxcolour);
|
|
|
|
short* const start_bot = start_top + hbox*screen->w;
|
|
short* const end_bot = start_bot + wbox;
|
|
|
|
std::fill(start_bot,end_bot+1,boxcolour);
|
|
|
|
short* side;
|
|
for(side = start_top; side != start_bot; side += screen->w) {
|
|
*side = boxcolour;
|
|
side[wbox] = boxcolour;
|
|
}
|
|
|
|
update_rect(minimap_location);
|
|
}
|
|
|
|
void display::draw_terrain_palette(int x, int y, gamemap::TERRAIN selected)
|
|
{
|
|
const int max_h = 35;
|
|
|
|
SDL_Rect invalid_rect;
|
|
invalid_rect.x = x;
|
|
invalid_rect.y = y;
|
|
invalid_rect.w = 0;
|
|
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
|
|
std::vector<gamemap::TERRAIN> terrains = map_.get_terrain_precedence();
|
|
for(std::vector<gamemap::TERRAIN>::const_iterator i = terrains.begin();
|
|
i != terrains.end(); ++i) {
|
|
SDL_Surface* const image = getTerrain(*i,SCALED,-1,-1);
|
|
if(x + image->w >= this->x() || y + image->h >= this->y()) {
|
|
std::cout << "terrain palette can't fit: " << x + image->w << " > " << this->x() << " or " << y+image->h << " > " << this->y() << "\n";
|
|
return;
|
|
}
|
|
|
|
SDL_Rect dstrect;
|
|
dstrect.x = x;
|
|
dstrect.y = y;
|
|
dstrect.w = image->w;
|
|
dstrect.h = image->h;
|
|
|
|
if(dstrect.h > max_h)
|
|
dstrect.h = max_h;
|
|
|
|
SDL_BlitSurface(image,NULL,screen,&dstrect);
|
|
gui::draw_rectangle(x,y,image->w-1,max_h-1,
|
|
*i == selected?0xF000:0,screen);
|
|
|
|
y += max_h+2;
|
|
|
|
if(image->w > invalid_rect.w)
|
|
invalid_rect.w = image->w;
|
|
}
|
|
|
|
invalid_rect.h = y - invalid_rect.y;
|
|
update_rect(invalid_rect);
|
|
}
|
|
|
|
gamemap::TERRAIN display::get_terrain_on(int palx, int paly, int x, int y)
|
|
{
|
|
const int height = 37;
|
|
if(y < paly || x < palx)
|
|
return 0;
|
|
|
|
const std::vector<gamemap::TERRAIN>& terrains=map_.get_terrain_precedence();
|
|
if(y > paly+terrains.size()*height)
|
|
return 0;
|
|
|
|
const int index = (y - paly)/height;
|
|
if(index < 0 || index >= terrains.size())
|
|
return 0;
|
|
|
|
return terrains[index];
|
|
}
|
|
|
|
void display::draw_tile(int x, int y, SDL_Surface* unit_image,
|
|
double highlight_ratio, Pixel blend_with)
|
|
|
|
{
|
|
if(updatesLocked_)
|
|
return;
|
|
|
|
const gamemap::location loc(x,y);
|
|
int xpos = int(get_location_x(loc));
|
|
int ypos = int(get_location_y(loc));
|
|
|
|
if(xpos > mapx() || ypos > this->y())
|
|
return;
|
|
|
|
int xend = xpos + static_cast<int>(zoom_);
|
|
int yend = int(get_location_y(gamemap::location(x,y+1)));
|
|
|
|
if(xend < 0 || yend < 0)
|
|
return;
|
|
|
|
const gamemap::location ne_loc(loc.get_direction(
|
|
gamemap::location::NORTH_EAST));
|
|
const gamemap::location se_loc(loc.get_direction(
|
|
gamemap::location::SOUTH_EAST));
|
|
|
|
//assert(tiles_adjacent(loc,ne_loc));
|
|
//assert(tiles_adjacent(loc,se_loc));
|
|
|
|
const int ne_xpos = (int)get_location_x(ne_loc);
|
|
const int ne_ypos = (int)get_location_y(ne_loc);
|
|
const int se_xpos = (int)get_location_x(se_loc);
|
|
const int se_ypos = (int)get_location_y(se_loc);
|
|
|
|
//mark the rectangle for updating
|
|
{
|
|
SDL_Rect update_rect;
|
|
if(xpos >= 0)
|
|
update_rect.x = xpos;
|
|
else
|
|
update_rect.x = 0;
|
|
|
|
if(ypos >= 0)
|
|
update_rect.y = ypos;
|
|
else
|
|
update_rect.y = 0;
|
|
|
|
if(xend > mapx())
|
|
update_rect.w = mapx() - update_rect.x;
|
|
else
|
|
update_rect.w = xend - update_rect.x;
|
|
|
|
if(yend > this->y())
|
|
update_rect.h = this->y() - update_rect.y;
|
|
else
|
|
update_rect.h = yend - update_rect.y;
|
|
|
|
this->update_rect(update_rect);
|
|
}
|
|
|
|
gamemap::TERRAIN terrain = gamemap::VOID_TERRAIN;
|
|
|
|
if(x >= 0 && y >= 0 && x < map_.x() && y < map_.y())
|
|
terrain = map_[x][y];
|
|
|
|
IMAGE_TYPE image_type = SCALED;
|
|
|
|
//find if this tile should be greyed
|
|
if(pathsList_ != NULL && pathsList_->routes.find(gamemap::location(x,y)) ==
|
|
pathsList_->routes.end()) {
|
|
image_type = GREYED;
|
|
} else if(gamemap::location(x,y) == mouseoverHex_ ||
|
|
gamemap::location(x,y) == selectedHex_ &&
|
|
units_.count(gamemap::location(x,y)) == 1) {
|
|
image_type = BRIGHTENED;
|
|
}
|
|
|
|
int r = 0, g = 0, b = 0;
|
|
|
|
SDL_Surface* surface = getTerrain(terrain,image_type,x,y);
|
|
|
|
if(surface == NULL) {
|
|
std::cerr << "Could not get terrain surface\n";
|
|
return;
|
|
}
|
|
|
|
std::vector<SDL_Surface*> overlaps = getAdjacentTerrain(x,y,image_type);
|
|
typedef std::multimap<gamemap::location,std::string>::const_iterator Itor;
|
|
for(std::pair<Itor,Itor> overlays =
|
|
overlays_.equal_range(gamemap::location(x,y));
|
|
overlays.first != overlays.second; ++overlays.first) {
|
|
SDL_Surface* const overlay_surface = getImage(overlays.first->second);
|
|
if(overlay_surface != NULL) {
|
|
overlaps.push_back(overlay_surface);
|
|
}
|
|
}
|
|
|
|
int ysrc = 0, xsrc = 0;
|
|
if(ypos < 0) {
|
|
ysrc -= ypos;
|
|
ypos = 0;
|
|
}
|
|
|
|
if(xpos < 0) {
|
|
xsrc -= xpos;
|
|
xpos = 0;
|
|
}
|
|
|
|
if(xend >= mapx())
|
|
xend = mapx()-1;
|
|
|
|
if(yend >= this->y())
|
|
yend = this->y()-1;
|
|
|
|
if(xend < xpos || yend < ypos)
|
|
return;
|
|
|
|
SDL_Surface* energy_image = NULL;
|
|
|
|
double unit_energy = 0.0;
|
|
|
|
const short energy_loss_colour = 0;
|
|
short energy_colour = 0;
|
|
|
|
const int max_energy = 80;
|
|
double energy_size = 1.0;
|
|
bool face_left = true;
|
|
|
|
//see if there is a unit on this tile
|
|
const unit_map::const_iterator it = units_.find(gamemap::location(x,y));
|
|
if(it != units_.end()) {
|
|
if(unit_image == NULL)
|
|
unit_image = getImage(it->second.image());
|
|
const int unit_move = it->second.movement_left();
|
|
const int unit_total_move = it->second.total_movement();
|
|
|
|
const char* energy_file = NULL;
|
|
|
|
if(it->second.side() != currentTeam_+1) {
|
|
if(teams_[currentTeam_].is_enemy(it->second.side())) {
|
|
energy_file = "enemy-energy.png";
|
|
} else {
|
|
energy_file = "ally-energy.png";
|
|
}
|
|
} else {
|
|
if(unit_move == unit_total_move) {
|
|
energy_file = "unmoved-energy.png";
|
|
} else if(unit_move > 0) {
|
|
energy_file = "partmoved-energy.png";
|
|
} else {
|
|
energy_file = "moved-energy.png";
|
|
}
|
|
}
|
|
|
|
energy_image = getImage(energy_file);
|
|
unit_energy = double(it->second.hitpoints()) /
|
|
double(it->second.max_hitpoints());
|
|
|
|
if(highlight_ratio == 1.0)
|
|
highlight_ratio = it->second.alpha();
|
|
|
|
if(it->second.invisible(map_.underlying_terrain(map_[x][y])) &&
|
|
highlight_ratio > 0.5) {
|
|
highlight_ratio = 0.5;
|
|
}
|
|
|
|
if(loc == selectedHex_ && highlight_ratio == 1.0) {
|
|
highlight_ratio = 1.5;
|
|
blend_with = short(0xFFFF);
|
|
}
|
|
|
|
{
|
|
int er = 0;
|
|
int eg = 0;
|
|
int eb = 0;
|
|
if(unit_energy < 0.33) {
|
|
er = 200;
|
|
} else if(unit_energy < 0.66) {
|
|
er = 200;
|
|
eg = 200;
|
|
} else {
|
|
eg = 200;
|
|
}
|
|
|
|
energy_colour = ::SDL_MapRGB(energy_image->format,er,eg,eb);
|
|
}
|
|
|
|
if(it->second.max_hitpoints() < max_energy) {
|
|
energy_size = double(it->second.max_hitpoints())/double(max_energy);
|
|
}
|
|
|
|
face_left = it->second.facing_left();
|
|
}
|
|
|
|
const std::pair<int,int>& energy_bar_loc = calculate_energy_bar();
|
|
double total_energy = double(energy_bar_loc.second - energy_bar_loc.first);
|
|
|
|
const int skip_energy_rows = int(total_energy*(1.0-energy_size));
|
|
total_energy -= double(skip_energy_rows);
|
|
|
|
const int show_energy_after = energy_bar_loc.first +
|
|
int((1.0-unit_energy)*total_energy);
|
|
|
|
const bool hide_unit = hiddenUnit_ == gamemap::location(x,y);
|
|
const bool draw_hit = hitUnit_ == gamemap::location(x,y);
|
|
const short hit_colour = short(0xF000);
|
|
|
|
if(deadUnit_ == gamemap::location(x,y)) {
|
|
highlight_ratio = deadAmount_;
|
|
}
|
|
|
|
const int xpad = (surface->w%2) == 1 ? 1:0;
|
|
|
|
SDL_Surface* const dst = screen_.getSurface();
|
|
|
|
const Pixel grid_colour = SDL_MapRGB(dst->format,0,0,0);
|
|
|
|
int j;
|
|
for(j = ypos; j != yend; ++j) {
|
|
const int yloc = ysrc+j-ypos;
|
|
const int xoffset = abs(yloc - static_cast<int>(zoom_/2.0))/2;
|
|
|
|
const int ne_yloc = ysrc+j-ne_ypos;
|
|
const int ne_xoffset = abs(ne_yloc - static_cast<int>(zoom_/2.0))/2;
|
|
|
|
const int se_yloc = ysrc+j-se_ypos;
|
|
const int se_xoffset = abs(se_yloc - static_cast<int>(zoom_/2.0))/2;
|
|
|
|
int xdst = xpos;
|
|
if(xoffset > xsrc) {
|
|
xdst += xoffset - xsrc;
|
|
if(xend < xdst)
|
|
continue;
|
|
}
|
|
|
|
const int maxlen = static_cast<int>(zoom_) - xoffset*2;
|
|
int len = ((xend - xdst) > maxlen) ? maxlen : xend - xdst;
|
|
|
|
const int neoffset = ne_xpos+ne_xoffset;
|
|
const int seoffset = se_xpos+se_xoffset;
|
|
const int minoffset = minimum<int>(neoffset,seoffset);
|
|
|
|
//FIXME: make it work with ne_ypos being <= 0
|
|
if(ne_ypos > 0 && xdst + len >= neoffset) {
|
|
len = neoffset - xdst;
|
|
if(len < 0)
|
|
len = 0;
|
|
} else if(ne_ypos > 0 && xdst + len >= seoffset) {
|
|
len = seoffset - xdst;
|
|
if(len < 0)
|
|
len = 0;
|
|
}
|
|
|
|
const int srcy = minimum<int>(yloc,surface->h-1);
|
|
short* startsrc = reinterpret_cast<short*>(surface->pixels) +
|
|
srcy*(surface->w+xpad) + (xoffset > xsrc ? xoffset:xsrc);
|
|
short* endsrc = startsrc + len;
|
|
|
|
assert(startsrc >= reinterpret_cast<short*>(surface->pixels) &&
|
|
endsrc <= reinterpret_cast<short*>(surface->pixels) +
|
|
(surface->w+xpad)*surface->h);
|
|
|
|
short* startdst =
|
|
reinterpret_cast<short*>(dst->pixels) + j*dst->w + xdst;
|
|
std::copy(startsrc,endsrc,startdst);
|
|
|
|
int extra = 0;
|
|
|
|
//if the line didn't make it to the next hex, then fill in with the
|
|
//last pixel up to the next hex
|
|
if(ne_ypos > 0 && xdst + len < minoffset && len > 0) {
|
|
extra = minimum(minoffset-(xdst + len),mapx()-(xdst+len));
|
|
std::fill(startdst+len,startdst+len+extra,startsrc[len-1]);
|
|
}
|
|
|
|
//copy any overlapping tiles on
|
|
for(std::vector<SDL_Surface*>::const_iterator ov = overlaps.begin();
|
|
ov != overlaps.end(); ++ov) {
|
|
const int srcy = minimum<int>(yloc,(*ov)->h-1);
|
|
const int w = (*ov)->w + (((*ov)->w%2) == 1 ? 1 : 0);
|
|
short* beg = reinterpret_cast<short*>((*ov)->pixels) +
|
|
srcy*w + (xoffset > xsrc ? xoffset:xsrc);
|
|
short* end = beg + len;
|
|
short* dst = startdst;
|
|
|
|
while(beg != end) {
|
|
if(*beg != 0) {
|
|
*dst = *beg;
|
|
}
|
|
|
|
++dst;
|
|
++beg;
|
|
}
|
|
|
|
//fill in any extra pixels on the end
|
|
if(extra > 0 && len > 0 && beg[-1] != 0)
|
|
std::fill(dst,dst+extra,beg[-1]);
|
|
}
|
|
|
|
if(grid_ && startsrc < endsrc) {
|
|
*startdst = grid_colour;
|
|
*(startdst+len-1) = grid_colour;
|
|
if(j == ypos || j == yend-1) {
|
|
std::fill(startdst,startdst+len,grid_colour);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(game_config::debug && debugHighlights_.count(gamemap::location(x,y))) {
|
|
SDL_Surface* const cross = getImage("cross.png");
|
|
if(cross != NULL)
|
|
draw_unit(xpos-xsrc,ypos-ysrc,cross,face_left,false,
|
|
debugHighlights_[gamemap::location(x,y)],0);
|
|
}
|
|
|
|
if(unit_image == NULL || energy_image == NULL)
|
|
return;
|
|
|
|
if(loc != hiddenUnit_) {
|
|
if(draw_hit) {
|
|
blend_with = hit_colour;
|
|
highlight_ratio = 0.7;
|
|
} else if(loc == advancingUnit_ && it != units_.end()) {
|
|
//set the advancing colour to white if it's a non-chaotic unit,
|
|
//otherwise black
|
|
blend_with = it->second.type().alignment() == unit_type::CHAOTIC ?
|
|
0x0001 : 0xFFFF;
|
|
highlight_ratio = advancingAmount_;
|
|
}
|
|
|
|
draw_unit(xpos-xsrc,ypos-ysrc,unit_image,face_left,false,
|
|
highlight_ratio,blend_with);
|
|
}
|
|
|
|
const bool energy_uses_alpha = highlight_ratio < 1.0 && blend_with == 0;
|
|
|
|
for(j = ypos; j != yend; ++j) {
|
|
const int yloc = ysrc+j-ypos;
|
|
const int xoffset = abs(yloc - static_cast<int>(zoom_/2.0))/2;
|
|
|
|
int xdst = xpos;
|
|
if(xoffset > xsrc) {
|
|
xdst += xoffset - xsrc;
|
|
if(xend < xdst)
|
|
continue;
|
|
}
|
|
|
|
const int maxlen = static_cast<int>(zoom_) - xoffset*2;
|
|
int len = ((xend - xdst) > maxlen) ? maxlen : xend - xdst;
|
|
|
|
short* startdst =
|
|
reinterpret_cast<short*>(dst->pixels) + j*dst->w + xdst;
|
|
|
|
const Pixel replace_energy =
|
|
Pixel(SDL_MapRGB(energy_image->format,0xFF,0xFF,0xFF));
|
|
const short new_energy = yloc >= show_energy_after ?
|
|
energy_colour : energy_loss_colour;
|
|
|
|
const int skip = yloc >= energy_bar_loc.first ? skip_energy_rows:0;
|
|
|
|
short* startenergy = NULL;
|
|
|
|
const int energy_w = energy_image->w + ((energy_image->w%2) == 1 ? 1:0);
|
|
if(yloc + skip < energy_image->h) {
|
|
startenergy = reinterpret_cast<short*>(energy_image->pixels) +
|
|
(yloc+skip)*energy_w + (xoffset > xsrc ? xoffset:xsrc);
|
|
|
|
for(int i = 0; i != len; ++i) {
|
|
if(startenergy != NULL && *startenergy != 0) {
|
|
if(!energy_uses_alpha) {
|
|
if(*startenergy == replace_energy) {
|
|
*startdst = new_energy;
|
|
} else {
|
|
*startdst = *startenergy;
|
|
}
|
|
} else {
|
|
Pixel p = *startenergy;
|
|
if(*startenergy == replace_energy) {
|
|
p = new_energy;
|
|
}
|
|
*startdst = alpha_blend_pixels(p,*startdst,
|
|
dst->format,highlight_ratio);
|
|
}
|
|
}
|
|
|
|
++startdst;
|
|
|
|
if(startenergy != NULL)
|
|
++startenergy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
const std::string& get_direction(int n)
|
|
{
|
|
static std::map<int,std::string> dirs;
|
|
if(dirs.empty()) {
|
|
dirs[0] = "-n";
|
|
dirs[1] = "-ne";
|
|
dirs[2] = "-se";
|
|
dirs[3] = "-s";
|
|
dirs[4] = "-sw";
|
|
dirs[5] = "-nw";
|
|
}
|
|
|
|
return dirs[n];
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<SDL_Surface*> display::getAdjacentTerrain(int x, int y,
|
|
IMAGE_TYPE image_type)
|
|
{
|
|
std::vector<SDL_Surface*> res;
|
|
gamemap::location loc(x,y);
|
|
|
|
const gamemap::TERRAIN current_terrain = map_.on_board(loc) ? map_[x][y]:0;
|
|
|
|
std::vector<bool> done(true,6);
|
|
gamemap::location adjacent[6];
|
|
get_adjacent_tiles(loc,adjacent);
|
|
int tiles[6];
|
|
for(int i = 0; i != 6; ++i) {
|
|
tiles[i] = map_.on_board(adjacent[i]) ?
|
|
(int)map_[adjacent[i].x][adjacent[i].y] :
|
|
(int)gamemap::VOID_TERRAIN;
|
|
}
|
|
|
|
const std::vector<gamemap::TERRAIN>& precedence =
|
|
map_.get_terrain_precedence();
|
|
std::vector<gamemap::TERRAIN>::const_iterator terrain =
|
|
std::find(precedence.begin(),precedence.end(),current_terrain);
|
|
|
|
if(terrain == precedence.end())
|
|
terrain = precedence.begin();
|
|
else
|
|
++terrain;
|
|
|
|
for(; terrain != precedence.end(); ++terrain){
|
|
//find somewhere that doesn't have overlap to use as a starting point
|
|
int start;
|
|
for(start = 0; start != 6; ++start) {
|
|
if(tiles[start] != *terrain)
|
|
break;
|
|
}
|
|
|
|
if(start == 6) {
|
|
start = 0;
|
|
}
|
|
|
|
//find all the directions overlap occurs from
|
|
for(int i = (start+1)%6, n = 0; i != start && n != 6; ++n) {
|
|
if(tiles[i] == *terrain) {
|
|
SDL_Surface* surface = NULL;
|
|
std::ostringstream stream;
|
|
for(int n = 0; tiles[i] == *terrain && n!=6; i = (i+1)%6, ++n) {
|
|
stream << get_direction(i);
|
|
SDL_Surface* const new_surface = getTerrain(
|
|
*terrain,image_type,x,y,stream.str());
|
|
|
|
if(new_surface == NULL) {
|
|
//if we don't have any surface at all,
|
|
//then move onto the next overlapped area
|
|
if(surface == NULL)
|
|
i = (i+1)%6;
|
|
break;
|
|
}
|
|
|
|
surface = new_surface;
|
|
}
|
|
|
|
if(surface != NULL)
|
|
res.push_back(surface);
|
|
} else {
|
|
i = (i+1)%6;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
SDL_Surface* display::getTerrain(gamemap::TERRAIN terrain,IMAGE_TYPE image_type,
|
|
int x, int y, const std::string& direction)
|
|
{
|
|
const bool tower = (terrain == gamemap::TOWER);
|
|
std::string image = "terrain/" + map_.get_terrain_info(terrain).image();
|
|
|
|
if(tower) {
|
|
int i;
|
|
for(i = 0; i != teams_.size(); ++i) {
|
|
if(teams_[i].owns_tower(gamemap::location(x,y))) {
|
|
char buf[50];
|
|
sprintf(buf,"-team%d",i+1);
|
|
image += buf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(i == teams_.size()) {
|
|
image += "-neutral";
|
|
}
|
|
}
|
|
|
|
if(terrain == gamemap::CASTLE &&
|
|
map_.is_starting_position(gamemap::location(x,y))) {
|
|
image = "terrain/keep";
|
|
}
|
|
|
|
image += direction + ".png";
|
|
|
|
return getImage(image,image_type);
|
|
}
|
|
|
|
void display::blit_surface(int x, int y, SDL_Surface* surface)
|
|
{
|
|
SDL_Surface* const target = video().getSurface();
|
|
|
|
const int srcx = x < 0 ? -x : 0;
|
|
const int srcw = x + surface->w > target->w ? target->w - x :
|
|
surface->w - srcx;
|
|
const int srcy = y < 0 ? -y : 0;
|
|
const int srch = y + surface->h > target->h ? target->h - x :
|
|
surface->h - srcy;
|
|
|
|
if(srcw <= 0 || srch <= 0 || srcx >= surface->w || srcy >= surface->h)
|
|
return;
|
|
|
|
SDL_LockSurface(surface);
|
|
SDL_LockSurface(target);
|
|
|
|
if(x < 0)
|
|
x = 0;
|
|
|
|
if(y < 0)
|
|
y = 0;
|
|
|
|
//lines are padded to always fit on 4-byte boundaries, so see if there
|
|
//is padding at the beginning of every line
|
|
const int padding = (surface->w%2) == 1 ? 1:0;
|
|
const int surface_width = surface->w + padding;
|
|
|
|
const short* src = reinterpret_cast<short*>(surface->pixels) +
|
|
srcy*surface_width + srcx;
|
|
short* dst = reinterpret_cast<short*>(target->pixels) + y*target->w + x;
|
|
|
|
static const short transperant = 0;
|
|
|
|
for(int i = 0; i != srch; ++i) {
|
|
const short* s = src + i*surface_width + padding;
|
|
const short* const end = s + srcw;
|
|
short* d = dst + i*target->w;
|
|
while(s != end) {
|
|
if(*s != transperant) {
|
|
*d = *s;
|
|
}
|
|
|
|
++s;
|
|
++d;
|
|
}
|
|
}
|
|
SDL_UnlockSurface(surface);
|
|
SDL_UnlockSurface(target);
|
|
}
|
|
|
|
SDL_Surface* display::getImage(const std::string& filename,
|
|
display::IMAGE_TYPE type)
|
|
{
|
|
if(type == GREYED)
|
|
return getImageTinted(filename,GREY_IMAGE);
|
|
else if(type == BRIGHTENED)
|
|
return getImageTinted(filename,BRIGHTEN_IMAGE);
|
|
|
|
std::map<std::string,SDL_Surface*>::iterator i;
|
|
|
|
if(type == SCALED) {
|
|
i = scaledImages_.find(filename);
|
|
if(i != scaledImages_.end())
|
|
return i->second;
|
|
}
|
|
|
|
i = images_.find(filename);
|
|
|
|
if(i == images_.end()) {
|
|
const std::string images_path = "images/";
|
|
const std::string images_filename = images_path + filename;
|
|
SDL_Surface* surf = NULL;
|
|
|
|
#ifdef WESNOTH_PATH
|
|
const std::string& fullpath = WESNOTH_PATH + std::string("/") +
|
|
images_filename;
|
|
surf = IMG_Load(fullpath.c_str());
|
|
#endif
|
|
|
|
if(surf == NULL)
|
|
surf = IMG_Load(images_filename.c_str());
|
|
|
|
if(surf == NULL) {
|
|
images_.insert(std::pair<std::string,SDL_Surface*>(filename,NULL));
|
|
return NULL;
|
|
}
|
|
|
|
SDL_Surface* const conv = SDL_ConvertSurface(surf,
|
|
screen_.getSurface()->format,
|
|
SDL_SWSURFACE);
|
|
SDL_FreeSurface(surf);
|
|
|
|
i = images_.insert(std::pair<std::string,SDL_Surface*>(filename,conv))
|
|
.first;
|
|
}
|
|
|
|
if(i->second == NULL)
|
|
return NULL;
|
|
|
|
if(type == UNSCALED) {
|
|
return i->second;
|
|
}
|
|
|
|
const int z = static_cast<int>(zoom_);
|
|
SDL_Surface* const new_surface = scale_surface(i->second,z,z);
|
|
|
|
if(new_surface == NULL)
|
|
return NULL;
|
|
|
|
scaledImages_.insert(std::pair<std::string,SDL_Surface*>(filename,
|
|
new_surface));
|
|
return new_surface;
|
|
}
|
|
|
|
SDL_Surface* display::getImageTinted(const std::string& filename, TINT tint)
|
|
{
|
|
std::map<std::string,SDL_Surface*>& image_map =
|
|
tint == GREY_IMAGE ? greyedImages_ : brightenedImages_;
|
|
|
|
const std::map<std::string,SDL_Surface*>::iterator itor =
|
|
image_map.find(filename);
|
|
|
|
if(itor != image_map.end())
|
|
return itor->second;
|
|
|
|
SDL_Surface* const base = getImage(filename,SCALED);
|
|
if(base == NULL)
|
|
return NULL;
|
|
|
|
SDL_Surface* const surface =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE,base->w,base->h,
|
|
base->format->BitsPerPixel,
|
|
base->format->Rmask,
|
|
base->format->Gmask,
|
|
base->format->Bmask,
|
|
base->format->Amask);
|
|
|
|
image_map.insert(std::pair<std::string,SDL_Surface*>(filename,surface));
|
|
|
|
short* begin = reinterpret_cast<short*>(base->pixels);
|
|
const short* const end = begin + base->h*(base->w + (base->w%2));
|
|
short* dest = reinterpret_cast<short*>(surface->pixels);
|
|
|
|
const int rmax = 0xFF;
|
|
const int gmax = 0xFF;
|
|
const int bmax = 0xFF;
|
|
|
|
while(begin != end) {
|
|
Uint8 red, green, blue;
|
|
SDL_GetRGB(*begin,base->format,&red,&green,&blue);
|
|
int r = int(red), g = int(green), b = int(blue);
|
|
|
|
if(tint == GREY_IMAGE) {
|
|
const double greyscale = (double(r)/(double)rmax +
|
|
double(g)/(double)gmax +
|
|
double(b)/(double)bmax)/3.0;
|
|
|
|
r = int(rmax*greyscale);
|
|
g = int(gmax*greyscale);
|
|
b = int(bmax*greyscale);
|
|
} else {
|
|
r = int(double(r)*1.5);
|
|
g = int(double(g)*1.5);
|
|
b = int(double(b)*1.5);
|
|
|
|
if(r > rmax)
|
|
r = rmax;
|
|
|
|
if(g > gmax)
|
|
g = gmax;
|
|
|
|
if(b > bmax)
|
|
b = bmax;
|
|
}
|
|
|
|
*dest = SDL_MapRGB(base->format,r,g,b);
|
|
|
|
++dest;
|
|
++begin;
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
SDL_Surface* display::getMinimap(int w, int h)
|
|
{
|
|
if(minimap_ == NULL) {
|
|
SDL_Surface* const surface = screen_.getSurface();
|
|
minimap_ = SDL_CreateRGBSurface(SDL_SWSURFACE,map_.x(),map_.y(),
|
|
surface->format->BitsPerPixel,
|
|
surface->format->Rmask,
|
|
surface->format->Gmask,
|
|
surface->format->Bmask,
|
|
surface->format->Amask);
|
|
if(minimap_ == NULL)
|
|
return NULL;
|
|
|
|
const int xpad = (minimap_->w%2) == 1 ? 1:0;
|
|
|
|
short* data = reinterpret_cast<short*>(minimap_->pixels);
|
|
for(int y = 0; y != map_.y(); ++y) {
|
|
for(int x = 0; x != map_.x(); ++x) {
|
|
|
|
*data = map_.get_terrain_info(map_[x][y]).get_rgb().
|
|
format(surface->format);
|
|
++data;
|
|
}
|
|
|
|
data += xpad;
|
|
}
|
|
}
|
|
|
|
while(minimap_ != NULL && minimap_->w*2 < w && minimap_->h*2 < h) {
|
|
SDL_Surface* const surf = minimap_;
|
|
minimap_ = scale_surface(minimap_,minimap_->w*2,minimap_->h*2);
|
|
SDL_FreeSurface(surf);
|
|
}
|
|
|
|
return minimap_;
|
|
}
|
|
|
|
void display::set_paths(const paths* paths_list)
|
|
{
|
|
pathsList_ = paths_list;
|
|
invalidate_all();
|
|
}
|
|
|
|
void display::move_unit(const std::vector<gamemap::location>& path, unit& u)
|
|
{
|
|
for(int i = 0; i+1 < path.size(); ++i) {
|
|
if(path[i+1].x > path[i].x) {
|
|
u.set_facing_left(true);
|
|
} else if(path[i+1].x < path[i].x) {
|
|
u.set_facing_left(false);
|
|
}
|
|
|
|
move_unit_between(path[i],path[i+1],u);
|
|
}
|
|
}
|
|
|
|
double display::get_location_x(const gamemap::location& loc) const
|
|
{
|
|
return static_cast<double>(loc.x)*zoom_*0.75 - xpos_;
|
|
}
|
|
|
|
double display::get_location_y(const gamemap::location& loc) const
|
|
{
|
|
return static_cast<double>(loc.y)*zoom_ - ypos_ +
|
|
((loc.x % 2) == 1 ? zoom_/2.0 : 0.0);
|
|
}
|
|
|
|
|
|
bool display::unit_attack_ranged(const gamemap::location& a,
|
|
const gamemap::location& b, int damage,
|
|
const attack_type& attack)
|
|
{
|
|
const unit_map::iterator att = units_.find(a);
|
|
const unit_map::iterator def = units_.find(b);
|
|
|
|
unit& attacker = att->second;
|
|
|
|
//the missile frames are based around the time when the missile impacts.
|
|
//the 'real' frames are based around the time when the missile launches.
|
|
const int first_missile = minimum<int>(-100,
|
|
attack.get_first_frame(attack_type::MISSILE_FRAME));
|
|
const int last_missile = attack.get_last_frame(attack_type::MISSILE_FRAME);
|
|
|
|
const int real_last_missile = last_missile - first_missile;
|
|
const int missile_impact = -first_missile;
|
|
|
|
const int time_resolution = 20;
|
|
|
|
const std::vector<attack_type::sfx>& sounds = attack.sound_effects();
|
|
std::vector<attack_type::sfx>::const_iterator sfx_it = sounds.begin();
|
|
|
|
const bool hits = damage > 0;
|
|
const int begin_at = attack.get_first_frame();
|
|
const int end_at = maximum((damage+1)*time_resolution+missile_impact,
|
|
maximum(attack.get_last_frame(),real_last_missile));
|
|
|
|
const double xsrc = get_location_x(a);
|
|
const double ysrc = get_location_y(a);
|
|
const double xdst = get_location_x(b);
|
|
const double ydst = get_location_y(b);
|
|
|
|
gamemap::location update_tiles[6];
|
|
get_adjacent_tiles(a,update_tiles);
|
|
|
|
const bool vflip = b.y > a.y || b.y == a.y && (a.x%2) == 0;
|
|
const bool hflip = b.x < a.x;
|
|
const attack_type::FRAME_DIRECTION dir =
|
|
(a.x == b.x) ? attack_type::VERTICAL:attack_type::DIAGONAL;
|
|
|
|
bool dead = false;
|
|
const int drain_speed = 1;
|
|
|
|
int flash_num = 0;
|
|
|
|
int ticks = SDL_GetTicks();
|
|
|
|
for(int i = begin_at; i < end_at; i += time_resolution) {
|
|
check_keys(*this);
|
|
|
|
//this is a while instead of an if, because there might be multiple
|
|
//sounds playing simultaneously or close together
|
|
while(!update_locked() && sfx_it != sounds.end() && i >= sfx_it->time) {
|
|
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
|
|
if(sfx.empty() == false) {
|
|
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
|
|
}
|
|
|
|
++sfx_it;
|
|
}
|
|
|
|
const std::string* const unit_image = attack.get_frame(i);
|
|
|
|
if(!update_locked()) {
|
|
SDL_Surface* const image = (unit_image == NULL) ?
|
|
NULL : getImage(*unit_image);
|
|
draw_tile(a.x,a.y,image);
|
|
}
|
|
|
|
Pixel defensive_colour = 0;
|
|
double defensive_alpha = 1.0;
|
|
|
|
if(damage > 0 && i >= missile_impact) {
|
|
if(def->second.gets_hit(drain_speed)) {
|
|
dead = true;
|
|
damage = 0;
|
|
} else {
|
|
damage -= drain_speed;
|
|
}
|
|
|
|
if(flash_num == 0 || flash_num == 2) {
|
|
defensive_alpha = 0.0;
|
|
defensive_colour = 0xF000;
|
|
}
|
|
|
|
++flash_num;
|
|
}
|
|
|
|
for(int j = 0; j != 6; ++j) {
|
|
if(update_tiles[j] != b) {
|
|
draw_tile(update_tiles[j].x,update_tiles[j].y);
|
|
}
|
|
}
|
|
|
|
draw_tile(b.x,b.y,NULL,defensive_alpha,defensive_colour);
|
|
|
|
if(i >= 0 && i < real_last_missile && !update_locked()) {
|
|
const int missile_frame = i + first_missile;
|
|
|
|
const std::string* missile_image
|
|
= attack.get_frame(missile_frame,attack_type::MISSILE_FRAME,dir);
|
|
|
|
static const std::string default_missile("missile-n.png");
|
|
static const std::string default_diag_missile("missile-ne.png");
|
|
if(missile_image == NULL) {
|
|
if(dir == attack_type::VERTICAL)
|
|
missile_image = &default_missile;
|
|
else
|
|
missile_image = &default_diag_missile;
|
|
}
|
|
|
|
SDL_Surface* const img = getImage(*missile_image);
|
|
if(img != NULL) {
|
|
double pos = double(missile_impact - i)/double(missile_impact);
|
|
if(pos < 0.0)
|
|
pos = 0.0;
|
|
const int xpos = int(xsrc*pos + xdst*(1.0-pos));
|
|
const int ypos = int(ysrc*pos + ydst*(1.0-pos));
|
|
|
|
draw_unit(xpos,ypos,img,!hflip,vflip);
|
|
}
|
|
}
|
|
|
|
const int wait_time = ticks + time_resolution - SDL_GetTicks();
|
|
if(wait_time > 0 && !turbo() && !update_locked())
|
|
SDL_Delay(wait_time);
|
|
|
|
ticks = SDL_GetTicks();
|
|
|
|
update_display();
|
|
}
|
|
|
|
if(dead) {
|
|
unit_die(b);
|
|
}
|
|
|
|
return dead;
|
|
}
|
|
|
|
void display::unit_die(const gamemap::location& loc, SDL_Surface* image)
|
|
{
|
|
if(update_locked())
|
|
return;
|
|
|
|
const int frame_time = 30;
|
|
int ticks = SDL_GetTicks();
|
|
|
|
for(double alpha = 1.0; alpha > 0.0; alpha -= 0.05) {
|
|
draw_tile(loc.x,loc.y,image,alpha);
|
|
|
|
const int wait_time = ticks + frame_time - SDL_GetTicks();
|
|
|
|
if(wait_time > 0 && !turbo())
|
|
SDL_Delay(wait_time);
|
|
|
|
ticks = SDL_GetTicks();
|
|
|
|
update_display();
|
|
}
|
|
}
|
|
|
|
bool display::unit_attack(const gamemap::location& a,
|
|
const gamemap::location& b, int damage,
|
|
const attack_type& attack)
|
|
{
|
|
log_scope("unit_attack");
|
|
invalidate_all();
|
|
draw(true,true);
|
|
|
|
const unit_map::iterator att = units_.find(a);
|
|
assert(att != units_.end());
|
|
|
|
unit& attacker = att->second;
|
|
|
|
const unit_map::iterator def = units_.find(b);
|
|
assert(def != units_.end());
|
|
|
|
if(b.x > a.x) {
|
|
att->second.set_facing_left(true);
|
|
def->second.set_facing_left(false);
|
|
} else if(b.x < a.x) {
|
|
att->second.set_facing_left(false);
|
|
def->second.set_facing_left(true);
|
|
}
|
|
|
|
if(attack.range() == attack_type::LONG_RANGE) {
|
|
return unit_attack_ranged(a,b,damage,attack);
|
|
}
|
|
|
|
const bool hits = damage > 0;
|
|
const std::vector<attack_type::sfx>& sounds = attack.sound_effects();
|
|
std::vector<attack_type::sfx>::const_iterator sfx_it = sounds.begin();
|
|
|
|
const int time_resolution = 20;
|
|
|
|
def->second.set_defending(true);
|
|
|
|
const int begin_at = minimum<int>(-200,attack.get_first_frame());
|
|
const int end_at = maximum<int>((damage+1)*time_resolution,
|
|
maximum<int>(200,
|
|
attack.get_last_frame()));
|
|
|
|
const double xsrc = get_location_x(a);
|
|
const double ysrc = get_location_y(a);
|
|
const double xdst = get_location_x(b)*0.6 + xsrc*0.4;
|
|
const double ydst = get_location_y(b)*0.6 + ysrc*0.4;
|
|
|
|
gamemap::location update_tiles[6];
|
|
get_adjacent_tiles(b,update_tiles);
|
|
|
|
bool dead = false;
|
|
const int drain_speed = 1;
|
|
|
|
int flash_num = 0;
|
|
|
|
int ticks = SDL_GetTicks();
|
|
|
|
hiddenUnit_ = a;
|
|
|
|
for(int i = begin_at; i < end_at; i += time_resolution) {
|
|
check_keys(*this);
|
|
|
|
//this is a while instead of an if, because there might be multiple
|
|
//sounds playing simultaneously or close together
|
|
while(!update_locked() && sfx_it != sounds.end() && i >= sfx_it->time) {
|
|
const std::string& sfx = hits ? sfx_it->on_hit : sfx_it->on_miss;
|
|
if(sfx.empty() == false) {
|
|
sound::play_sound(hits ? sfx_it->on_hit : sfx_it->on_miss);
|
|
}
|
|
|
|
++sfx_it;
|
|
}
|
|
|
|
for(int j = 0; j != 6; ++j) {
|
|
draw_tile(update_tiles[j].x,update_tiles[j].y);
|
|
}
|
|
|
|
int defender_colour = 0;
|
|
double defender_alpha = 1.0;
|
|
|
|
if(damage > 0 && i >= 0) {
|
|
if(def->second.gets_hit(drain_speed)) {
|
|
dead = true;
|
|
damage = 0;
|
|
} else {
|
|
damage -= drain_speed;
|
|
}
|
|
|
|
if(flash_num == 0 || flash_num == 2) {
|
|
defender_alpha = 0.0;
|
|
defender_colour = 0xF000;
|
|
}
|
|
|
|
++flash_num;
|
|
}
|
|
|
|
draw_tile(b.x,b.y,NULL,defender_alpha,defender_colour);
|
|
|
|
const std::string* unit_image = attack.get_frame(i);
|
|
if(unit_image == NULL)
|
|
unit_image = &attacker.image();
|
|
|
|
SDL_Surface* const image = (unit_image == NULL) ?
|
|
NULL : getImage(*unit_image);
|
|
|
|
const double pos = double(i)/double(i < 0 ? begin_at : end_at);
|
|
const int posx = int(pos*xsrc + (1.0-pos)*xdst);
|
|
const int posy = int(pos*ysrc + (1.0-pos)*ydst);
|
|
|
|
if(image != NULL && !update_locked())
|
|
draw_unit(posx,posy,image,attacker.facing_left());
|
|
|
|
const int wait_time = ticks + time_resolution - SDL_GetTicks();
|
|
if(wait_time > 0 && !turbo() && !update_locked())
|
|
SDL_Delay(wait_time);
|
|
|
|
ticks = SDL_GetTicks();
|
|
|
|
update_display();
|
|
}
|
|
|
|
hiddenUnit_ = gamemap::location();
|
|
def->second.set_defending(false);
|
|
|
|
if(dead) {
|
|
unit_die(b);
|
|
}
|
|
|
|
return dead;
|
|
}
|
|
|
|
void display::move_unit_between(const gamemap::location& a,
|
|
const gamemap::location& b,
|
|
const unit& u)
|
|
{
|
|
if(update_locked())
|
|
return;
|
|
|
|
const bool face_left = u.facing_left();
|
|
|
|
const double side_threshhold = 80.0;
|
|
|
|
double xsrc = get_location_x(a);
|
|
double ysrc = get_location_y(a);
|
|
double xdst = get_location_x(b);
|
|
double ydst = get_location_y(b);
|
|
|
|
const double nsteps = 10.0;
|
|
const double xstep = (xdst - xsrc)/nsteps;
|
|
const double ystep = (ydst - ysrc)/nsteps;
|
|
|
|
const int time_between_frames = 10;
|
|
int ticks = SDL_GetTicks();
|
|
|
|
int skips = 0;
|
|
|
|
for(double i = 0.0; i < nsteps; i += 1.0) {
|
|
check_keys(*this);
|
|
|
|
//checking keys may have invalidated all images (if they have
|
|
//zoomed in or out), so reget the image here
|
|
const SDL_Surface* const image = getImage(u.type().image());
|
|
if(image == NULL) {
|
|
std::cerr << "failed to get image " << u.type().image() << "\n";
|
|
return;
|
|
}
|
|
|
|
xsrc = get_location_x(a);
|
|
ysrc = get_location_y(a);
|
|
xdst = get_location_x(b);
|
|
ydst = get_location_y(b);
|
|
|
|
double xloc = xsrc + xstep*i;
|
|
double yloc = ysrc + ystep*i;
|
|
if(xloc < side_threshhold) {
|
|
scroll(xloc - side_threshhold,0.0);
|
|
}
|
|
|
|
if(yloc < side_threshhold) {
|
|
scroll(0.0,yloc - side_threshhold);
|
|
}
|
|
|
|
if(xloc + double(image->w) > this->mapx() - side_threshhold) {
|
|
scroll(((xloc + double(image->w)) -
|
|
(this->mapx() - side_threshhold)),0.0);
|
|
}
|
|
|
|
if(yloc + double(image->h) > this->y() - side_threshhold) {
|
|
scroll(0.0,((yloc + double(image->h)) -
|
|
(this->y() - side_threshhold)));
|
|
}
|
|
|
|
xsrc = get_location_x(a);
|
|
ysrc = get_location_y(a);
|
|
xdst = get_location_x(b);
|
|
ydst = get_location_y(b);
|
|
|
|
xloc = xsrc + xstep*i;
|
|
yloc = ysrc + ystep*i;
|
|
|
|
//invalidate the source tile and all adjacent tiles,
|
|
//since the unit can partially overlap adjacent tiles
|
|
gamemap::location adjacent[6];
|
|
get_adjacent_tiles(a,adjacent);
|
|
draw_tile(a.x,a.y);
|
|
for(int tile = 0; tile != 6; ++tile) {
|
|
draw_tile(adjacent[tile].x,adjacent[tile].y);
|
|
}
|
|
|
|
draw(false);
|
|
draw_unit((int)xloc,(int)yloc,image,face_left);
|
|
|
|
const int new_ticks = SDL_GetTicks();
|
|
const int wait_time = time_between_frames - (new_ticks - ticks);
|
|
if(wait_time > 0 && !turbo()) {
|
|
SDL_Delay(wait_time);
|
|
}
|
|
|
|
ticks = SDL_GetTicks();
|
|
|
|
if(wait_time >= 0 || skips == 4 || (i+1.0) >= nsteps) {
|
|
skips = 0;
|
|
update_display();
|
|
} else {
|
|
++skips;
|
|
}
|
|
}
|
|
}
|
|
|
|
void display::draw_unit(int x, int y, const SDL_Surface* image,
|
|
bool reverse, bool upside_down,
|
|
double alpha, Pixel blendto)
|
|
{
|
|
if(updatesLocked_) {
|
|
return;
|
|
}
|
|
|
|
const int w = mapx()-1;
|
|
const int h = this->y()-1;
|
|
if(x > w || y > h)
|
|
return;
|
|
|
|
const int image_w = image->w + ((image->w%2) == 1 ? 1 : 0);
|
|
|
|
SDL_Surface* const screen = screen_.getSurface();
|
|
|
|
const Pixel* src = reinterpret_cast<const short*>(image->pixels);
|
|
|
|
const int endy = (y + image->h) < h ? (y + image->h) : h;
|
|
const int endx = (x + image->w) < w ? (x + image->w) : w;
|
|
if(endx < x)
|
|
return;
|
|
|
|
const int len = endx - x;
|
|
|
|
if(y < 0) {
|
|
//this is adding to src, since y is negative
|
|
src -= image_w*y;
|
|
y = 0;
|
|
if(y >= endy)
|
|
return;
|
|
}
|
|
|
|
int xoffset = 0;
|
|
if(x < 0) {
|
|
xoffset = -x;
|
|
x = 0;
|
|
if(x >= endx)
|
|
return;
|
|
}
|
|
|
|
const SDL_PixelFormat* const fmt = screen->format;
|
|
|
|
static const Pixel semi_trans = ((0x19 >> fmt->Rloss) << fmt->Rshift) |
|
|
((0x19 >> fmt->Gloss) << fmt->Gshift) |
|
|
((0x11 >> fmt->Bloss) << fmt->Bshift);
|
|
|
|
if(upside_down)
|
|
src += image_w * (endy - y - 1);
|
|
|
|
const int src_increment = image_w * (upside_down ? -1 : 1);
|
|
|
|
for(; y != endy; ++y, src += src_increment) {
|
|
Pixel* dst =
|
|
reinterpret_cast<Pixel*>(screen->pixels) + y*screen->w + x;
|
|
|
|
if(alpha == 1.0) {
|
|
if(reverse) {
|
|
for(int i = xoffset; i != len; ++i) {
|
|
if(src[i] == semi_trans)
|
|
dst[i-xoffset] = alpha_blend_pixels(
|
|
0,dst[i-xoffset],fmt,0.5);
|
|
else if(src[i] != 0)
|
|
dst[i-xoffset] = src[i];
|
|
}
|
|
} else {
|
|
for(int i = image->w-1-xoffset; i != image->w-len-1; --i,++dst){
|
|
if(src[i] == semi_trans)
|
|
*dst = alpha_blend_pixels(0,*dst,fmt,0.5);
|
|
else if(src[i] != 0)
|
|
*dst = src[i];
|
|
}
|
|
}
|
|
} else {
|
|
if(reverse) {
|
|
for(int i = xoffset; i != len; ++i) {
|
|
const Pixel blend = blendto ? blendto : dst[i-xoffset];
|
|
|
|
if(src[i] != 0)
|
|
dst[i-xoffset]
|
|
= alpha_blend_pixels(src[i],blend,fmt,alpha);
|
|
}
|
|
} else {
|
|
for(int i = image->w-1-xoffset; i != image->w-len-1; --i,++dst){
|
|
const Pixel blend = blendto ? blendto : *dst;
|
|
if(src[i] != 0)
|
|
*dst = alpha_blend_pixels(src[i],blend,fmt,alpha);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::pair<int,int>& display::calculate_energy_bar()
|
|
{
|
|
if(energy_bar_count_.first != -1) {
|
|
return energy_bar_count_;
|
|
}
|
|
|
|
int first_row = -1;
|
|
int last_row = -1;
|
|
|
|
const SDL_Surface* const image = getImage("unmoved-energy.png");
|
|
const short* const begin = reinterpret_cast<short*>(image->pixels);
|
|
|
|
const Pixel colour = Pixel(SDL_MapRGB(image->format,0xFF,0xFF,0xFF));
|
|
|
|
for(int y = 0; y != image->h; ++y) {
|
|
const short* const i1 = begin + image->w*y;
|
|
const short* const i2 = i1 + image->w;
|
|
if(std::find(i1,i2,colour) != i2) {
|
|
if(first_row == -1)
|
|
first_row = y;
|
|
|
|
last_row = y;
|
|
}
|
|
}
|
|
|
|
energy_bar_count_ = std::pair<int,int>(first_row,last_row+1);
|
|
return energy_bar_count_;
|
|
}
|
|
|
|
void display::invalidate(const gamemap::location& loc)
|
|
{
|
|
if(!invalidateAll_ && loc.valid()) {
|
|
invalidated_.insert(loc);
|
|
}
|
|
}
|
|
|
|
void display::invalidate_all()
|
|
{
|
|
invalidateAll_ = true;
|
|
invalidated_.clear();
|
|
update_map_area();
|
|
}
|
|
|
|
void display::invalidate_unit()
|
|
{
|
|
invalidateUnit_ = true;
|
|
}
|
|
|
|
void display::recalculate_minimap()
|
|
{
|
|
if(minimap_ != NULL) {
|
|
SDL_FreeSurface(minimap_);
|
|
minimap_ = NULL;
|
|
}
|
|
}
|
|
|
|
void display::invalidate_game_status()
|
|
{
|
|
invalidateGameStatus_ = true;
|
|
}
|
|
|
|
void display::add_overlay(const gamemap::location& loc, const std::string& img)
|
|
{
|
|
overlays_.insert(std::pair<gamemap::location,std::string>(loc,img));
|
|
}
|
|
|
|
void display::remove_overlay(const gamemap::location& loc)
|
|
{
|
|
overlays_.erase(loc);
|
|
}
|
|
|
|
void display::update_whole_screen()
|
|
{
|
|
SDL_Rect rect;
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.w = x();
|
|
rect.h = y();
|
|
update_rect(rect);
|
|
}
|
|
|
|
void display::update_map_area()
|
|
{
|
|
SDL_Rect rect;
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.w = mapx();
|
|
rect.h = y();
|
|
update_rect(rect);
|
|
}
|
|
|
|
void display::update_side_bar()
|
|
{
|
|
SDL_Rect rect;
|
|
rect.x = mapx();
|
|
rect.y = 0;
|
|
rect.w = x() - mapx();
|
|
rect.h = y();
|
|
update_rect(rect);
|
|
}
|
|
|
|
struct inside_rect {
|
|
|
|
inside_rect(const SDL_Rect& a) : a(a)
|
|
{}
|
|
|
|
bool operator()(const SDL_Rect& b) const
|
|
{
|
|
if(a.x <= b.x && a.y <= b.y && a.x+a.w >= b.x+b.w && a.y+a.h >= b.y+b.h)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
SDL_Rect a;
|
|
};
|
|
|
|
struct outside_rect {
|
|
|
|
outside_rect(const SDL_Rect& b) : b(b)
|
|
{}
|
|
|
|
bool operator()(const SDL_Rect& a) const
|
|
{
|
|
if(a.x <= b.x && a.y <= b.y && a.x+a.w >= b.x+b.w && a.y+a.h >= b.y+b.h)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
SDL_Rect b;
|
|
};
|
|
|
|
void display::update_rect(const SDL_Rect& rect)
|
|
{
|
|
std::remove_if(updateRects_.begin(),updateRects_.end(),inside_rect(rect));
|
|
if(std::find_if(updateRects_.begin(),updateRects_.end(),outside_rect(rect))
|
|
!= updateRects_.end())
|
|
return;
|
|
|
|
updateRects_.push_back(rect);
|
|
}
|
|
|
|
void display::set_team(int team)
|
|
{
|
|
currentTeam_ = team;
|
|
}
|
|
|
|
void display::set_advancing_unit(const gamemap::location& loc, double amount)
|
|
{
|
|
advancingUnit_ = loc;
|
|
advancingAmount_ = amount;
|
|
draw_tile(loc.x,loc.y);
|
|
}
|
|
|
|
void display::lock_updates(bool value)
|
|
{
|
|
if(value == true)
|
|
++updatesLocked_;
|
|
else
|
|
--updatesLocked_;
|
|
}
|
|
|
|
bool display::update_locked() const
|
|
{
|
|
return updatesLocked_ > 0;
|
|
}
|
|
|
|
bool display::turbo() const
|
|
{
|
|
bool res = turbo_;
|
|
if(keys_[KEY_LSHIFT] || keys_[KEY_RSHIFT])
|
|
res = !res;
|
|
|
|
return res;
|
|
}
|
|
|
|
void display::set_turbo(bool turbo)
|
|
{
|
|
turbo_ = turbo;
|
|
}
|
|
|
|
void display::set_grid(bool grid)
|
|
{
|
|
grid_ = grid;
|
|
}
|
|
|
|
void display::debug_highlight(const gamemap::location& loc, double amount)
|
|
{
|
|
assert(game_config::debug);
|
|
debugHighlights_[loc] += amount;
|
|
}
|
|
|
|
void display::clear_debug_highlights()
|
|
{
|
|
debugHighlights_.clear();
|
|
}
|