/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom 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 3 of the License, or
* (at your option) any later version.
*
* OpenXcom 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 OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include "../fmath.h"
#include "MiniMapView.h"
#include "MiniMapState.h"
#include "Pathfinding.h"
#include "../Savegame/Tile.h"
#include "Camera.h"
#include "../Engine/Action.h"
#include "../Interface/Cursor.h"
#include "../Savegame/SavedBattleGame.h"
#include "../Engine/Game.h"
#include "../Engine/SurfaceSet.h"
#include "../Mod/Mod.h"
#include "../Mod/Armor.h"
#include "../Engine/Options.h"
#include "../Engine/Screen.h"
namespace OpenXcom
{
const int CELL_WIDTH = 4;
const int CELL_HEIGHT = 4;
const int MAX_FRAME = 2;
/**
* Initializes all the elements in the MiniMapView.
* @param w The MiniMapView width.
* @param h The MiniMapView height.
* @param x The MiniMapView x origin.
* @param y The MiniMapView y origin.
* @param game Pointer to the core game.
* @param camera The Battlescape camera.
* @param battleGame Pointer to the SavedBattleGame.
*/
MiniMapView::MiniMapView(int w, int h, int x, int y, Game * game, Camera * camera, SavedBattleGame * battleGame) : InteractiveSurface(w, h, x, y), _game(game), _camera(camera), _battleGame(battleGame), _frame(0), _isMouseScrolling(false), _isMouseScrolled(false), _xBeforeMouseScrolling(0), _yBeforeMouseScrolling(0), _mouseScrollX(0), _mouseScrollY(0), _mouseScrollingStartTime(0), _totalMouseMoveX(0), _totalMouseMoveY(0), _mouseMovedOverThreshold(false)
{
_set = _game->getMod()->getSurfaceSet("SCANG.DAT");
}
/**
* Draws the minimap.
*/
void MiniMapView::draw()
{
int _startX = _camera->getCenterPosition().x - ((getWidth() / CELL_WIDTH) / 2);
int _startY = _camera->getCenterPosition().y - ((getHeight() / CELL_HEIGHT) / 2);
InteractiveSurface::draw();
if (!_set)
{
return;
}
drawRect(0, 0, getWidth(), getHeight(), 15);
this->lock();
for (int lvl = 0; lvl <= _camera->getCenterPosition().z; lvl++)
{
int py = _startY;
for (int y = Surface::getY(); y < getHeight() + Surface::getY(); y += CELL_HEIGHT)
{
int px = _startX;
for (int x = Surface::getX(); x < getWidth() + Surface::getX(); x += CELL_WIDTH)
{
Position p(px, py, lvl);
Tile *t = _battleGame->getTile(p);
if (!t)
{
px++;
continue;
}
for (int i = O_FLOOR; i <= O_OBJECT; i++)
{
MapData *data = t->getMapData((TilePart)i);
if (data && data->getMiniMapIndex())
{
Surface *s = _set->getFrame(data->getMiniMapIndex() + 35);
if (s)
{
int shade = 16;
if (t->isDiscovered(2))
{
shade = t->getShade();
if (shade > 7) shade = 7; //vanilla
}
s->blitNShade(this, x, y, shade);
}
}
}
// alive units
if (t->getUnit() && t->getUnit()->getVisible())
{
int frame = t->getUnit()->getMiniMapSpriteIndex();
int size = t->getUnit()->getArmor()->getSize();
frame += (t->getPosition().y - t->getUnit()->getPosition().y) * size;
frame += t->getPosition().x - t->getUnit()->getPosition().x;
frame += _frame * size * size;
Surface * s = _set->getFrame(frame);
if (size > 1 && t->getUnit()->getFaction() == FACTION_NEUTRAL)
{
s->blitNShade(this, x, y, 0, false, Pathfinding::red);
}
else
{
s->blitNShade(this, x, y, 0);
}
}
// perhaps (at least one) item on this tile?
if (t->isDiscovered(2) && !t->getInventory()->empty())
{
int frame = 9 + _frame;
Surface * s = _set->getFrame(frame);
s->blitNShade(this, x, y, 0);
}
px++;
}
py++;
}
}
this->unlock();
int centerX = getWidth() / 2 - 1;
int centerY = getHeight() / 2 - 1;
Uint8 color = 1 + _frame * 3;
int xOffset = CELL_WIDTH / 2;
int yOffset = CELL_HEIGHT / 2;
drawLine(centerX - CELL_WIDTH, centerY - CELL_HEIGHT,
centerX - xOffset, centerY - yOffset,
color); // top left
drawLine(centerX + xOffset, centerY - yOffset,
centerX + CELL_WIDTH, centerY - CELL_HEIGHT,
color); // top right
drawLine(centerX - CELL_WIDTH, centerY + CELL_HEIGHT,
centerX - xOffset, centerY + yOffset,
color); // bottom left
drawLine(centerX + CELL_WIDTH, centerY + CELL_HEIGHT,
centerX + xOffset, centerY + yOffset,
color); //bottom right
}
/**
* Increments the displayed level.
* @return New display level.
*/
int MiniMapView::up()
{
_camera->setViewLevel(_camera->getViewLevel()+1);
_redraw = true;
return _camera->getViewLevel();
}
/**
* Decrements the displayed level.
* @return New display level.
*/
int MiniMapView::down()
{
_camera->setViewLevel(_camera->getViewLevel()-1);
_redraw = true;
return _camera->getViewLevel();
}
/**
* Handles mouse presses on the minimap. Enters mouse-moving mode when the right button is used.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void MiniMapView::mousePress(Action *action, State *state)
{
InteractiveSurface::mousePress(action, state);
if (action->getDetails()->button.button == Options::battleDragScrollButton)
{
_isMouseScrolling = true;
_isMouseScrolled = false;
SDL_GetMouseState(&_xBeforeMouseScrolling, &_yBeforeMouseScrolling);
_posBeforeMouseScrolling = _camera->getCenterPosition();
if (!Options::battleDragScrollInvert && _cursorPosition.z == 0)
{
_cursorPosition.x = action->getDetails()->motion.x;
_cursorPosition.y = action->getDetails()->motion.y;
// the Z is irrelevant to our mouse position, but we can use it as a boolean to check if the position is set or not
_cursorPosition.z = 1;
}
_mouseScrollX = 0; _mouseScrollY = 0;
_totalMouseMoveX = 0; _totalMouseMoveY = 0;
_mouseMovedOverThreshold = false;
_mouseScrollingStartTime = SDL_GetTicks();
}
}
/**
* Handles mouse clicks on the minimap. Will change the camera center to the clicked point.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void MiniMapView::mouseClick(Action *action, State *state)
{
InteractiveSurface::mouseClick(action, state);
// The following is the workaround for a rare problem where sometimes
// the mouse-release event is missed for any reason.
// However if the SDL is also missed the release event, then it is to no avail :(
// (this part handles the release if it is missed and now an other button is used)
if (_isMouseScrolling) {
if (action->getDetails()->button.button != Options::battleDragScrollButton
&& 0==(SDL_GetMouseState(0,0)&SDL_BUTTON(Options::battleDragScrollButton))) { // so we missed again the mouse-release :(
// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
if ((!_mouseMovedOverThreshold) && ((int)(SDL_GetTicks() - _mouseScrollingStartTime) <= (Options::dragScrollTimeTolerance)))
{ _camera->centerOnPosition(_posBeforeMouseScrolling); _redraw = true; }
_isMouseScrolled = _isMouseScrolling = false;
stopScrolling(action);
}
}
// Drag-Scroll release: release mouse-scroll-mode
if (_isMouseScrolling)
{
// While scrolling, other buttons are ineffective
if (action->getDetails()->button.button == Options::battleDragScrollButton)
{
_isMouseScrolling = false;
stopScrolling(action);
}
else
{
return;
}
// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
if ((!_mouseMovedOverThreshold) && ((int)(SDL_GetTicks() - _mouseScrollingStartTime) <= (Options::dragScrollTimeTolerance)))
{
_isMouseScrolled = false;
stopScrolling(action);
_camera->centerOnPosition(_posBeforeMouseScrolling);
_redraw = true;
}
if (_isMouseScrolled) return;
}
if (action->getDetails()->button.button == SDL_BUTTON_RIGHT)
{
((MiniMapState*)(state))->btnOkClick(action);
}
if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
{
int origX = action->getRelativeXMouse() / action->getXScale();
int origY = action->getRelativeYMouse() / action->getYScale();
// get offset (in cells) of the click relative to center of screen
int xOff = (origX / CELL_WIDTH) - ((getWidth() / 2) / CELL_WIDTH);
int yOff = (origY / CELL_HEIGHT) - ((getHeight() / 2) / CELL_HEIGHT);
// center the camera on this new position
int newX = _camera->getCenterPosition().x + xOff;
int newY = _camera->getCenterPosition().y + yOff;
_camera->centerOnPosition(Position(newX,newY,_camera->getViewLevel()));
_redraw = true;
}
}
/**
* Handles moving over the minimap.
* Will change the camera center when the mouse is moved in mouse-moving mode.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void MiniMapView::mouseOver(Action *action, State *state)
{
InteractiveSurface::mouseOver(action, state);
if (_isMouseScrolling && action->getDetails()->type == SDL_MOUSEMOTION)
{
// The following is the workaround for a rare problem where sometimes
// the mouse-release event is missed for any reason.
// However if the SDL is also missed the release event, then it is to no avail :(
// (checking: is the dragScroll-mouse-button still pressed?)
if (0==(SDL_GetMouseState(0,0)&SDL_BUTTON(Options::battleDragScrollButton))) { // so we missed again the mouse-release :(
// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
if ((!_mouseMovedOverThreshold) && ((int)(SDL_GetTicks() - _mouseScrollingStartTime) <= (Options::dragScrollTimeTolerance)))
{
_camera->centerOnPosition(_posBeforeMouseScrolling);
_redraw = true;
}
_isMouseScrolled = _isMouseScrolling = false;
stopScrolling(action);
return;
}
_isMouseScrolled = true;
if (Options::touchEnabled == false)
{
// Set the mouse cursor back
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_WarpMouse(_xBeforeMouseScrolling, _yBeforeMouseScrolling);
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
}
// Check the threshold
_totalMouseMoveX += action->getDetails()->motion.xrel;
_totalMouseMoveY += action->getDetails()->motion.yrel;
if (!_mouseMovedOverThreshold)
_mouseMovedOverThreshold = ((std::abs(_totalMouseMoveX) > Options::dragScrollPixelTolerance) || (std::abs(_totalMouseMoveY) > Options::dragScrollPixelTolerance));
// Calculate the move
int newX, newY;
int scrollX, scrollY;
if (Options::battleDragScrollInvert)
{
scrollX = action->getDetails()->motion.xrel;
scrollY = action->getDetails()->motion.yrel;
}
else
{
scrollX = -action->getDetails()->motion.xrel;
scrollY = -action->getDetails()->motion.yrel;
}
_mouseScrollX += scrollX;
_mouseScrollY += scrollY;
newX = _posBeforeMouseScrolling.x + _mouseScrollX / action->getXScale() / 4;
newY = _posBeforeMouseScrolling.y + _mouseScrollY / action->getYScale() / 4;
// Keep the limits...
if (newX < -1 || _camera->getMapSizeX() < newX)
{
_mouseScrollX -= scrollX;
newX = _posBeforeMouseScrolling.x + _mouseScrollX / 4;
}
if (newY < -1 || _camera->getMapSizeY() < newY)
{
_mouseScrollY -= scrollY;
newY = _posBeforeMouseScrolling.y + _mouseScrollY / 4;
}
// Scrolling
_camera->centerOnPosition(Position(newX,newY,_camera->getViewLevel()));
_redraw = true;
if (Options::touchEnabled == false)
{
// We don't want to see the mouse-cursor jumping :)
if (Options::battleDragScrollInvert)
{
action->getDetails()->motion.x = _xBeforeMouseScrolling;
action->getDetails()->motion.y = _yBeforeMouseScrolling;
}
else
{
Position delta(-scrollX, -scrollY, 0);
int barWidth = _game->getScreen()->getCursorLeftBlackBand();
int barHeight = _game->getScreen()->getCursorTopBlackBand();
int cursorX = _cursorPosition.x + delta.x;
int cursorY =_cursorPosition.y + delta.y;
_cursorPosition.x = Clamp(cursorX, (int)Round(getX() * action->getXScale()) + barWidth, (int)Round((getX() + getWidth()) * action->getXScale()) + barWidth);
_cursorPosition.y = Clamp(cursorY, (int)Round(getY() * action->getYScale()) + barHeight, (int)Round((getY() + getHeight()) * action->getYScale()) + barHeight);
action->getDetails()->motion.x = _cursorPosition.x;
action->getDetails()->motion.y = _cursorPosition.y;
}
}
_game->getCursor()->handle(action);
}
}
/**
* Handles moving into the minimap.
* Stops the mouse-scrolling mode, if its left on.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void MiniMapView::mouseIn(Action *action, State *state)
{
InteractiveSurface::mouseIn(action, state);
_isMouseScrolling = false;
setButtonPressed(SDL_BUTTON_RIGHT, false);
}
/**
* Updates the minimap animation.
*/
void MiniMapView::animate()
{
_frame++;
if (_frame > MAX_FRAME)
{
_frame = 0;
}
_redraw = true;
}
void MiniMapView::stopScrolling(Action *action)
{
if (!Options::battleDragScrollInvert)
{
SDL_WarpMouse(_cursorPosition.x, _cursorPosition.y);
action->setMouseAction(_cursorPosition.x, _cursorPosition.y, getX(), getY());
}
// reset our "mouse position stored" flag
_cursorPosition.z = 0;
}
}
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 't->getUnit()' expression repeatedly.
↑ V807 Decreased performance. Consider creating a reference to avoid using the 'action->getDetails()->motion' expression repeatedly.
↑ V807 Decreased performance. Consider creating a reference to avoid using the '_camera->getCenterPosition()' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'action->getDetails()' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'action->getDetails()' expression repeatedly.