2010-02-02 16:25:35 +00:00
/* 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 2
* of the License , or ( at your option ) any later version .
2014-02-18 02:34:24 +01:00
*
2010-02-02 16:25:35 +00:00
* 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 .
2014-02-18 02:34:24 +01:00
*
2010-02-02 16:25:35 +00:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2011-04-24 11:34:27 +03:00
# include "common/algorithm.h"
2016-02-21 17:21:48 +01:00
# include "common/config-manager.h"
2011-04-24 11:34:27 +03:00
# include "common/events.h"
# include "common/keyboard.h"
2016-01-02 15:32:38 +02:00
# include "common/list.h"
2011-04-24 11:34:27 +03:00
# include "common/str.h"
# include "common/system.h"
# include "common/textconsole.h"
# include "engines/engine.h"
2016-07-28 14:49:13 -05:00
# include "engines/util.h"
2012-08-16 13:30:32 -04:00
# include "graphics/palette.h"
2011-04-24 11:34:27 +03:00
# include "graphics/surface.h"
2010-02-02 16:25:35 +00:00
# include "sci/sci.h"
2012-05-21 01:29:30 +03:00
# include "sci/console.h"
2017-01-09 22:57:41 -06:00
# include "sci/event.h"
2016-11-27 10:53:06 -06:00
# include "sci/engine/features.h"
2010-06-23 15:23:37 +00:00
# include "sci/engine/kernel.h"
2010-02-02 16:25:35 +00:00
# include "sci/engine/state.h"
# include "sci/engine/selector.h"
# include "sci/engine/vm.h"
# include "sci/graphics/cache.h"
2011-10-19 20:27:43 +03:00
# include "sci/graphics/compare.h"
2016-07-31 13:41:05 -05:00
# include "sci/graphics/cursor32.h"
2010-02-03 01:36:53 +00:00
# include "sci/graphics/font.h"
2016-07-03 17:57:58 -05:00
# include "sci/graphics/frameout.h"
2010-02-04 17:57:44 +00:00
# include "sci/graphics/paint32.h"
2016-01-06 20:00:28 -06:00
# include "sci/graphics/palette32.h"
2016-01-18 00:12:47 -06:00
# include "sci/graphics/plane32.h"
2016-06-25 14:19:47 -05:00
# include "sci/graphics/remap32.h"
2016-07-03 17:57:58 -05:00
# include "sci/graphics/screen.h"
2016-01-18 00:12:47 -06:00
# include "sci/graphics/screen_item32.h"
2016-06-25 14:19:47 -05:00
# include "sci/graphics/text32.h"
2010-02-02 16:25:35 +00:00
# include "sci/graphics/frameout.h"
2016-07-25 11:06:27 -05:00
# include "sci/graphics/transitions32.h"
2016-07-03 17:57:58 -05:00
# include "sci/graphics/video32.h"
2010-02-02 16:25:35 +00:00
namespace Sci {
2016-08-19 11:53:04 -05:00
GfxFrameout : : GfxFrameout ( SegManager * segMan , GfxPalette32 * palette , GfxTransitions32 * transitions , GfxCursor32 * cursor ) :
2016-09-09 16:17:41 -05:00
_isHiRes ( gameIsHiRes ( ) ) ,
2016-01-18 00:12:47 -06:00
_palette ( palette ) ,
2016-07-31 13:41:05 -05:00
_cursor ( cursor ) ,
2016-02-18 21:09:15 -06:00
_segMan ( segMan ) ,
2016-07-25 11:06:27 -05:00
_transitions ( transitions ) ,
2016-07-01 19:47:15 -05:00
_throttleState ( 0 ) ,
2016-02-18 21:09:15 -06:00
_remapOccurred ( false ) ,
_overdrawThreshold ( 0 ) ,
2017-05-22 18:40:52 -05:00
_palMorphIsOn ( false ) ,
_lastScreenUpdateTick ( 0 ) {
2016-01-18 00:12:47 -06:00
2016-07-28 14:49:13 -05:00
if ( g_sci - > getGameId ( ) = = GID_PHANTASMAGORIA ) {
_currentBuffer = Buffer ( 630 , 450 , nullptr ) ;
} else if ( _isHiRes ) {
_currentBuffer = Buffer ( 640 , 480 , nullptr ) ;
} else {
_currentBuffer = Buffer ( 320 , 200 , nullptr ) ;
}
_currentBuffer . setPixels ( calloc ( 1 , _currentBuffer . screenWidth * _currentBuffer . screenHeight ) ) ;
_screenRect = Common : : Rect ( _currentBuffer . screenWidth , _currentBuffer . screenHeight ) ;
initGraphics ( _currentBuffer . screenWidth , _currentBuffer . screenHeight , _isHiRes ) ;
2016-03-18 18:49:10 -05:00
switch ( g_sci - > getGameId ( ) ) {
2016-07-11 13:02:05 -05:00
case GID_HOYLE5 :
2016-03-18 18:49:10 -05:00
case GID_LIGHTHOUSE :
case GID_LSL7 :
case GID_PHANTASMAGORIA2 :
case GID_TORIN :
case GID_RAMA :
_currentBuffer . scriptWidth = 640 ;
_currentBuffer . scriptHeight = 480 ;
break ;
2017-01-08 19:34:24 -06:00
case GID_GK2 :
2017-01-09 11:39:43 -06:00
case GID_PQSWAT :
2017-01-08 19:34:24 -06:00
if ( ! g_sci - > isDemo ( ) ) {
_currentBuffer . scriptWidth = 640 ;
_currentBuffer . scriptHeight = 480 ;
}
break ;
2016-03-18 18:49:10 -05:00
default :
// default script width for other games is 320x200
break ;
}
2010-02-02 16:25:35 +00:00
}
GfxFrameout : : ~ GfxFrameout ( ) {
2012-01-15 18:35:33 +02:00
clear ( ) ;
2016-03-06 16:40:23 -06:00
CelObj : : deinit ( ) ;
free ( _currentBuffer . getPixels ( ) ) ;
2010-02-02 16:25:35 +00:00
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : run ( ) {
CelObj : : init ( ) ;
Plane : : init ( ) ;
ScreenItem : : init ( ) ;
2017-01-22 19:01:02 -06:00
GfxText32 : : init ( ) ;
2016-01-18 00:12:47 -06:00
// NOTE: This happens in SCI::InitPlane in the actual engine,
// and is a background fill plane to ensure hidden planes
// (planes with a priority of -1) are never drawn
2016-02-18 00:07:28 -06:00
Plane * initPlane = new Plane ( Common : : Rect ( _currentBuffer . scriptWidth , _currentBuffer . scriptHeight ) ) ;
2016-01-18 00:12:47 -06:00
initPlane - > _priority = 0 ;
_planes . add ( initPlane ) ;
}
2011-02-14 22:38:12 -05:00
void GfxFrameout : : clear ( ) {
_planes . clear ( ) ;
2016-02-21 01:44:28 +01:00
_visiblePlanes . clear ( ) ;
2016-01-18 00:12:47 -06:00
_showList . clear ( ) ;
}
2016-09-09 16:17:41 -05:00
bool GfxFrameout : : gameIsHiRes ( ) const {
// QFG4 is always low resolution
if ( g_sci - > getGameId ( ) = = GID_QFG4 ) {
return false ;
}
2017-05-05 23:24:20 -05:00
// PQ4 DOS floppy is low resolution only
if ( g_sci - > getGameId ( ) = = GID_PQ4 & & ! g_sci - > isCD ( ) ) {
return false ;
}
2016-09-09 16:17:41 -05:00
// GK1 DOS floppy is low resolution only, but GK1 Mac floppy is high
// resolution only
if ( g_sci - > getGameId ( ) = = GID_GK1 & &
! g_sci - > isCD ( ) & &
g_sci - > getPlatform ( ) ! = Common : : kPlatformMacintosh ) {
return false ;
}
// All other games are either high resolution by default, or have a
// user-defined toggle
return ConfMan . getBool ( " enable_high_resolution_graphics " ) ;
}
2016-01-18 00:12:47 -06:00
# pragma mark -
# pragma mark Screen items
2016-07-02 19:11:46 -05:00
void GfxFrameout : : addScreenItem ( ScreenItem & screenItem ) const {
Plane * plane = _planes . findByObject ( screenItem . _plane ) ;
if ( plane = = nullptr ) {
error ( " GfxFrameout::addScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x " , PRINT_REG ( screenItem . _plane ) , PRINT_REG ( screenItem . _object ) ) ;
}
plane - > _screenItemList . add ( & screenItem ) ;
}
void GfxFrameout : : updateScreenItem ( ScreenItem & screenItem ) const {
// TODO: In SCI3+ this will need to go through Plane
// Plane *plane = _planes.findByObject(screenItem._plane);
// if (plane == nullptr) {
// error("GfxFrameout::updateScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object));
// }
screenItem . update ( ) ;
}
void GfxFrameout : : deleteScreenItem ( ScreenItem & screenItem ) {
Plane * plane = _planes . findByObject ( screenItem . _plane ) ;
if ( plane = = nullptr ) {
error ( " GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x " , PRINT_REG ( screenItem . _plane ) , PRINT_REG ( screenItem . _object ) ) ;
}
if ( plane - > _screenItemList . findByObject ( screenItem . _object ) = = nullptr ) {
error ( " GfxFrameout::deleteScreenItem: Screen item %04x:%04x not found in plane %04x:%04x " , PRINT_REG ( screenItem . _object ) , PRINT_REG ( screenItem . _plane ) ) ;
}
deleteScreenItem ( screenItem , * plane ) ;
}
void GfxFrameout : : deleteScreenItem ( ScreenItem & screenItem , Plane & plane ) {
if ( screenItem . _created = = 0 ) {
screenItem . _created = 0 ;
screenItem . _updated = 0 ;
screenItem . _deleted = getScreenCount ( ) ;
2016-06-18 20:48:10 -05:00
} else {
2016-07-02 19:11:46 -05:00
plane . _screenItemList . erase ( & screenItem ) ;
plane . _screenItemList . pack ( ) ;
2016-06-18 20:48:10 -05:00
}
}
2016-07-02 19:11:46 -05:00
void GfxFrameout : : deleteScreenItem ( ScreenItem & screenItem , const reg_t planeObject ) {
2016-06-18 20:48:10 -05:00
Plane * plane = _planes . findByObject ( planeObject ) ;
2016-07-02 19:11:46 -05:00
if ( plane = = nullptr ) {
error ( " GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x " , PRINT_REG ( planeObject ) , PRINT_REG ( screenItem . _object ) ) ;
}
deleteScreenItem ( screenItem , * plane ) ;
2016-06-18 20:48:10 -05:00
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelAddScreenItem ( const reg_t object ) {
const reg_t planeObject = readSelector ( _segMan , object , SELECTOR ( plane ) ) ;
_segMan - > getObject ( object ) - > setInfoSelectorFlag ( kInfoFlagViewInserted ) ;
Plane * plane = _planes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kAddScreenItem: Plane %04x:%04x not found for screen item %04x:%04x " , PRINT_REG ( planeObject ) , PRINT_REG ( object ) ) ;
2016-01-18 00:12:47 -06:00
}
ScreenItem * screenItem = plane - > _screenItemList . findByObject ( object ) ;
if ( screenItem ! = nullptr ) {
screenItem - > update ( object ) ;
2012-06-07 11:26:32 +03:00
} else {
2016-01-18 00:12:47 -06:00
screenItem = new ScreenItem ( object ) ;
plane - > _screenItemList . add ( screenItem ) ;
2012-06-07 11:26:32 +03:00
}
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelUpdateScreenItem ( const reg_t object ) {
const reg_t magnifierObject = readSelector ( _segMan , object , SELECTOR ( magnifier ) ) ;
if ( magnifierObject . isNull ( ) ) {
const reg_t planeObject = readSelector ( _segMan , object , SELECTOR ( plane ) ) ;
Plane * plane = _planes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kUpdateScreenItem: Plane %04x:%04x not found for screen item %04x:%04x " , PRINT_REG ( planeObject ) , PRINT_REG ( object ) ) ;
2016-01-18 00:12:47 -06:00
}
2012-06-07 11:26:32 +03:00
2016-01-18 00:12:47 -06:00
ScreenItem * screenItem = plane - > _screenItemList . findByObject ( object ) ;
if ( screenItem = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kUpdateScreenItem: Screen item %04x:%04x not found in plane %04x:%04x " , PRINT_REG ( object ) , PRINT_REG ( planeObject ) ) ;
2016-01-18 00:12:47 -06:00
}
2010-09-19 14:50:28 +00:00
2016-01-18 00:12:47 -06:00
screenItem - > update ( object ) ;
} else {
2016-03-07 17:27:38 -06:00
error ( " Magnifier view is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks! " ) ;
2016-01-18 00:12:47 -06:00
}
}
2010-09-19 14:50:28 +00:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelDeleteScreenItem ( const reg_t object ) {
_segMan - > getObject ( object ) - > clearInfoSelectorFlag ( kInfoFlagViewInserted ) ;
2012-09-26 04:17:31 +02:00
2016-01-18 00:12:47 -06:00
const reg_t planeObject = readSelector ( _segMan , object , SELECTOR ( plane ) ) ;
Plane * plane = _planes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
return ;
}
2011-10-14 13:51:59 +03:00
2016-01-18 00:12:47 -06:00
ScreenItem * screenItem = plane - > _screenItemList . findByObject ( object ) ;
if ( screenItem = = nullptr ) {
return ;
}
2010-09-19 14:50:28 +00:00
2016-07-02 19:11:46 -05:00
deleteScreenItem ( * screenItem , * plane ) ;
2016-01-18 00:12:47 -06:00
}
2010-09-19 14:50:28 +00:00
2016-01-18 00:12:47 -06:00
# pragma mark -
# pragma mark Planes
2010-10-23 19:23:07 +00:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelAddPlane ( const reg_t object ) {
Plane * plane = _planes . findByObject ( object ) ;
if ( plane ! = nullptr ) {
plane - > update ( object ) ;
updatePlane ( * plane ) ;
} else {
plane = new Plane ( object ) ;
addPlane ( * plane ) ;
}
}
2010-10-23 19:23:07 +00:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelUpdatePlane ( const reg_t object ) {
Plane * plane = _planes . findByObject ( object ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kUpdatePlane: Plane %04x:%04x not found " , PRINT_REG ( object ) ) ;
2010-07-24 16:47:12 +00:00
}
2016-01-18 00:12:47 -06:00
plane - > update ( object ) ;
updatePlane ( * plane ) ;
2010-02-02 16:25:35 +00:00
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : kernelDeletePlane ( const reg_t object ) {
Plane * plane = _planes . findByObject ( object ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kDeletePlane: Plane %04x:%04x not found " , PRINT_REG ( object ) ) ;
2016-01-18 00:12:47 -06:00
}
2012-01-14 14:22:23 +02:00
2016-01-18 00:12:47 -06:00
if ( plane - > _created ) {
// NOTE: The original engine calls some `AbortPlane` function that
// just ends up doing this anyway so we skip the extra indirection
_planes . erase ( plane ) ;
} else {
plane - > _created = 0 ;
plane - > _deleted = g_sci - > _gfxFrameout - > getScreenCount ( ) ;
}
}
2010-07-25 19:56:44 +00:00
2016-03-05 23:56:38 -06:00
void GfxFrameout : : deletePlane ( Plane & planeToFind ) {
Plane * plane = _planes . findByObject ( planeToFind . _object ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " deletePlane: Plane %04x:%04x not found " , PRINT_REG ( planeToFind . _object ) ) ;
2016-03-05 23:56:38 -06:00
}
if ( plane - > _created ) {
_planes . erase ( plane ) ;
} else {
plane - > _created = 0 ;
plane - > _moved = 0 ;
plane - > _deleted = getScreenCount ( ) ;
}
}
2016-03-10 15:48:48 -06:00
void GfxFrameout : : kernelMovePlaneItems ( const reg_t object , const int16 deltaX , const int16 deltaY , const bool scrollPics ) {
Plane * plane = _planes . findByObject ( object ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kMovePlaneItems: Plane %04x:%04x not found " , PRINT_REG ( object ) ) ;
2016-03-10 15:48:48 -06:00
}
plane - > scrollScreenItems ( deltaX , deltaY , scrollPics ) ;
for ( ScreenItemList : : iterator it = plane - > _screenItemList . begin ( ) ; it ! = plane - > _screenItemList . end ( ) ; + + it ) {
ScreenItem & screenItem = * * it ;
// If object is a number, the screen item from the
// engine, not a script, and should be ignored
if ( screenItem . _object . isNumber ( ) ) {
continue ;
}
if ( deltaX ! = 0 ) {
writeSelectorValue ( _segMan , screenItem . _object , SELECTOR ( x ) , readSelectorValue ( _segMan , screenItem . _object , SELECTOR ( x ) ) + deltaX ) ;
}
if ( deltaY ! = 0 ) {
writeSelectorValue ( _segMan , screenItem . _object , SELECTOR ( y ) , readSelectorValue ( _segMan , screenItem . _object , SELECTOR ( y ) ) + deltaY ) ;
}
}
}
2016-01-18 00:12:47 -06:00
int16 GfxFrameout : : kernelGetHighPlanePri ( ) {
return _planes . getTopSciPlanePriority ( ) ;
}
2012-06-22 09:31:51 +03:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : addPlane ( Plane & plane ) {
if ( _planes . findByObject ( plane . _object ) = = nullptr ) {
plane . clipScreenRect ( _screenRect ) ;
_planes . add ( & plane ) ;
} else {
plane . _deleted = 0 ;
if ( plane . _created = = 0 ) {
plane . _moved = g_sci - > _gfxFrameout - > getScreenCount ( ) ;
2010-02-02 16:25:35 +00:00
}
2016-01-18 00:12:47 -06:00
_planes . sort ( ) ;
2010-02-02 16:25:35 +00:00
}
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : updatePlane ( Plane & plane ) {
// NOTE: This assertion comes from SCI engine code.
assert ( _planes . findByObject ( plane . _object ) = = & plane ) ;
2012-06-09 12:12:44 +03:00
2016-01-18 00:12:47 -06:00
Plane * visiblePlane = _visiblePlanes . findByObject ( plane . _object ) ;
plane . sync ( visiblePlane , _screenRect ) ;
// NOTE: updateScreenRect was originally called a second time here,
// but it is already called at the end of the Plane::Update call
// in the original engine anyway.
_planes . sort ( ) ;
2010-07-26 14:41:19 +00:00
}
2016-01-18 00:12:47 -06:00
# pragma mark -
# pragma mark Pics
2012-01-15 18:35:33 +02:00
2016-07-25 13:40:51 -05:00
void GfxFrameout : : kernelAddPicAt ( const reg_t planeObject , const GuiResourceId pictureId , const int16 x , const int16 y , const bool mirrorX , const bool deleteDuplicate ) {
2016-01-18 00:12:47 -06:00
Plane * plane = _planes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kAddPicAt: Plane %04x:%04x not found " , PRINT_REG ( planeObject ) ) ;
2010-07-26 14:41:19 +00:00
}
2016-07-25 13:40:51 -05:00
plane - > addPic ( pictureId , Common : : Point ( x , y ) , mirrorX , deleteDuplicate ) ;
2010-07-26 14:41:19 +00:00
}
2016-01-18 00:12:47 -06:00
# pragma mark -
# pragma mark Rendering
2016-06-30 13:24:46 -05:00
void GfxFrameout : : frameOut ( const bool shouldShowBits , const Common : : Rect & eraseRect ) {
2017-01-09 22:57:41 -06:00
// In SSCI, mouse events were received via hardware interrupt, so the mouse
// cursor would always get updated whenever the user moved the mouse. Since
// we must poll for mouse events instead, poll here so that the mouse gets
// updated with its current position at render time. If we do not do this,
// the mouse gets "stuck" during loops that do not make calls to kGetEvent,
2017-05-04 22:42:32 -05:00
// like transitions.
2017-01-09 22:57:41 -06:00
g_sci - > getEventManager ( ) - > getSciEvent ( SCI_EVENT_PEEK ) ;
2016-07-03 17:57:58 -05:00
RobotDecoder & robotPlayer = g_sci - > _video32 - > getRobotPlayer ( ) ;
const bool robotIsActive = robotPlayer . getStatus ( ) ! = RobotDecoder : : kRobotStatusUninitialized ;
if ( robotIsActive ) {
robotPlayer . doRobot ( ) ;
}
2016-01-18 00:12:47 -06:00
// NOTE: The original engine allocated these as static arrays of 100
// pointers to ScreenItemList / RectList
ScreenItemListList screenItemLists ;
EraseListList eraseLists ;
screenItemLists . resize ( _planes . size ( ) ) ;
eraseLists . resize ( _planes . size ( ) ) ;
2016-03-11 07:54:45 +02:00
if ( g_sci - > _gfxRemap32 - > getRemapCount ( ) > 0 & & _remapOccurred ) {
2016-03-15 21:05:01 +02:00
remapMarkRedraw ( ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:24:46 -05:00
calcLists ( screenItemLists , eraseLists , eraseRect ) ;
2016-01-18 00:12:47 -06:00
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
list - > sort ( ) ;
}
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
for ( DrawList : : iterator drawItem = list - > begin ( ) ; drawItem ! = list - > end ( ) ; + + drawItem ) {
( * drawItem ) - > screenItem - > getCelObj ( ) . submitPalette ( ) ;
2012-06-09 15:36:36 +03:00
}
}
2016-01-18 00:12:47 -06:00
_remapOccurred = _palette - > updateForFrame ( ) ;
for ( PlaneList : : size_type i = 0 ; i < _planes . size ( ) ; + + i ) {
drawEraseList ( eraseLists [ i ] , * _planes [ i ] ) ;
drawScreenItemList ( screenItemLists [ i ] ) ;
}
2016-07-03 17:57:58 -05:00
if ( robotIsActive ) {
robotPlayer . frameAlmostVisible ( ) ;
}
2016-01-18 00:12:47 -06:00
2017-05-06 00:00:04 -05:00
_palette - > updateHardware ( ) ;
2016-01-18 00:12:47 -06:00
if ( shouldShowBits ) {
showBits ( ) ;
}
2016-07-03 17:57:58 -05:00
if ( robotIsActive ) {
robotPlayer . frameNowVisible ( ) ;
}
2012-06-09 15:36:36 +03:00
}
2016-07-25 11:06:27 -05:00
void GfxFrameout : : palMorphFrameOut ( const int8 * styleRanges , PlaneShowStyle * showStyle ) {
Palette sourcePalette ( _palette - > getNextPalette ( ) ) ;
alterVmap ( sourcePalette , sourcePalette , - 1 , styleRanges ) ;
2016-09-26 19:28:51 -05:00
int16 prevRoom = g_sci - > getEngineState ( ) - > variables [ VAR_GLOBAL ] [ kGlobalVarPreviousRoomNo ] . toSint16 ( ) ;
2016-07-25 11:06:27 -05:00
2016-07-28 14:49:13 -05:00
Common : : Rect rect ( _currentBuffer . screenWidth , _currentBuffer . screenHeight ) ;
2016-07-25 11:06:27 -05:00
_showList . add ( rect ) ;
showBits ( ) ;
// NOTE: The original engine allocated these as static arrays of 100
// pointers to ScreenItemList / RectList
ScreenItemListList screenItemLists ;
EraseListList eraseLists ;
screenItemLists . resize ( _planes . size ( ) ) ;
eraseLists . resize ( _planes . size ( ) ) ;
if ( g_sci - > _gfxRemap32 - > getRemapCount ( ) > 0 & & _remapOccurred ) {
remapMarkRedraw ( ) ;
}
calcLists ( screenItemLists , eraseLists ) ;
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
list - > sort ( ) ;
}
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
for ( DrawList : : iterator drawItem = list - > begin ( ) ; drawItem ! = list - > end ( ) ; + + drawItem ) {
( * drawItem ) - > screenItem - > getCelObj ( ) . submitPalette ( ) ;
}
}
_remapOccurred = _palette - > updateForFrame ( ) ;
for ( PlaneList : : size_type i = 0 ; i < _planes . size ( ) ; + + i ) {
drawEraseList ( eraseLists [ i ] , * _planes [ i ] ) ;
drawScreenItemList ( screenItemLists [ i ] ) ;
}
Palette nextPalette ( _palette - > getNextPalette ( ) ) ;
if ( prevRoom < 1000 ) {
for ( int i = 0 ; i < ARRAYSIZE ( sourcePalette . colors ) ; + + i ) {
if ( styleRanges [ i ] = = - 1 | | styleRanges [ i ] = = 0 ) {
sourcePalette . colors [ i ] = nextPalette . colors [ i ] ;
sourcePalette . colors [ i ] . used = true ;
}
}
} else {
for ( int i = 0 ; i < ARRAYSIZE ( sourcePalette . colors ) ; + + i ) {
if ( styleRanges [ i ] = = - 1 | | validZeroStyle ( styleRanges [ i ] , i ) ) {
sourcePalette . colors [ i ] = nextPalette . colors [ i ] ;
sourcePalette . colors [ i ] . used = true ;
}
}
}
_palette - > submit ( sourcePalette ) ;
_palette - > updateFFrame ( ) ;
_palette - > updateHardware ( ) ;
alterVmap ( nextPalette , sourcePalette , 1 , _transitions - > _styleRanges ) ;
if ( showStyle & & showStyle - > type ! = kShowStyleMorph ) {
_transitions - > processEffects ( * showStyle ) ;
} else {
showBits ( ) ;
}
for ( PlaneList : : iterator plane = _planes . begin ( ) ; plane ! = _planes . end ( ) ; + + plane ) {
( * plane ) - > _redrawAllCount = getScreenCount ( ) ;
}
if ( g_sci - > _gfxRemap32 - > getRemapCount ( ) > 0 & & _remapOccurred ) {
remapMarkRedraw ( ) ;
}
calcLists ( screenItemLists , eraseLists ) ;
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
list - > sort ( ) ;
}
for ( ScreenItemListList : : iterator list = screenItemLists . begin ( ) ; list ! = screenItemLists . end ( ) ; + + list ) {
for ( DrawList : : iterator drawItem = list - > begin ( ) ; drawItem ! = list - > end ( ) ; + + drawItem ) {
( * drawItem ) - > screenItem - > getCelObj ( ) . submitPalette ( ) ;
}
}
_remapOccurred = _palette - > updateForFrame ( ) ;
for ( PlaneList : : size_type i = 0 ; i < _planes . size ( ) ; + + i ) {
drawEraseList ( eraseLists [ i ] , * _planes [ i ] ) ;
drawScreenItemList ( screenItemLists [ i ] ) ;
}
_palette - > submit ( nextPalette ) ;
_palette - > updateFFrame ( ) ;
2017-05-06 00:00:04 -05:00
_palette - > updateHardware ( ) ;
2016-07-25 11:06:27 -05:00
showBits ( ) ;
}
2016-06-30 13:50:10 -05:00
/**
* Determines the parts of ` r ` that aren ' t overlapped by ` other ` .
* Returns - 1 if ` r ` and ` other ` have no intersection .
* Returns number of returned parts ( in ` outRects ` ) otherwise .
* ( In particular , this returns 0 if ` r ` is contained in ` other ` . )
*/
2016-01-18 00:12:47 -06:00
int splitRects ( Common : : Rect r , const Common : : Rect & other , Common : : Rect ( & outRects ) [ 4 ] ) {
if ( ! r . intersects ( other ) ) {
return - 1 ;
}
2016-06-30 13:50:10 -05:00
int splitCount = 0 ;
2016-01-18 00:12:47 -06:00
if ( r . top < other . top ) {
2016-06-30 13:50:10 -05:00
Common : : Rect & t = outRects [ splitCount + + ] ;
2016-01-18 00:12:47 -06:00
t = r ;
t . bottom = other . top ;
r . top = other . top ;
}
if ( r . bottom > other . bottom ) {
2016-06-30 13:50:10 -05:00
Common : : Rect & t = outRects [ splitCount + + ] ;
2016-01-18 00:12:47 -06:00
t = r ;
t . top = other . bottom ;
r . bottom = other . bottom ;
}
if ( r . left < other . left ) {
2016-06-30 13:50:10 -05:00
Common : : Rect & t = outRects [ splitCount + + ] ;
2016-01-18 00:12:47 -06:00
t = r ;
t . right = other . left ;
r . left = other . left ;
}
if ( r . right > other . right ) {
2016-06-30 13:50:10 -05:00
Common : : Rect & t = outRects [ splitCount + + ] ;
2016-01-18 00:12:47 -06:00
t = r ;
t . left = other . right ;
}
2016-06-30 13:50:10 -05:00
return splitCount ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
/**
* Determines the parts of ` middleRect ` that aren ' t overlapped
* by ` showRect ` , optimised for contiguous memory writes .
* Returns - 1 if ` middleRect ` and ` showRect ` have no intersection .
* Returns number of returned parts ( in ` outRects ` ) otherwise .
* ( In particular , this returns 0 if ` middleRect ` is contained
* in ` other ` . )
*
* ` middleRect ` is modified directly to extend into the upper
* and lower rects .
*/
int splitRectsForRender ( Common : : Rect & middleRect , const Common : : Rect & showRect , Common : : Rect ( & outRects ) [ 2 ] ) {
if ( ! middleRect . intersects ( showRect ) ) {
return - 1 ;
}
const int16 minLeft = MIN ( middleRect . left , showRect . left ) ;
const int16 maxRight = MAX ( middleRect . right , showRect . right ) ;
int16 upperLeft , upperTop , upperRight , upperMaxTop ;
if ( middleRect . top < showRect . top ) {
upperLeft = middleRect . left ;
upperTop = middleRect . top ;
upperRight = middleRect . right ;
upperMaxTop = showRect . top ;
}
else {
upperLeft = showRect . left ;
upperTop = showRect . top ;
upperRight = showRect . right ;
upperMaxTop = middleRect . top ;
}
int16 lowerLeft , lowerRight , lowerBottom , lowerMinBottom ;
if ( middleRect . bottom > showRect . bottom ) {
lowerLeft = middleRect . left ;
lowerRight = middleRect . right ;
lowerBottom = middleRect . bottom ;
lowerMinBottom = showRect . bottom ;
} else {
lowerLeft = showRect . left ;
lowerRight = showRect . right ;
lowerBottom = showRect . bottom ;
lowerMinBottom = middleRect . bottom ;
}
int splitCount = 0 ;
middleRect . left = minLeft ;
middleRect . top = upperMaxTop ;
middleRect . right = maxRight ;
middleRect . bottom = lowerMinBottom ;
if ( upperTop ! = upperMaxTop ) {
Common : : Rect & upperRect = outRects [ 0 ] ;
upperRect . left = upperLeft ;
upperRect . top = upperTop ;
upperRect . right = upperRight ;
upperRect . bottom = upperMaxTop ;
// Merge upper rect into middle rect if possible
if ( upperRect . left = = middleRect . left & & upperRect . right = = middleRect . right ) {
middleRect . top = upperRect . top ;
} else {
+ + splitCount ;
}
}
if ( lowerBottom ! = lowerMinBottom ) {
Common : : Rect & lowerRect = outRects [ splitCount ] ;
lowerRect . left = lowerLeft ;
lowerRect . top = lowerMinBottom ;
lowerRect . right = lowerRight ;
lowerRect . bottom = lowerBottom ;
// Merge lower rect into middle rect if possible
if ( lowerRect . left = = middleRect . left & & lowerRect . right = = middleRect . right ) {
middleRect . bottom = lowerRect . bottom ;
} else {
+ + splitCount ;
}
}
assert ( splitCount < = 2 ) ;
return splitCount ;
}
2016-01-18 00:12:47 -06:00
2016-06-30 13:24:46 -05:00
// NOTE: The third rectangle parameter is only ever given a non-empty rect
// by VMD code, via `frameOut`
void GfxFrameout : : calcLists ( ScreenItemListList & drawLists , EraseListList & eraseLists , const Common : : Rect & eraseRect ) {
2016-06-30 13:50:10 -05:00
RectList eraseList ;
Common : : Rect outRects [ 4 ] ;
2016-01-18 00:12:47 -06:00
int deletedPlaneCount = 0 ;
2016-06-30 13:50:10 -05:00
bool addedToEraseList = false ;
2016-01-18 00:12:47 -06:00
bool foundTransparentPlane = false ;
2016-06-30 13:24:46 -05:00
if ( ! eraseRect . isEmpty ( ) ) {
addedToEraseList = true ;
2016-06-30 13:50:10 -05:00
eraseList . add ( eraseRect ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
PlaneList : : size_type planeCount = _planes . size ( ) ;
2016-07-01 15:54:52 -05:00
for ( PlaneList : : size_type outerPlaneIndex = 0 ; outerPlaneIndex < planeCount ; + + outerPlaneIndex ) {
2016-06-30 13:50:10 -05:00
const Plane * outerPlane = _planes [ outerPlaneIndex ] ;
const Plane * visiblePlane = _visiblePlanes . findByObject ( outerPlane - > _object ) ;
2016-01-18 00:12:47 -06:00
2016-06-27 20:58:22 -05:00
// NOTE: SSCI only ever checks for kPlaneTypeTransparent here, even
// though kPlaneTypeTransparentPicture is also a transparent plane
2016-01-18 00:12:47 -06:00
if ( outerPlane - > _type = = kPlaneTypeTransparent ) {
foundTransparentPlane = true ;
}
2012-07-05 13:42:00 +03:00
2016-01-18 00:12:47 -06:00
if ( outerPlane - > _deleted ) {
2016-06-30 13:50:10 -05:00
if ( visiblePlane ! = nullptr & & ! visiblePlane - > _screenRect . isEmpty ( ) ) {
eraseList . add ( visiblePlane - > _screenRect ) ;
addedToEraseList = true ;
2016-01-18 00:12:47 -06:00
}
+ + deletedPlaneCount ;
2016-06-30 13:50:10 -05:00
} else if ( visiblePlane ! = nullptr & & outerPlane - > _moved ) {
// _moved will be decremented in the final loop through the planes,
// at the end of this function
{
const int splitCount = splitRects ( visiblePlane - > _screenRect , outerPlane - > _screenRect , outRects ) ;
if ( splitCount ) {
if ( splitCount = = - 1 & & ! visiblePlane - > _screenRect . isEmpty ( ) ) {
eraseList . add ( visiblePlane - > _screenRect ) ;
2016-01-18 00:12:47 -06:00
} else {
2016-06-30 13:50:10 -05:00
for ( int i = 0 ; i < splitCount ; + + i ) {
eraseList . add ( outRects [ i ] ) ;
2016-01-18 00:12:47 -06:00
}
}
2016-06-30 13:50:10 -05:00
addedToEraseList = true ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
}
2016-01-18 00:12:47 -06:00
2016-06-30 13:50:10 -05:00
if ( ! outerPlane - > _redrawAllCount ) {
const int splitCount = splitRects ( outerPlane - > _screenRect , visiblePlane - > _screenRect , outRects ) ;
if ( splitCount ) {
for ( int i = 0 ; i < splitCount ; + + i ) {
eraseList . add ( outRects [ i ] ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
addedToEraseList = true ;
2012-06-09 15:36:36 +03:00
}
}
}
2016-06-30 13:50:10 -05:00
if ( addedToEraseList ) {
2016-07-01 15:54:52 -05:00
for ( RectList : : size_type rectIndex = 0 ; rectIndex < eraseList . size ( ) ; + + rectIndex ) {
2016-06-30 13:50:10 -05:00
const Common : : Rect & rect = * eraseList [ rectIndex ] ;
for ( int innerPlaneIndex = planeCount - 1 ; innerPlaneIndex > = 0 ; - - innerPlaneIndex ) {
const Plane & innerPlane = * _planes [ innerPlaneIndex ] ;
if (
! innerPlane . _deleted & &
innerPlane . _type ! = kPlaneTypeTransparent & &
innerPlane . _screenRect . intersects ( rect )
) {
if ( ! innerPlane . _redrawAllCount ) {
eraseLists [ innerPlaneIndex ] . add ( innerPlane . _screenRect . findIntersectingRect ( rect ) ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
const int splitCount = splitRects ( rect , innerPlane . _screenRect , outRects ) ;
2016-01-18 00:12:47 -06:00
for ( int i = 0 ; i < splitCount ; + + i ) {
2016-06-30 13:50:10 -05:00
eraseList . add ( outRects [ i ] ) ;
2016-01-18 00:12:47 -06:00
}
2012-07-05 13:42:00 +03:00
2016-06-30 13:50:10 -05:00
eraseList . erase_at ( rectIndex ) ;
2016-01-18 00:12:47 -06:00
break ;
}
2012-06-09 15:36:36 +03:00
}
}
2016-01-18 00:12:47 -06:00
2016-06-30 13:50:10 -05:00
eraseList . pack ( ) ;
2012-06-09 15:36:36 +03:00
}
}
2016-01-18 00:12:47 -06:00
// clean up deleted planes
if ( deletedPlaneCount ) {
for ( int planeIndex = planeCount - 1 ; planeIndex > = 0 ; - - planeIndex ) {
Plane * plane = _planes [ planeIndex ] ;
if ( plane - > _deleted ) {
- - plane - > _deleted ;
if ( plane - > _deleted < = 0 ) {
2016-06-30 13:50:10 -05:00
const int visiblePlaneIndex = _visiblePlanes . findIndexByObject ( plane - > _object ) ;
if ( visiblePlaneIndex ! = - 1 ) {
_visiblePlanes . remove_at ( visiblePlaneIndex ) ;
2016-01-18 00:12:47 -06:00
}
2013-01-11 00:45:10 +02:00
2016-01-18 00:12:47 -06:00
_planes . remove_at ( planeIndex ) ;
eraseLists . remove_at ( planeIndex ) ;
drawLists . remove_at ( planeIndex ) ;
}
2013-01-11 00:45:10 +02:00
2016-01-18 00:12:47 -06:00
if ( - - deletedPlaneCount < = 0 ) {
break ;
}
}
}
}
2013-01-11 00:45:10 +02:00
2016-06-30 13:50:10 -05:00
// Some planes may have been deleted, so re-retrieve count
2016-01-18 00:12:47 -06:00
planeCount = _planes . size ( ) ;
2016-06-30 13:50:10 -05:00
for ( PlaneList : : size_type outerIndex = 0 ; outerIndex < planeCount ; + + outerIndex ) {
2016-01-18 00:12:47 -06:00
// "outer" just refers to the outer loop
2016-06-30 13:50:10 -05:00
Plane & outerPlane = * _planes [ outerIndex ] ;
if ( outerPlane . _priorityChanged ) {
- - outerPlane . _priorityChanged ;
2016-01-18 00:12:47 -06:00
2016-06-30 13:50:10 -05:00
const Plane * visibleOuterPlane = _visiblePlanes . findByObject ( outerPlane . _object ) ;
2016-05-30 08:33:38 -05:00
if ( visibleOuterPlane = = nullptr ) {
2016-06-30 13:50:10 -05:00
warning ( " calcLists could not find visible plane for %04x:%04x " , PRINT_REG ( outerPlane . _object ) ) ;
2016-05-30 08:33:38 -05:00
continue ;
}
2016-01-18 00:12:47 -06:00
2016-06-30 13:50:10 -05:00
eraseList . add ( outerPlane . _screenRect . findIntersectingRect ( visibleOuterPlane - > _screenRect ) ) ;
2016-01-18 00:12:47 -06:00
2016-07-01 15:54:52 -05:00
for ( int innerIndex = ( int ) planeCount - 1 ; innerIndex > = 0 ; - - innerIndex ) {
2016-01-18 00:12:47 -06:00
// "inner" just refers to the inner loop
2016-06-30 13:50:10 -05:00
const Plane & innerPlane = * _planes [ innerIndex ] ;
const Plane * visibleInnerPlane = _visiblePlanes . findByObject ( innerPlane . _object ) ;
2016-01-18 00:12:47 -06:00
2016-06-30 13:50:10 -05:00
const RectList : : size_type rectCount = eraseList . size ( ) ;
for ( RectList : : size_type rectIndex = 0 ; rectIndex < rectCount ; + + rectIndex ) {
const int splitCount = splitRects ( * eraseList [ rectIndex ] , innerPlane . _screenRect , outRects ) ;
2016-01-18 00:12:47 -06:00
if ( splitCount = = 0 ) {
2016-05-30 08:33:38 -05:00
if ( visibleInnerPlane ! = nullptr ) {
2016-01-18 00:12:47 -06:00
// same priority, or relative priority between inner/outer changed
2016-06-30 13:50:10 -05:00
if ( ( visibleOuterPlane - > _priority - visibleInnerPlane - > _priority ) * ( outerPlane . _priority - innerPlane . _priority ) < = 0 ) {
if ( outerPlane . _priority < = innerPlane . _priority ) {
eraseLists [ innerIndex ] . add ( * eraseList [ rectIndex ] ) ;
2016-01-18 00:12:47 -06:00
} else {
2016-06-30 13:50:10 -05:00
eraseLists [ outerIndex ] . add ( * eraseList [ rectIndex ] ) ;
2016-01-18 00:12:47 -06:00
}
}
}
2016-06-30 13:50:10 -05:00
eraseList . erase_at ( rectIndex ) ;
2016-01-18 00:12:47 -06:00
} else if ( splitCount ! = - 1 ) {
for ( int i = 0 ; i < splitCount ; + + i ) {
2016-06-30 13:50:10 -05:00
eraseList . add ( outRects [ i ] ) ;
2016-01-18 00:12:47 -06:00
}
2016-05-30 08:33:38 -05:00
if ( visibleInnerPlane ! = nullptr ) {
2016-01-18 00:12:47 -06:00
// same priority, or relative priority between inner/outer changed
2016-06-30 13:50:10 -05:00
if ( ( visibleOuterPlane - > _priority - visibleInnerPlane - > _priority ) * ( outerPlane . _priority - innerPlane . _priority ) < = 0 ) {
* eraseList [ rectIndex ] = outerPlane . _screenRect . findIntersectingRect ( innerPlane . _screenRect ) ;
if ( outerPlane . _priority < = innerPlane . _priority ) {
eraseLists [ innerIndex ] . add ( * eraseList [ rectIndex ] ) ;
} else {
eraseLists [ outerIndex ] . add ( * eraseList [ rectIndex ] ) ;
2016-01-18 00:12:47 -06:00
}
}
}
2016-06-30 13:50:10 -05:00
eraseList . erase_at ( rectIndex ) ;
2016-01-18 00:12:47 -06:00
}
}
2016-06-30 13:50:10 -05:00
eraseList . pack ( ) ;
2016-01-18 00:12:47 -06:00
}
}
}
2013-01-11 00:45:10 +02:00
2016-06-30 13:50:10 -05:00
for ( PlaneList : : size_type planeIndex = 0 ; planeIndex < planeCount ; + + planeIndex ) {
Plane & plane = * _planes [ planeIndex ] ;
Plane * visiblePlane = _visiblePlanes . findByObject ( plane . _object ) ;
2013-01-11 00:45:10 +02:00
2016-06-30 13:50:10 -05:00
if ( ! plane . _screenRect . isEmpty ( ) ) {
if ( plane . _redrawAllCount ) {
plane . redrawAll ( visiblePlane , _planes , drawLists [ planeIndex ] , eraseLists [ planeIndex ] ) ;
} else {
if ( visiblePlane = = nullptr ) {
error ( " Missing visible plane for source plane %04x:%04x " , PRINT_REG ( plane . _object ) ) ;
}
2013-01-11 00:45:10 +02:00
2016-06-30 13:50:10 -05:00
plane . calcLists ( * visiblePlane , _planes , drawLists [ planeIndex ] , eraseLists [ planeIndex ] ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
} else {
plane . decrementScreenItemArrayCounts ( visiblePlane , false ) ;
}
2013-01-11 00:45:10 +02:00
2016-06-30 13:50:10 -05:00
if ( plane . _moved ) {
// the work for handling moved/resized planes was already done
// earlier in the function, we are just cleaning up now
- - plane . _moved ;
2016-01-18 00:12:47 -06:00
}
2016-06-30 13:50:10 -05:00
if ( plane . _created ) {
_visiblePlanes . add ( new Plane ( plane ) ) ;
- - plane . _created ;
} else if ( plane . _updated ) {
2016-10-10 09:27:49 -05:00
if ( visiblePlane = = nullptr ) {
error ( " [GfxFrameout::calcLists]: Attempt to update nonexistent visible plane " ) ;
}
2016-06-30 13:50:10 -05:00
* visiblePlane = plane ;
- - plane . _updated ;
2016-01-18 00:12:47 -06:00
}
2012-07-04 02:17:27 +03:00
}
2010-10-23 19:23:07 +00:00
2016-06-27 20:58:22 -05:00
// NOTE: SSCI only looks for kPlaneTypeTransparent, not
// kPlaneTypeTransparentPicture
2016-01-18 00:12:47 -06:00
if ( foundTransparentPlane ) {
2016-06-30 13:50:10 -05:00
for ( PlaneList : : size_type planeIndex = 0 ; planeIndex < planeCount ; + + planeIndex ) {
for ( PlaneList : : size_type i = planeIndex + 1 ; i < planeCount ; + + i ) {
2016-01-18 00:12:47 -06:00
if ( _planes [ i ] - > _type = = kPlaneTypeTransparent ) {
_planes [ i ] - > filterUpEraseRects ( drawLists [ i ] , eraseLists [ planeIndex ] ) ;
}
}
if ( _planes [ planeIndex ] - > _type = = kPlaneTypeTransparent ) {
2016-07-01 15:54:52 -05:00
for ( int i = ( int ) planeIndex - 1 ; i > = 0 ; - - i ) {
2016-01-18 00:12:47 -06:00
_planes [ i ] - > filterDownEraseRects ( drawLists [ i ] , eraseLists [ i ] , eraseLists [ planeIndex ] ) ;
}
if ( eraseLists [ planeIndex ] . size ( ) > 0 ) {
error ( " Transparent plane's erase list not absorbed " ) ;
}
}
2010-10-23 19:23:07 +00:00
2016-06-30 13:50:10 -05:00
for ( PlaneList : : size_type i = planeIndex + 1 ; i < planeCount ; + + i ) {
2016-01-18 00:12:47 -06:00
if ( _planes [ i ] - > _type = = kPlaneTypeTransparent ) {
_planes [ i ] - > filterUpDrawRects ( drawLists [ i ] , drawLists [ planeIndex ] ) ;
}
}
}
}
2010-02-02 16:25:35 +00:00
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : drawEraseList ( const RectList & eraseList , const Plane & plane ) {
if ( plane . _type ! = kPlaneTypeColored ) {
2010-10-23 19:23:07 +00:00
return ;
2012-07-04 02:17:27 +03:00
}
2010-10-23 19:23:07 +00:00
2016-06-30 13:50:10 -05:00
const RectList : : size_type eraseListSize = eraseList . size ( ) ;
for ( RectList : : size_type i = 0 ; i < eraseListSize ; + + i ) {
mergeToShowList ( * eraseList [ i ] , _showList , _overdrawThreshold ) ;
_currentBuffer . fillRect ( * eraseList [ i ] , plane . _back ) ;
2016-01-18 00:12:47 -06:00
}
}
void GfxFrameout : : drawScreenItemList ( const DrawList & screenItemList ) {
2016-06-30 13:50:10 -05:00
const DrawList : : size_type drawListSize = screenItemList . size ( ) ;
for ( DrawList : : size_type i = 0 ; i < drawListSize ; + + i ) {
const DrawItem & drawItem = * screenItemList [ i ] ;
2016-01-18 00:12:47 -06:00
mergeToShowList ( drawItem . rect , _showList , _overdrawThreshold ) ;
2016-06-30 13:50:10 -05:00
const ScreenItem & screenItem = * drawItem . screenItem ;
2016-01-18 00:12:47 -06:00
// TODO: Remove
2016-03-05 23:56:38 -06:00
// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect));
2016-01-18 00:12:47 -06:00
CelObj & celObj = * screenItem . _celObj ;
celObj . draw ( _currentBuffer , screenItem , drawItem . rect , screenItem . _mirrorX ^ celObj . _mirrorX ) ;
}
}
2011-10-12 02:43:08 +03:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : mergeToShowList ( const Common : : Rect & drawRect , RectList & showList , const int overdrawThreshold ) {
2016-06-30 13:50:10 -05:00
RectList mergeList ;
Common : : Rect merged ;
mergeList . add ( drawRect ) ;
for ( RectList : : size_type i = 0 ; i < mergeList . size ( ) ; + + i ) {
bool didMerge = false ;
const Common : : Rect & r1 = * mergeList [ i ] ;
if ( ! r1 . isEmpty ( ) ) {
for ( RectList : : size_type j = 0 ; j < showList . size ( ) ; + + j ) {
const Common : : Rect & r2 = * showList [ j ] ;
if ( ! r2 . isEmpty ( ) ) {
merged = r1 ;
merged . extend ( r2 ) ;
int difference = merged . width ( ) * merged . height ( ) ;
difference - = r1 . width ( ) * r1 . height ( ) ;
difference - = r2 . width ( ) * r2 . height ( ) ;
if ( r1 . intersects ( r2 ) ) {
const Common : : Rect overlap = r1 . findIntersectingRect ( r2 ) ;
difference + = overlap . width ( ) * overlap . height ( ) ;
}
if ( difference < = overdrawThreshold ) {
mergeList . erase_at ( i ) ;
showList . erase_at ( j ) ;
mergeList . add ( merged ) ;
didMerge = true ;
break ;
} else {
Common : : Rect outRects [ 2 ] ;
int splitCount = splitRectsForRender ( * mergeList [ i ] , * showList [ j ] , outRects ) ;
if ( splitCount ! = - 1 ) {
mergeList . add ( * mergeList [ i ] ) ;
mergeList . erase_at ( i ) ;
showList . erase_at ( j ) ;
didMerge = true ;
while ( splitCount - - ) {
mergeList . add ( outRects [ splitCount ] ) ;
}
break ;
}
}
}
2016-01-18 00:12:47 -06:00
}
2013-01-13 17:28:09 +02:00
2016-06-30 13:50:10 -05:00
if ( didMerge ) {
showList . pack ( ) ;
}
}
2013-01-11 00:45:10 +02:00
}
2012-05-14 02:30:15 +03:00
2016-06-30 13:50:10 -05:00
mergeList . pack ( ) ;
for ( RectList : : size_type i = 0 ; i < mergeList . size ( ) ; + + i ) {
showList . add ( * mergeList [ i ] ) ;
}
2010-09-19 14:50:28 +00:00
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : showBits ( ) {
2016-07-31 13:41:05 -05:00
if ( ! _showList . size ( ) ) {
2017-05-22 18:40:52 -05:00
updateScreen ( ) ;
2016-07-31 13:41:05 -05:00
return ;
}
2016-01-18 00:12:47 -06:00
for ( RectList : : const_iterator rect = _showList . begin ( ) ; rect ! = _showList . end ( ) ; + + rect ) {
Common : : Rect rounded ( * * rect ) ;
// NOTE: SCI engine used BR-inclusive rects so used slightly
// different masking here to ensure that the width of rects
// was always even.
rounded . left & = ~ 1 ;
rounded . right = ( rounded . right + 1 ) & ~ 1 ;
2016-07-31 13:41:05 -05:00
_cursor - > gonnaPaint ( rounded ) ;
2010-06-20 17:17:46 +00:00
}
2016-07-31 13:41:05 -05:00
_cursor - > paintStarting ( ) ;
2016-01-18 00:12:47 -06:00
for ( RectList : : const_iterator rect = _showList . begin ( ) ; rect ! = _showList . end ( ) ; + + rect ) {
Common : : Rect rounded ( * * rect ) ;
// NOTE: SCI engine used BR-inclusive rects so used slightly
// different masking here to ensure that the width of rects
// was always even.
rounded . left & = ~ 1 ;
rounded . right = ( rounded . right + 1 ) & ~ 1 ;
byte * sourceBuffer = ( byte * ) _currentBuffer . getPixels ( ) + rounded . top * _currentBuffer . screenWidth + rounded . left ;
2017-01-12 12:41:27 -06:00
// Sometimes screen items (especially from SCI2.1early transitions, like
// in the asteroids minigame in PQ4) generate zero-dimension show
// rectangles. In SSCI, zero-dimension rectangles are OK (they just
// result in no copy), but OSystem::copyRectToScreen will assert on
// them, so we need to check for zero-dimensions rectangles and ignore
// them explicitly
2016-07-26 19:56:12 -05:00
if ( rounded . width ( ) = = 0 | | rounded . height ( ) = = 0 ) {
continue ;
}
2016-01-18 00:12:47 -06:00
g_system - > copyRectToScreen ( sourceBuffer , _currentBuffer . screenWidth , rounded . left , rounded . top , rounded . width ( ) , rounded . height ( ) ) ;
}
2016-07-31 13:41:05 -05:00
_cursor - > donePainting ( ) ;
2016-01-18 00:12:47 -06:00
_showList . clear ( ) ;
2017-05-22 18:40:52 -05:00
updateScreen ( ) ;
2010-06-20 17:17:46 +00:00
}
2010-06-14 16:58:15 +00:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : alterVmap ( const Palette & palette1 , const Palette & palette2 , const int8 style , const int8 * const styleRanges ) {
uint8 clut [ 256 ] ;
for ( int paletteIndex = 0 ; paletteIndex < ARRAYSIZE ( palette1 . colors ) ; + + paletteIndex ) {
int outerR = palette1 . colors [ paletteIndex ] . r ;
int outerG = palette1 . colors [ paletteIndex ] . g ;
int outerB = palette1 . colors [ paletteIndex ] . b ;
if ( styleRanges [ paletteIndex ] = = style ) {
int minDiff = 262140 ;
2016-05-28 11:02:40 -05:00
int minDiffIndex = paletteIndex ;
2016-01-18 00:12:47 -06:00
for ( int i = 0 ; i < 236 ; + + i ) {
if ( styleRanges [ i ] ! = style ) {
int r = palette1 . colors [ i ] . r ;
int g = palette1 . colors [ i ] . g ;
int b = palette1 . colors [ i ] . b ;
int diffSquared = ( outerR - r ) * ( outerR - r ) + ( outerG - g ) * ( outerG - g ) + ( outerB - b ) * ( outerB - b ) ;
if ( diffSquared < minDiff ) {
minDiff = diffSquared ;
minDiffIndex = i ;
}
}
}
2011-11-17 22:08:36 +02:00
2016-01-18 00:12:47 -06:00
clut [ paletteIndex ] = minDiffIndex ;
}
2011-11-17 22:08:36 +02:00
2016-01-18 00:12:47 -06:00
if ( style = = 1 & & styleRanges [ paletteIndex ] = = 0 ) {
int minDiff = 262140 ;
2016-05-28 11:02:40 -05:00
int minDiffIndex = paletteIndex ;
2011-11-17 22:08:36 +02:00
2016-01-18 00:12:47 -06:00
for ( int i = 0 ; i < 236 ; + + i ) {
int r = palette2 . colors [ i ] . r ;
int g = palette2 . colors [ i ] . g ;
int b = palette2 . colors [ i ] . b ;
2011-11-17 22:08:36 +02:00
2016-01-18 00:12:47 -06:00
int diffSquared = ( outerR - r ) * ( outerR - r ) + ( outerG - g ) * ( outerG - g ) + ( outerB - b ) * ( outerB - b ) ;
if ( diffSquared < minDiff ) {
minDiff = diffSquared ;
minDiffIndex = i ;
}
2011-02-07 12:24:09 +00:00
}
2016-01-18 00:12:47 -06:00
clut [ paletteIndex ] = minDiffIndex ;
2011-11-17 22:08:36 +02:00
}
2016-01-18 00:12:47 -06:00
}
2011-02-07 12:24:09 +00:00
2016-01-18 00:12:47 -06:00
byte * pixels = ( byte * ) _currentBuffer . getPixels ( ) ;
for ( int pixelIndex = 0 , numPixels = _currentBuffer . screenWidth * _currentBuffer . screenHeight ; pixelIndex < numPixels ; + + pixelIndex ) {
byte currentValue = pixels [ pixelIndex ] ;
int8 styleRangeValue = styleRanges [ currentValue ] ;
if ( styleRangeValue = = - 1 & & styleRangeValue = = style ) {
currentValue = pixels [ pixelIndex ] = clut [ currentValue ] ;
2016-03-18 09:10:10 -05:00
// NOTE: In original engine this assignment happens outside of the
// condition, but if the branch is not followed the value is just
// going to be the same as it was before
styleRangeValue = styleRanges [ currentValue ] ;
2011-11-17 22:08:36 +02:00
}
2016-01-18 00:12:47 -06:00
if (
( styleRangeValue = = 1 & & styleRangeValue = = style ) | |
( styleRangeValue = = 0 & & style = = 1 )
) {
pixels [ pixelIndex ] = clut [ currentValue ] ;
}
2011-11-17 22:08:36 +02:00
}
}
2017-05-22 18:40:52 -05:00
void GfxFrameout : : updateScreen ( const int delta ) {
// using OSystem::getMillis instead of Sci::getTickCount because these
// values need to be monotonically increasing for the duration of the
// GfxFrameout object or else the screen will stop updating
const uint32 now = g_system - > getMillis ( ) * 60 / 1000 ;
if ( now < = _lastScreenUpdateTick + delta ) {
return ;
}
_lastScreenUpdateTick = now ;
g_system - > updateScreen ( ) ;
g_sci - > getSciDebugger ( ) - > onFrame ( ) ;
}
2016-03-02 14:02:15 -06:00
void GfxFrameout : : kernelFrameOut ( const bool shouldShowBits ) {
2016-07-25 11:06:27 -05:00
if ( _transitions - > hasShowStyles ( ) ) {
_transitions - > processShowStyles ( ) ;
2016-01-18 00:12:47 -06:00
} else if ( _palMorphIsOn ) {
2016-07-25 11:06:27 -05:00
palMorphFrameOut ( _transitions - > _styleRanges , nullptr ) ;
2016-01-18 00:12:47 -06:00
_palMorphIsOn = false ;
} else {
2016-07-25 11:06:27 -05:00
if ( _transitions - > hasScrolls ( ) ) {
_transitions - > processScrolls ( ) ;
}
2012-05-21 01:29:30 +03:00
2016-03-02 14:02:15 -06:00
frameOut ( shouldShowBits ) ;
2016-01-18 00:12:47 -06:00
}
2016-06-14 17:43:57 -05:00
2016-07-02 19:11:59 -05:00
throttle ( ) ;
}
void GfxFrameout : : throttle ( ) {
2017-05-04 22:42:32 -05:00
uint8 throttleTime ;
if ( _throttleState = = 2 ) {
throttleTime = 16 ;
_throttleState = 0 ;
} else {
throttleTime = 17 ;
+ + _throttleState ;
2016-06-14 17:43:57 -05:00
}
2017-05-04 22:42:32 -05:00
g_sci - > getEngineState ( ) - > speedThrottler ( throttleTime ) ;
g_sci - > getEngineState ( ) - > _throttleTrigger = true ;
2016-01-18 00:12:47 -06:00
}
2012-05-21 01:29:30 +03:00
2016-08-02 09:49:04 -05:00
void GfxFrameout : : shakeScreen ( int16 numShakes , const ShakeDirection direction ) {
if ( direction & kShakeHorizontal ) {
// Used by QFG4 room 750
warning ( " TODO: Horizontal shake not implemented " ) ;
return ;
}
while ( numShakes - - ) {
if ( direction & kShakeVertical ) {
g_system - > setShakePos ( _isHiRes ? 8 : 4 ) ;
}
2017-05-22 18:40:52 -05:00
updateScreen ( ) ;
2016-08-02 09:49:04 -05:00
g_sci - > getEngineState ( ) - > wait ( 3 ) ;
if ( direction & kShakeVertical ) {
g_system - > setShakePos ( 0 ) ;
}
2017-05-22 18:40:52 -05:00
updateScreen ( ) ;
2016-08-02 09:49:04 -05:00
g_sci - > getEngineState ( ) - > wait ( 3 ) ;
}
}
2016-03-08 10:27:15 -06:00
# pragma mark -
# pragma mark Mouse cursor
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
reg_t GfxFrameout : : kernelIsOnMe ( const reg_t object , const Common : : Point & position , bool checkPixel ) const {
2016-03-18 13:08:37 -05:00
const reg_t planeObject = readSelector ( _segMan , object , SELECTOR ( plane ) ) ;
2016-03-08 10:27:15 -06:00
Plane * plane = _visiblePlanes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
return make_reg ( 0 , 0 ) ;
2016-02-20 05:38:22 +01:00
}
2016-03-08 10:27:15 -06:00
ScreenItem * screenItem = plane - > _screenItemList . findByObject ( object ) ;
if ( screenItem = = nullptr ) {
return make_reg ( 0 , 0 ) ;
2016-02-20 05:38:22 +01:00
}
2016-03-18 13:08:37 -05:00
// NOTE: The original engine passed a copy of the ScreenItem into isOnMe
// as a hack around the fact that the screen items in `_visiblePlanes`
// did not have their `_celObj` pointers cleared when their CelInfo was
// updated by `Plane::decrementScreenItemArrayCounts`. We handle this
// this more intelligently by clearing `_celObj` in the copy assignment
// operator, which is only ever called by `decrementScreenItemArrayCounts`
// anyway.
2016-03-08 10:27:15 -06:00
return make_reg ( 0 , isOnMe ( * screenItem , * plane , position , checkPixel ) ) ;
2016-02-20 15:46:12 +01:00
}
2016-03-08 10:27:15 -06:00
bool GfxFrameout : : isOnMe ( const ScreenItem & screenItem , const Plane & plane , const Common : : Point & position , const bool checkPixel ) const {
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
Common : : Point scaledPosition ( position ) ;
mulru ( scaledPosition , Ratio ( _currentBuffer . screenWidth , _currentBuffer . scriptWidth ) , Ratio ( _currentBuffer . screenHeight , _currentBuffer . scriptHeight ) ) ;
scaledPosition . x + = plane . _planeRect . left ;
scaledPosition . y + = plane . _planeRect . top ;
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
if ( ! screenItem . _screenRect . contains ( scaledPosition ) ) {
return false ;
2016-02-20 05:38:22 +01:00
}
2016-03-08 10:27:15 -06:00
if ( checkPixel ) {
CelObj & celObj = screenItem . getCelObj ( ) ;
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
bool mirrorX = screenItem . _mirrorX ^ celObj . _mirrorX ;
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
scaledPosition . x - = screenItem . _scaledPosition . x ;
scaledPosition . y - = screenItem . _scaledPosition . y ;
2016-02-20 05:38:22 +01:00
2016-10-09 10:52:08 -05:00
mulru ( scaledPosition , Ratio ( celObj . _xResolution , _currentBuffer . screenWidth ) , Ratio ( celObj . _yResolution , _currentBuffer . screenHeight ) ) ;
2016-02-20 05:38:22 +01:00
2016-03-08 10:27:15 -06:00
if ( screenItem . _scale . signal ! = kScaleSignalNone & & screenItem . _scale . x & & screenItem . _scale . y ) {
scaledPosition . x = scaledPosition . x * 128 / screenItem . _scale . x ;
scaledPosition . y = scaledPosition . y * 128 / screenItem . _scale . y ;
2016-02-20 05:38:22 +01:00
}
2017-06-17 14:17:16 -05:00
// TODO/HACK: When clicking at the very bottom edge of a scaled cel, it
// is possible that the calculated `scaledPosition` ends up one pixel
// outside of the bounds of the cel. It is not known yet whether this is
// a bug that also existed in SSCI (and so garbage memory would be read
// there), or if there is actually an error in our scaling of
// `ScreenItem::_screenRect` and/or `scaledPosition`. For now, just do
// an extra bounds check and return so games don't crash when a user
// clicks an unlucky point. Later, double-check the disassembly and
// either confirm this is a suitable fix (because SSCI just read bad
// memory) or fix the actual broken thing and remove this workaround.
if ( scaledPosition . x < 0 | |
scaledPosition . y < 0 | |
scaledPosition . x > = celObj . _width | |
scaledPosition . y > = celObj . _height ) {
return false ;
}
2016-03-08 10:27:15 -06:00
uint8 pixel = celObj . readPixel ( scaledPosition . x , scaledPosition . y , mirrorX ) ;
2016-10-09 10:52:08 -05:00
return pixel ! = celObj . _skipColor ;
2016-02-20 05:38:22 +01:00
}
2016-03-08 10:27:15 -06:00
return true ;
2016-02-20 05:38:22 +01:00
}
2016-07-31 15:19:48 -05:00
bool GfxFrameout : : kernelSetNowSeen ( const reg_t screenItemObject ) const {
2016-03-10 14:05:27 -06:00
const reg_t planeObject = readSelector ( _segMan , screenItemObject , SELECTOR ( plane ) ) ;
Plane * plane = _planes . findByObject ( planeObject ) ;
if ( plane = = nullptr ) {
2016-03-10 17:31:51 -06:00
error ( " kSetNowSeen: Plane %04x:%04x not found for screen item %04x:%04x " , PRINT_REG ( planeObject ) , PRINT_REG ( screenItemObject ) ) ;
2016-03-10 14:05:27 -06:00
}
ScreenItem * screenItem = plane - > _screenItemList . findByObject ( screenItemObject ) ;
if ( screenItem = = nullptr ) {
2016-07-31 15:19:48 -05:00
return false ;
2016-03-10 14:05:27 -06:00
}
Common : : Rect result = screenItem - > getNowSeenRect ( * plane ) ;
2016-11-27 10:53:06 -06:00
if ( g_sci - > _features - > usesAlternateSelectors ( ) ) {
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( left ) , result . left ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( top ) , result . top ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( right ) , result . right - 1 ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( bottom ) , result . bottom - 1 ) ;
} else {
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( nsLeft ) , result . left ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( nsTop ) , result . top ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( nsRight ) , result . right - 1 ) ;
writeSelectorValue ( _segMan , screenItemObject , SELECTOR ( nsBottom ) , result . bottom - 1 ) ;
}
2016-07-31 15:19:48 -05:00
return true ;
2016-03-10 14:05:27 -06:00
}
2016-03-15 21:05:01 +02:00
void GfxFrameout : : remapMarkRedraw ( ) {
for ( PlaneList : : const_iterator it = _planes . begin ( ) ; it ! = _planes . end ( ) ; + + it ) {
Plane * p = * it ;
p - > remapMarkRedraw ( ) ;
}
}
2016-01-18 00:12:47 -06:00
# pragma mark -
# pragma mark Debugging
2012-05-21 01:29:30 +03:00
2016-01-18 00:12:47 -06:00
void GfxFrameout : : printPlaneListInternal ( Console * con , const PlaneList & planeList ) const {
for ( PlaneList : : const_iterator it = planeList . begin ( ) ; it ! = planeList . end ( ) ; + + it ) {
Plane * p = * it ;
p - > printDebugInfo ( con ) ;
2012-05-21 01:29:30 +03:00
}
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : printPlaneList ( Console * con ) const {
printPlaneListInternal ( con , _planes ) ;
}
void GfxFrameout : : printVisiblePlaneList ( Console * con ) const {
printPlaneListInternal ( con , _visiblePlanes ) ;
}
2012-09-26 04:17:31 +02:00
2016-03-07 16:41:57 -06:00
void GfxFrameout : : printPlaneItemListInternal ( Console * con , const ScreenItemList & screenItemList ) const {
ScreenItemList : : size_type i = 0 ;
for ( ScreenItemList : : const_iterator sit = screenItemList . begin ( ) ; sit ! = screenItemList . end ( ) ; sit + + ) {
ScreenItem * screenItem = * sit ;
con - > debugPrintf ( " %2d: " , i + + ) ;
screenItem - > printDebugInfo ( con ) ;
}
}
2016-01-18 00:12:47 -06:00
void GfxFrameout : : printPlaneItemList ( Console * con , const reg_t planeObject ) const {
Plane * p = _planes . findByObject ( planeObject ) ;
2012-05-21 01:29:30 +03:00
2016-01-18 00:12:47 -06:00
if ( p = = nullptr ) {
con - > debugPrintf ( " Plane does not exist " ) ;
return ;
}
2016-03-07 16:41:57 -06:00
printPlaneItemListInternal ( con , p - > _screenItemList ) ;
}
void GfxFrameout : : printVisiblePlaneItemList ( Console * con , const reg_t planeObject ) const {
Plane * p = _visiblePlanes . findByObject ( planeObject ) ;
if ( p = = nullptr ) {
con - > debugPrintf ( " Plane does not exist " ) ;
return ;
2012-05-21 01:29:30 +03:00
}
2016-03-07 16:41:57 -06:00
printPlaneItemListInternal ( con , p - > _screenItemList ) ;
2012-05-21 01:29:30 +03:00
}
2011-10-28 22:18:10 +03:00
} // End of namespace Sci