Skip to content

Commit

Permalink
PEGASUS: Implement screen shaking
Browse files Browse the repository at this point in the history
Would be great to be able to use OSystem's stuff, but that only handles vertical shaking.
  • Loading branch information
Matthew Hoops committed Oct 17, 2011
1 parent 802873d commit 0dada6e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 3 deletions.
99 changes: 99 additions & 0 deletions engines/pegasus/graphics.cpp
Expand Up @@ -212,5 +212,104 @@ void GraphicsManager::doFadeInSync(const TimeValue, const TimeValue, uint32) {
void GraphicsManager::markCursorAsDirty() {
_modifiedScreen = true;
}

void GraphicsManager::newShakePoint(int32 index1, int32 index2, int32 maxRadius) {
int32 index3 = (index1 + index2) >> 1;

if (maxRadius == 0) {
_shakeOffsets[index3].x = ((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1);
_shakeOffsets[index3].y = ((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1);
} else {
double angle = (int32)(_vm->getRandomNumber(360 - 1) * 3.1415926535 / 180);
int32 radius = maxRadius;
_shakeOffsets[index3].x = (int32)(((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1) +
cos(angle) / 2 * radius);
_shakeOffsets[index3].y = (int32)(((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1) +
sin(angle) * radius);
}

if (index1 < index3 - 1)
newShakePoint(index1, index3, maxRadius * 2 / 3);

if (index3 < index2 - 1)
newShakePoint(index3, index2, maxRadius * 2 / 3);
}

void GraphicsManager::shakeTheWorld(TimeValue duration, TimeScale scale) {
if (duration == 0 || scale == 0)
return;

_shakeOffsets[0].x = 0;
_shakeOffsets[0].y = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) / 4].x = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) / 4].y = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) / 2].x = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) / 2].y = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].x = 0;
_shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].y = 0;
_shakeOffsets[kMaxShakeOffsets - 1].x = 0;
_shakeOffsets[kMaxShakeOffsets - 1].y = 0;

newShakePoint(0, (kMaxShakeOffsets - 1) / 4, 8);
newShakePoint((kMaxShakeOffsets - 1) / 4, (kMaxShakeOffsets - 1) / 2, 6);
newShakePoint((kMaxShakeOffsets - 1) / 2, (kMaxShakeOffsets - 1) * 3 / 4, 4);
newShakePoint((kMaxShakeOffsets - 1) * 3 / 4, kMaxShakeOffsets - 1, 3);

Common::Point lastOffset(0, 0);

// Convert to millis
duration = duration * 1000 / scale;

uint32 startTime = g_system->getMillis();

while (g_system->getMillis() < startTime + duration) {
Common::Point thisOffset = _shakeOffsets[(g_system->getMillis() - startTime) * (kMaxShakeOffsets - 1) / duration];
if (thisOffset != lastOffset) {
// Fill the screen with black
Graphics::Surface *screen = g_system->lockScreen();
screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0));
g_system->unlockScreen();

// Calculate the src/dst offsets and the width/height
int32 srcOffsetX, dstOffsetX, width;

if (thisOffset.x > 0) {
srcOffsetX = 0;
dstOffsetX = thisOffset.x;
width = 640 - dstOffsetX;
} else {
srcOffsetX = -thisOffset.x;
dstOffsetX = 0;
width = 640 - srcOffsetX;
}

int32 srcOffsetY, dstOffsetY, height;

if (thisOffset.y > 0) {
srcOffsetY = 0;
dstOffsetY = thisOffset.y;
height = 480 - dstOffsetY;
} else {
srcOffsetY = -thisOffset.y;
dstOffsetY = 0;
height = 480 - srcOffsetY;
}

// Now copy to the screen
g_system->copyRectToScreen((byte *)_workArea.getBasePtr(srcOffsetX, srcOffsetY), _workArea.pitch,
dstOffsetX, dstOffsetY, width, height);
g_system->updateScreen();

lastOffset = thisOffset;
}

g_system->delayMillis(10);
}

if (lastOffset.x != 0 || lastOffset.y != 0) {
g_system->copyRectToScreen((byte *)_workArea.pixels, _workArea.pitch, 0, 0, 640, 480);
g_system->updateScreen();
}
}

} // End of namespace Pegasus
6 changes: 6 additions & 0 deletions engines/pegasus/graphics.h
Expand Up @@ -57,6 +57,7 @@ friend class Cursor;
Graphics::Surface *getWorkArea() { return &_workArea; }
void clearScreen();
DisplayElement *findDisplayElement(const tDisplayElementID id);
void shakeTheWorld(TimeValue time, TimeScale scale);

// These default to black
void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0);
Expand All @@ -73,6 +74,11 @@ friend class Cursor;
tDisplayOrder _backLayer, _frontLayer;
DisplayElement *_firstDisplayElement, *_lastDisplayElement;
Graphics::Surface _workArea;

// Shake Shake Shake!
static const int kMaxShakeOffsets = 17;
Common::Point _shakeOffsets[kMaxShakeOffsets];
void newShakePoint(int32 index1, int32 index2, int32 maxRadius);
};

} // End of namespace Pegasus
Expand Down
7 changes: 4 additions & 3 deletions engines/pegasus/neighborhood/neighborhood.cpp
Expand Up @@ -30,6 +30,7 @@
#include "pegasus/cursor.h"
#include "pegasus/energymonitor.h"
#include "pegasus/gamestate.h"
#include "pegasus/graphics.h"
#include "pegasus/input.h"
#include "pegasus/interaction.h"
#include "pegasus/interface.h"
Expand Down Expand Up @@ -726,7 +727,8 @@ void Neighborhood::turnTo(const tDirectionConstant direction) {
if (g_map)
g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction);

_pushIn.copyToCurrentPort();
// FIXME: This isn't right. Crazy TGWorldSaver stuff
//_pushIn.copyToCurrentPort();

// Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to
// always when turning to a new view?
Expand Down Expand Up @@ -1419,8 +1421,7 @@ void Neighborhood::newInteraction(const tInteractionID interactionID) {
}

void Neighborhood::bumpIntoWall() {
// TODO
warning("bump");
_vm->_gfx->shakeTheWorld(15, 30);
}

void Neighborhood::zoomUpOrBump() {
Expand Down

0 comments on commit 0dada6e

Please sign in to comment.