BLADERUNNER: Allow use of beta crosshairs and additive draw

This is only through the debugger mouse command
This commit is contained in:
antoniou79 2022-02-21 22:46:48 +02:00
parent cf9794d895
commit 4bd2a36ba9
7 changed files with 229 additions and 47 deletions

View File

@ -396,6 +396,22 @@ static inline void drawPixel(Graphics::Surface &surface, void* dst, uint32 value
}
}
static inline void getPixel(Graphics::Surface &surface, void* dst, uint32 &value) {
switch (surface.format.bytesPerPixel) {
case 1:
value = (uint8)(*(uint8*)dst);
break;
case 2:
value = (uint16)(*(uint16*)dst);
break;
case 4:
value = (uint32)(*(uint32*)dst);
break;
default:
break;
}
}
void blit(const Graphics::Surface &src, Graphics::Surface &dst);
} // End of namespace BladeRunner

View File

@ -109,6 +109,9 @@ Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() {
_showStatsVk = false;
_showMazeScore = false;
_showMouseClickInfo = false;
_useBetaCrosshairsCursor = false;
_useAdditiveDrawModeForMouseCursorMode0 = false;
_useAdditiveDrawModeForMouseCursorMode1 = false;
registerCmd("anim", WRAP_METHOD(Debugger, cmdAnimation));
registerCmd("health", WRAP_METHOD(Debugger, cmdHealth));
@ -134,7 +137,7 @@ Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() {
registerCmd("object", WRAP_METHOD(Debugger, cmdObject));
registerCmd("item", WRAP_METHOD(Debugger, cmdItem));
registerCmd("region", WRAP_METHOD(Debugger, cmdRegion));
registerCmd("click", WRAP_METHOD(Debugger, cmdClick));
registerCmd("mouse", WRAP_METHOD(Debugger, cmdMouse));
registerCmd("difficulty", WRAP_METHOD(Debugger, cmdDifficulty));
#if BLADERUNNER_ORIGINAL_BUGS
#else
@ -1854,31 +1857,55 @@ bool Debugger::cmdRegion(int argc, const char **argv) {
}
/**
* Toggle showing mouse click info in the text console (not the debugger window)
* click: Toggle showing mouse click info in the text console (not the debugger window)
* beta: Toggle beta crosshairs for aiming in combat mode
* add0: Toggle semi-transparent hotspot cursor (additive draw mode 0)
* add1: Toggle semi-transparent hotspot cursor (additive draw mode 1)
*/
bool Debugger::cmdClick(int argc, const char **argv) {
bool Debugger::cmdMouse(int argc, const char **argv) {
bool invalidSyntax = false;
if (argc != 2) {
invalidSyntax = true;
} else {
if (argc == 2) {
//
// set a debug variable to enable showing the mouse click info
//
Common::String argName = argv[1];
argName.toLowercase();
if (argc == 2 && argName == "toggle") {
invalidSyntax = false;
if (argName == "click") {
_showMouseClickInfo = !_showMouseClickInfo;
debugPrintf("Showing mouse click info = %s\n", _showMouseClickInfo ? "True":"False");
return false; // close the debugger console
} else if (argName == "beta") {
_useBetaCrosshairsCursor = !_useBetaCrosshairsCursor;
} else if (argName == "add0") {
_useAdditiveDrawModeForMouseCursorMode0 = !_useAdditiveDrawModeForMouseCursorMode0;
_useAdditiveDrawModeForMouseCursorMode1 = false;
} else if (argName == "add1") {
_useAdditiveDrawModeForMouseCursorMode0 = false;
_useAdditiveDrawModeForMouseCursorMode1 = !_useAdditiveDrawModeForMouseCursorMode1;
} else {
invalidSyntax = true;
}
if (!invalidSyntax) {
debugPrintf("Showing mouse click info = %s\n", _showMouseClickInfo ? "True":"False");
debugPrintf("Showing beta crosshairs = %s\n", _useBetaCrosshairsCursor ? "True":"False");
debugPrintf("Mouse draw additive mode 0 = %s\n", _useAdditiveDrawModeForMouseCursorMode0 ? "True":"False");
debugPrintf("Mouse draw additive mode 1 = %s\n", _useAdditiveDrawModeForMouseCursorMode1 ? "True":"False");
}
} else {
invalidSyntax = true;
}
if (invalidSyntax) {
debugPrintf("Toggle showing mouse info (on mouse click) in the text console\n");
debugPrintf("Usage: %s toggle\n", argv[0]);
debugPrintf("click: Toggle showing mouse info (on mouse click) in the text console\n");
debugPrintf("beta: Toggle beta crosshairs cursor\n");
debugPrintf("add0: Toggle semi-transparent hotspot cursor (additive mode 0)\n");
debugPrintf("add1: Toggle semi-transparent hotspot cursor (additive mode 1)\n");
debugPrintf("Usage 1: %s click\n", argv[0]);
debugPrintf("Usage 2: %s beta\n", argv[0]);
debugPrintf("Usage 3: %s add0\n", argv[0]);
debugPrintf("Usage 4: %s add1\n", argv[0]);
}
return true;
}

View File

@ -85,6 +85,9 @@ public:
bool _showStatsVk;
bool _showMazeScore;
bool _showMouseClickInfo;
bool _useBetaCrosshairsCursor;
bool _useAdditiveDrawModeForMouseCursorMode0;
bool _useAdditiveDrawModeForMouseCursorMode1;
Debugger(BladeRunnerEngine *vm);
~Debugger() override;
@ -112,7 +115,7 @@ public:
bool cmdObject(int argc, const char **argv);
bool cmdItem(int argc, const char **argv);
bool cmdRegion(int argc, const char **argv);
bool cmdClick(int argc, const char **argv);
bool cmdMouse(int argc, const char **argv);
bool cmdDifficulty(int argc, const char **argv);
#if BLADERUNNER_ORIGINAL_BUGS
#else

View File

@ -18,12 +18,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/mouse.h"
#include "bladerunner/actor.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/combat.h"
#include "bladerunner/debugger.h"
#include "bladerunner/dialogue_menu.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/items.h"
@ -56,106 +56,165 @@ Mouse::Mouse(BladeRunnerEngine *vm) {
_randomCountdownY = 0;
_randomX = 0;
_randomY = 0;
_drawModeBitFlags = 0;
}
Mouse::~Mouse() {
}
void Mouse::setCursor(int cursor) {
assert(cursor >= 0 && cursor <= 16);
assert(cursor >= 0 && cursor <= 17);
if (cursor == _cursor) {
return;
}
_cursor = cursor;
_drawModeBitFlags = 0;
switch (_cursor) {
case 0:
// normal cursor (white)
// (also the default init value for mouse cursor)
_frame = 3;
_hotspotX = 0;
_hotspotY = 0;
break;
case 1:
// normal cursor over hotspot (not exit) (green rotating)
// animating: 8 frames (4-11)
_frame = 4;
_hotspotX = 0;
_hotspotY = 0;
if (_vm->_debugger->_useAdditiveDrawModeForMouseCursorMode0
|| _vm->_debugger->_useAdditiveDrawModeForMouseCursorMode1) {
_drawModeBitFlags |= MouseDrawFlags::SPECIAL;
if (_vm->_debugger->_useAdditiveDrawModeForMouseCursorMode0) {
_drawModeBitFlags |= MouseDrawFlags::ADDITIVE_MODE0;
} else {
_drawModeBitFlags |= MouseDrawFlags::ADDITIVE_MODE1;
}
}
break;
case 2:
// exit cursor (upwards/North)
_frame = 12;
_hotspotX = 12;
_hotspotY = 0;
break;
case 3:
// exit cursor (right/East)
_frame = 15;
_hotspotX = 23;
_hotspotY = 12;
break;
case 4:
// exit cursor (downwards/South)
_frame = 13;
_hotspotX = 12;
_hotspotY = 23;
break;
case 5:
// exit cursor (left/West)
_frame = 14;
_hotspotX = 0;
_hotspotY = 12;
break;
case 6:
// combat cursor, simple bullets (normal / no target)
_frame = 16;
_hotspotX = 19;
_hotspotY = 19;
break;
case 7:
// combat cursor, simple bullets (hot target)
// animating: 8 frames (17-24)
_frame = 17;
_hotspotX = 19;
_hotspotY = 19;
break;
case 8:
// combat cursor, advanced bullets (normal / no target)
_frame = 25;
_hotspotX = 19;
_hotspotY = 19;
break;
case 9:
// combat cursor, advanced bullets (hot target)
// animating: 8 frames (26-33)
_frame = 26;
_hotspotX = 19;
_hotspotY = 19;
break;
case 10:
// combat cursor, best bullets (normal / no target)
_frame = 34;
_hotspotX = 19;
_hotspotY = 19;
break;
case 11:
// combat cursor, best bullets (hot target)
// animating: 8 frames (35-42)
_frame = 35;
_hotspotX = 19;
_hotspotY = 19;
break;
case 12:
// exit cursor (upwards/North)
// resets animCounter too (as opposed to _cursor == 2)
// bouncy animation (handled in updateCursorFrame())
_frame = 12;
_hotspotX = 12;
_hotspotY = 0;
_animCounter = 0;
break;
case 13:
// exit cursor (right/East)
// resets animCounter too (as opposed to _cursor == 3)
// bouncy animation (handled in updateCursorFrame())
_frame = 15;
_hotspotX = 23;
_hotspotY = 12;
_animCounter = 0;
break;
case 14:
// exit cursor (downwards/South)
// resets animCounter too (as opposed to _cursor == 4)
// bouncy animation (handled in updateCursorFrame())
_frame = 13;
_hotspotX = 12;
_hotspotY = 23;
_animCounter = 0;
break;
case 15:
// exit cursor (left/West)
// resets animCounter too (as opposed to _cursor == 5)
// bouncy animation (handled in updateCursorFrame())
_frame = 14;
_hotspotX = 0;
_hotspotY = 12;
_animCounter = 0;
break;
case 16:
#if !BLADERUNNER_ORIGINAL_BUGS
_frame = 0;
break;
case 17:
#endif
// (beta version) combat cursor (white or flashing white/blue)
// Original behavior in the code remnandts seems to have been
// that this cursor (id: 16) would animate (3 frames (0-2)),
// essentially continuously flashing fast.
// In the preview trailers, the cursor is white while not on a target
// and red when on target (shown on Zuben) -- perhaps other solid colors were used,
// (it's hard to tell the color at the shooting grounds shown in the preview trailer).
// We introduce an extra case (id 17) to differentiate the beta crosshairs
// depending on whether they're hovering over a hot target or not.
// TODO Maybe replace the solid colored frames with a color based on McCoy's bullet type
// So:
// id 16: inactive (beta) combat crosshairs
// id 17: active (beta) combat crosshairs
_frame = 1;
_hotspotX = 11;
_hotspotY = 11;
default:
@ -256,7 +315,7 @@ void Mouse::draw(Graphics::Surface &surface, int x, int y) {
_x = CLIP(x, 0, surface.w - 1);
_y = CLIP(y, 0, surface.h - 1);
_vm->_shapes->get(_frame)->draw(surface, _x - _hotspotX, _y - _hotspotY);
_vm->_shapes->get(_frame)->draw(surface, _x - _hotspotX, _y - _hotspotY, _drawModeBitFlags);
updateCursorFrame();
}
@ -324,8 +383,19 @@ void Mouse::updateCursorFrame() {
_hotspotX = -offset[_animCounter];
break;
case 16:
#if !BLADERUNNER_ORIGINAL_BUGS
break;
case 17:
#endif
if (++_frame > 2)
#if BLADERUNNER_ORIGINAL_BUGS
_frame = 0;
#else
// Better not to flash the white frame (frame 0),
// while quickly animating the beta cursor.
// It's less annoying to the eyes this way.
_frame = 1;
#endif
break;
default:
break;
@ -404,36 +474,47 @@ void Mouse::tick(int x, int y) {
}
if (actorId >= 0 || itemId >= 0 || isObject) {
switch (_vm->_settings->getAmmoType()) {
case 0:
cursorId = 7;
break;
case 1:
cursorId = 9;
break;
case 2:
cursorId = 11;
break;
default:
break;
if (_vm->_debugger->_useBetaCrosshairsCursor) {
cursorId = 17;
_drawModeBitFlags |= (0x01 << _vm->_settings->getAmmoType());
_drawModeBitFlags |= MouseDrawFlags::SPECIAL;
} else {
switch (_vm->_settings->getAmmoType()) {
case 0:
cursorId = 7;
break;
case 1:
cursorId = 9;
break;
case 2:
cursorId = 11;
break;
default:
break;
}
}
if (!_vm->_playerActor->isMoving() && animationMode != kAnimationModeCombatAim && animationMode != kAnimationModeCombatHit && animationMode != kAnimationModeCombatDie) {
_vm->_playerActor->changeAnimationMode(kAnimationModeCombatAim, false);
}
} else {
switch (_vm->_settings->getAmmoType()) {
case 0:
cursorId = 6;
break;
case 1:
cursorId = 8;
break;
case 2:
cursorId = 10;
break;
default:
break;
if (_vm->_debugger->_useBetaCrosshairsCursor) {
cursorId = 16;
_drawModeBitFlags &= ~(0x01 << _vm->_settings->getAmmoType());
_drawModeBitFlags &= ~(MouseDrawFlags::SPECIAL);
} else {
switch (_vm->_settings->getAmmoType()) {
case 0:
cursorId = 6;
break;
case 1:
cursorId = 8;
break;
case 2:
cursorId = 10;
break;
default:
break;
}
}
if (!_vm->_playerActor->isMoving() && animationMode != kAnimationModeCombatIdle && animationMode != kAnimationModeCombatHit && animationMode != kAnimationModeCombatDie) {
_vm->_playerActor->changeAnimationMode(kAnimationModeCombatIdle, false);
@ -448,7 +529,8 @@ bool Mouse::isRandomized() const {
}
bool Mouse::isInactive() const {
return _cursor == 6 || _cursor == 8 || _cursor == 10;
// Note: This only refers to "inactive" cursor in combat mode!
return _cursor == 6 || _cursor == 8 || _cursor == 10 || _cursor == 16;
}
// TEST: RC01 after intro: [290, 216] -> [-204.589249 51.450668 7.659241]

View File

@ -50,6 +50,8 @@ class Mouse {
int _randomX;
int _randomY;
uint8 _drawModeBitFlags; // replaces the additive bool with a set of bit flags (including flags for additive mode)
public:
Mouse(BladeRunnerEngine *vm);
~Mouse();
@ -71,8 +73,16 @@ public:
bool isRandomized() const;
bool isInactive() const;
// private:
Vector3 getXYZ(int x, int y) const;
typedef enum mouseDrawFlags {
REDCROSSHAIRS = 0x01,
YELLOWCROSSHAIRS = 0x02,
BLUECROSSHAIRS = 0x04,
SPECIAL = 0x08,
ADDITIVE_MODE0 = 0x10,
ADDITIVE_MODE1 = 0x20
} MouseDrawFlags;
};
} // End of namespace BladeRunner

View File

@ -22,6 +22,7 @@
#include "bladerunner/shape.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/mouse.h"
#include "common/debug.h"
#include "common/ptr.h"
@ -60,7 +61,7 @@ Shape::~Shape() {
delete[] _data;
}
void Shape::draw(Graphics::Surface &surface, int x, int y) const {
void Shape::draw(Graphics::Surface &surface, int x, int y, uint8 drawModeBitFlags) const {
int src_x = CLIP(-x, 0, _width);
int src_y = CLIP(-y, 0, _height);
@ -78,18 +79,61 @@ void Shape::draw(Graphics::Surface &surface, int x, int y) const {
const uint8 *src_p = _data + 2 * (src_y * _width + src_x);
uint16 shpColor = 0;
uint32 surfaceColorRGBPrev = 0;
uint32 newSurfaceColorRGB = 0;
uint8 a, r, g, b;
uint8 rPrev, gPrev, bPrev;
uint16 rgb16bitPrev = 0;
uint16 rgb16bitAdd = 0;
for (int yi = 0; yi != rect_h; ++yi) {
for (int xi = 0; xi != rect_w; ++xi) {
uint16 shpColor = READ_LE_UINT16(src_p);
shpColor = READ_LE_UINT16(src_p);
src_p += 2;
uint8 a, r, g, b;
getGameDataColor(shpColor, a, r, g, b);
if (!a) {
// Ignore the alpha in the output as it is inversed in the input
void *dstPtr = surface.getBasePtr(CLIP(dst_x + xi, 0, surface.w - 1), CLIP(dst_y + yi, 0, surface.h - 1));
drawPixel(surface, dstPtr, surface.format.RGBToColor(r, g, b));
if (drawModeBitFlags & Mouse::MouseDrawFlags::SPECIAL) {
// It seems that the additive mode was supposed to be used only for cursor shapes
// From testing, the only cursor shape that seems to work with it is the green rotating cursor
// We add extra code here to cover the cases of the beta crosshairs cursor
// being drawn a different color based on bullet power
// The code for creating the specific color is custom.
if (drawModeBitFlags & Mouse::MouseDrawFlags::REDCROSSHAIRS) {
newSurfaceColorRGB = surface.format.RGBToColor((b & 0x8B) | (g >> 1), 0, 0);
} else if (drawModeBitFlags & Mouse::MouseDrawFlags::YELLOWCROSSHAIRS) {
newSurfaceColorRGB = surface.format.RGBToColor(b & 0xDF, (b & 0xA5) | (g >> 1), 0);
} else if (drawModeBitFlags & Mouse::MouseDrawFlags::BLUECROSSHAIRS) {
newSurfaceColorRGB = surface.format.RGBToColor(r, g, b);
} else {
// Additive modes
getPixel(surface, dstPtr, surfaceColorRGBPrev);
if (drawModeBitFlags & Mouse::MouseDrawFlags::ADDITIVE_MODE0) {
// This code makes the cursor semi-transparent
// but it may not be what the disassembly of the original was going for.
newSurfaceColorRGB = surface.format.RGBToColor(r, g, b);
newSurfaceColorRGB = (((uint16)surfaceColorRGBPrev >> 1) & 0xFBEF)
+ (((uint16)newSurfaceColorRGB >> 1) & 0xFBEF);
} else if (drawModeBitFlags & Mouse::MouseDrawFlags::ADDITIVE_MODE1) {
// This code may be closer to what the disassembly of the original was doing
// for additive draw mode but it doesn't look well.
surface.format.colorToRGB(surfaceColorRGBPrev, rPrev, gPrev, bPrev);
rgb16bitPrev = ( ((uint16)(rPrev >> 3) << 10)
| ((uint16)(gPrev >> 3) << 5)
| ((uint16)(bPrev >> 3)));
rgb16bitAdd = (((uint16)rgb16bitPrev >> 1) & 0xFBEF)
+ ((shpColor >> 1) & 0xFBEF);
getGameDataColor(rgb16bitAdd, a, r, g, b);
newSurfaceColorRGB = surface.format.RGBToColor(r, g, b);
}
}
} else {
newSurfaceColorRGB = surface.format.RGBToColor(r, g, b);
}
drawPixel(surface, dstPtr, newSurfaceColorRGB);
}
}
src_p += 2 * (_width - rect_w);

View File

@ -50,7 +50,7 @@ class Shape {
public:
~Shape();
void draw(Graphics::Surface &surface, int x, int y) const;
void draw(Graphics::Surface &surface, int x, int y, uint8 drawModeBitFlags = 0) const;
int getWidth() const { return _width; }
int getHeight() const { return _height; }