scummvm/engines/gob/aniobject.cpp
Eugene Sandulenko 5dcdfd2600
GOB: Dual-license the gob engine under LGPL
The major contributors provided their consent:
DrMcCoy, Strangerke, sdelamarre, sev.

The goal is to allow the re-release of this code under
Switch, which is incompatible with pure GPL
2024-01-08 01:44:04 +01:00

318 lines
7.3 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/surface.h"
#include "gob/anifile.h"
#include "gob/cmpfile.h"
#include "gob/aniobject.h"
namespace Gob {
ANIObject::ANIObject(const ANIFile &ani) : _ani(&ani), _cmp(nullptr),
_visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) {
setAnimation(0);
setPosition();
}
ANIObject::ANIObject(const CMPFile &cmp) : _ani(nullptr), _cmp(&cmp),
_visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) {
setAnimation(0);
setPosition();
}
ANIObject::~ANIObject() {
}
void ANIObject::setVisible(bool visible) {
_visible = visible;
}
bool ANIObject::isVisible() const {
return _visible;
}
void ANIObject::setPause(bool pause) {
_paused = pause;
}
bool ANIObject::isPaused() const {
return _paused;
}
void ANIObject::setMode(Mode mode) {
_mode = mode;
}
void ANIObject::setAnimation(uint16 animation) {
_animation = animation;
_frame = 0;
}
void ANIObject::rewind() {
_frame = 0;
}
void ANIObject::setFrame(uint16 frame) {
_frame = frame % _ani->getAnimationInfo(_animation).frameCount;
}
void ANIObject::setPosition() {
// CMP "animations" have no default position
if (_cmp)
return;
if (_animation >= _ani->getAnimationCount())
return;
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
_x = animation.x;
_y = animation.y;
}
void ANIObject::setPosition(int16 x, int16 y) {
_x = x;
_y = y;
}
void ANIObject::getPosition(int16 &x, int16 &y) const {
x = _x;
y = _y;
}
void ANIObject::getFramePosition(int16 &x, int16 &y, uint16 n) const {
// CMP "animations" have no specific frame positions
if (_cmp) {
getPosition(x, y);
return;
}
if (_animation >= _ani->getAnimationCount())
return;
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
if (_frame >= animation.frameCount)
return;
// If we're paused, we don't advance any frames
if (_paused)
n = 0;
// Number of cycles run through after n frames
uint16 cycles = (_frame + n) / animation.frameCount;
// Frame position after n frames
uint16 frame = (_frame + n) % animation.frameCount;
// Only doing one cycle?
if (_mode == kModeOnce)
cycles = MAX<uint16>(cycles, 1);
x = _x + animation.frameAreas[frame].left + cycles * animation.deltaX;
y = _y + animation.frameAreas[frame].top + cycles * animation.deltaY;
}
void ANIObject::getFrameSize(int16 &width, int16 &height, uint16 n) const {
if (_cmp) {
width = _cmp->getWidth (_animation);
height = _cmp->getHeight(_animation);
return;
}
if (_animation >= _ani->getAnimationCount())
return;
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
if (_frame >= animation.frameCount)
return;
// If we're paused, we don't advance any frames
if (_paused)
n = 0;
// Frame position after n frames
uint16 frame = (_frame + n) % animation.frameCount;
width = animation.frameAreas[frame].right - animation.frameAreas[frame].left + 1;
height = animation.frameAreas[frame].bottom - animation.frameAreas[frame].top + 1;
}
bool ANIObject::isIn(int16 x, int16 y) const {
if (!isVisible())
return false;
int16 frameX, frameY, frameWidth, frameHeight;
getFramePosition(frameX, frameY);
getFrameSize(frameWidth, frameHeight);
if ((x < frameX) || (y < frameY))
return false;
if ((x > (frameX + frameWidth)) || (y > (frameY + frameHeight)))
return false;
return true;
}
bool ANIObject::isIn(const ANIObject &obj) const {
if (!isVisible() || !obj.isVisible())
return false;
int16 frameX, frameY, frameWidth, frameHeight;
getFramePosition(frameX, frameY);
getFrameSize(frameWidth, frameHeight);
return obj.isIn(frameX , frameY ) ||
obj.isIn(frameX + frameWidth - 1, frameY ) ||
obj.isIn(frameX , frameY + frameHeight - 1) ||
obj.isIn(frameX + frameWidth - 1, frameY + frameHeight - 1);
}
bool ANIObject::draw(Surface &dest, int16 &left, int16 &top,
int16 &right, int16 &bottom) {
if (!_visible)
return false;
if (_cmp)
return drawCMP(dest, left, top, right, bottom);
else if (_ani)
return drawANI(dest, left, top, right, bottom);
return false;
}
bool ANIObject::drawCMP(Surface &dest, int16 &left, int16 &top,
int16 &right, int16 &bottom) {
if (!hasBuffer()) {
uint16 width, height;
_cmp->getMaxSize(width, height);
resizeBuffer(width, height);
}
left = _x;
top = _y;
right = _x + _cmp->getWidth (_animation) - 1;
bottom = _y + _cmp->getHeight(_animation) - 1;
if (!saveScreen(dest, left, top, right, bottom))
return false;
_cmp->draw(dest, _animation, _x, _y, 0);
return true;
}
bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top,
int16 &right, int16 &bottom) {
if (!hasBuffer()) {
uint16 width, height;
_ani->getMaxSize(width, height);
resizeBuffer(width, height);
}
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
if (_frame >= animation.frameCount)
return false;
const ANIFile::FrameArea &area = animation.frameAreas[_frame];
left = _x + area.left;
top = _y + area.top;
right = _x + area.right;
bottom = _y + area.bottom;
if (!saveScreen(dest, left, top, right, bottom))
return false;
_ani->draw(dest, _animation, _frame, _x, _y);
return true;
}
bool ANIObject::clear(Surface &dest, int16 &left, int16 &top,
int16 &right, int16 &bottom) {
return restoreScreen(dest, left, top, right, bottom);
}
void ANIObject::advance() {
if (_paused)
return;
// CMP "animations" have only one frame
if (_cmp)
return;
if (_animation >= _ani->getAnimationCount())
return;
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
_frame = (_frame + 1) % animation.frameCount;
if (_frame == 0) {
_x += animation.deltaX;
_y += animation.deltaY;
if (_mode == kModeOnce) {
_paused = true;
_visible = false;
}
}
}
uint16 ANIObject::getAnimation() const {
return _animation;
}
uint16 ANIObject::getFrame() const {
return _frame;
}
bool ANIObject::lastFrame() const {
// CMP "animations" have only one frame
if (_cmp)
return true;
if (_animation >= _ani->getAnimationCount())
return true;
const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation);
return (_frame + 1) >= animation.frameCount;
}
} // End of namespace Gob