Initial commit
This commit is contained in:
165
engines/ags/engine/ac/walk_behind.cpp
Normal file
165
engines/ags/engine/ac/walk_behind.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/std/algorithm.h"
|
||||
#include "ags/engine/ac/walk_behind.h"
|
||||
#include "ags/shared/ac/common.h"
|
||||
#include "ags/shared/ac/common_defines.h"
|
||||
#include "ags/engine/ac/room_status.h"
|
||||
#include "ags/engine/ac/game_state.h"
|
||||
#include "ags/engine/gfx/graphics_driver.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
using namespace AGS::Engine;
|
||||
|
||||
// Generates walk-behinds as separate sprites
|
||||
void walkbehinds_generate_sprites() {
|
||||
const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
|
||||
const Bitmap *bg = _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic.get();
|
||||
|
||||
const int coldepth = bg->GetColorDepth();
|
||||
Bitmap wbbmp; // temp buffer
|
||||
// Iterate through walk-behinds and generate a texture for each of them
|
||||
for (int wb = 1 /* 0 is "no area" */; wb < MAX_WALK_BEHINDS; ++wb) {
|
||||
const Rect pos = _G(walkBehindAABB)[wb];
|
||||
if (pos.Right > 0) {
|
||||
wbbmp.CreateTransparent(pos.GetWidth(), pos.GetHeight(), coldepth);
|
||||
// Copy over all solid pixels belonging to this WB area
|
||||
const int sx = pos.Left, ex = pos.Right, sy = pos.Top, ey = pos.Bottom;
|
||||
for (int y = sy; y <= ey; ++y) {
|
||||
const uint8_t *check_line = mask->GetScanLine(y);
|
||||
const uint8_t *src_line = bg->GetScanLine(y);
|
||||
uint8_t *dst_line = wbbmp.GetScanLineForWriting(y - sy);
|
||||
for (int x = sx; x <= ex; ++x) {
|
||||
if (check_line[x] != wb) continue;
|
||||
switch (coldepth) {
|
||||
case 8:
|
||||
dst_line[(x - sx)] = src_line[x];
|
||||
break;
|
||||
case 16:
|
||||
reinterpret_cast<uint16_t *>(dst_line)[(x - sx)] =
|
||||
reinterpret_cast<const uint16_t *>(src_line)[x];
|
||||
break;
|
||||
case 32:
|
||||
reinterpret_cast<uint32_t *>(dst_line)[(x - sx)] =
|
||||
reinterpret_cast<const uint32_t *>(src_line)[x];
|
||||
break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add to walk-behinds image list
|
||||
add_walkbehind_image(wb, &wbbmp, pos.Left, pos.Top);
|
||||
}
|
||||
}
|
||||
|
||||
_G(walkBehindsCachedForBgNum) = _GP(play).bg_frame;
|
||||
}
|
||||
|
||||
// Edits the given game object's sprite, cutting out pixels covered by walk-behinds;
|
||||
// returns whether any pixels were updated;
|
||||
bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel) {
|
||||
if (_G(noWalkBehindsAtAll))
|
||||
return false;
|
||||
|
||||
const int maskcol = sprit->GetMaskColor();
|
||||
const int spcoldep = sprit->GetColorDepth();
|
||||
|
||||
bool pixels_changed = false;
|
||||
// pass along the sprite's pixels, but skip those that lie outside the mask
|
||||
for (int x = MAX(0, 0 - sprx);
|
||||
(x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
|
||||
// select the WB column at this x
|
||||
const auto &wbcol = _G(walkBehindCols)[x + sprx];
|
||||
// skip if no area, or sprite lies outside of all areas in this column
|
||||
if ((!wbcol.Exists) ||
|
||||
(wbcol.Y2 <= spry) ||
|
||||
(wbcol.Y1 > spry + sprit->GetHeight()))
|
||||
continue;
|
||||
|
||||
// ensure we only check within the valid areas (between Y1 and Y2)
|
||||
// we assume that Y1 and Y2 are always within the mask
|
||||
for (int y = MAX(0, wbcol.Y1 - spry);
|
||||
(y < sprit->GetHeight()) && (y + spry < wbcol.Y2); ++y) {
|
||||
const int wb = _GP(thisroom).WalkBehindMask->GetScanLine(y + spry)[x + sprx];
|
||||
if (wb < 1) continue; // "no area"
|
||||
if (_G(croom)->walkbehind_base[wb] <= basel) continue;
|
||||
|
||||
pixels_changed = true;
|
||||
uint8_t *dst_line = sprit->GetScanLineForWriting(y);
|
||||
switch (spcoldep) {
|
||||
case 8:
|
||||
dst_line[x] = maskcol;
|
||||
break;
|
||||
case 16:
|
||||
reinterpret_cast<uint16_t *>(dst_line)[x] = maskcol;
|
||||
break;
|
||||
case 32:
|
||||
reinterpret_cast<uint32_t *>(dst_line)[x] = maskcol;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pixels_changed;
|
||||
}
|
||||
|
||||
void walkbehinds_recalc() {
|
||||
// Reset all data
|
||||
_G(walkBehindCols).clear();
|
||||
for (int wb = 0; wb < MAX_WALK_BEHINDS; ++wb) {
|
||||
_G(walkBehindAABB)[wb] = Rect(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
|
||||
}
|
||||
_G(noWalkBehindsAtAll) = true;
|
||||
|
||||
// Recalculate everything; note that mask is always 8-bit
|
||||
const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
|
||||
_G(walkBehindCols).resize(mask->GetWidth());
|
||||
for (int col = 0; col < mask->GetWidth(); ++col) {
|
||||
auto &wbcol = _G(walkBehindCols)[col];
|
||||
for (int y = 0; y < mask->GetHeight(); ++y) {
|
||||
int wb = mask->GetScanLine(y)[col];
|
||||
// Valid areas start with index 1, 0 = no area
|
||||
if ((wb >= 1) && (wb < MAX_WALK_BEHINDS)) {
|
||||
if (!wbcol.Exists) {
|
||||
wbcol.Y1 = y;
|
||||
wbcol.Exists = true;
|
||||
_G(noWalkBehindsAtAll) = false;
|
||||
}
|
||||
wbcol.Y2 = y + 1; // +1 to allow bottom line of screen to work (CHECKME??)
|
||||
// resize the bounding rect
|
||||
_G(walkBehindAABB)[wb].Left = MIN(col, _G(walkBehindAABB)[wb].Left);
|
||||
_G(walkBehindAABB)[wb].Top = MIN(y, _G(walkBehindAABB)[wb].Top);
|
||||
_G(walkBehindAABB)[wb].Right = MAX(col, _G(walkBehindAABB)[wb].Right);
|
||||
_G(walkBehindAABB)[wb].Bottom = MAX(y, _G(walkBehindAABB)[wb].Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
_G(walkBehindsCachedForBgNum) = -1;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
Reference in New Issue
Block a user