File: cursor.cpp

package info (click to toggle)
residualvm 0.3.1%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: bullseye
  • size: 31,292 kB
  • sloc: cpp: 227,029; sh: 7,256; xml: 1,731; perl: 1,067; java: 861; asm: 738; python: 691; ansic: 272; makefile: 139; objc: 81; sed: 22; php: 1
file content (250 lines) | stat: -rw-r--r-- 7,041 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* ResidualVM - A 3D game interpreter
 *
 * ResidualVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the AUTHORS
 * 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.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "engines/myst3/cursor.h"
#include "engines/myst3/directorysubentry.h"
#include "engines/myst3/gfx.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/scene.h"
#include "engines/myst3/state.h"

#include "graphics/surface.h"

#include "image/bmp.h"

namespace Myst3 {

struct CursorData {
	uint32 nodeID;
	uint16 hotspotX;
	uint16 hotspotY;
	double transparency;
	double transparencyXbox;
};

static const CursorData availableCursors[] = {
	{ 1000,  8,  8, 0.25, 0.00 },
	{ 1001,  8,  8, 0.50, 0.50 },
	{ 1002,  8,  8, 0.50, 0.50 },
	{ 1003,  1,  5, 0.50, 0.50 },
	{ 1004, 14,  5, 0.50, 0.50 },
	{ 1005, 16, 14, 0.50, 0.50 },
	{ 1006, 16, 14, 0.50, 0.50 },
	{ 1007,  8,  8, 0.55, 0.55 },
	{ 1000,  8,  8, 0.25, 0.00 },
	{ 1001,  8,  8, 0.50, 0.50 },
	{ 1011, 16, 16, 0.50, 0.50 },
	{ 1000,  6,  1, 0.50, 0.50 },
	{ 1000,  8,  8, 0.00, 0.25 }
};

Cursor::Cursor(Myst3Engine *vm) :
	_vm(vm),
	_position(vm->_scene->getCenter()),
	_hideLevel(0),
	_lockedAtCenter(false) {

	// The cursor is drawn unscaled
	_scaled = false;
	_isConstrainedToWindow = false;

	// Load available cursors
	loadAvailableCursors();

	// Set default cursor
	changeCursor(8);
}

void Cursor::loadAvailableCursors() {
	assert(_textures.empty());

	// Load available cursors
	for (uint i = 0; i < ARRAYSIZE(availableCursors); i++) {
		// Check if a cursor sharing the same texture has already been loaded
		if (_textures.contains(availableCursors[i].nodeID)) continue;

		// Load the cursor bitmap
		const DirectorySubEntry *cursorDesc = _vm->getFileDescription("GLOB", availableCursors[i].nodeID, 0, DirectorySubEntry::kRawData);
		if (!cursorDesc)
			error("Cursor %d does not exist", availableCursors[i].nodeID);

		Common::MemoryReadStream *bmpStream = cursorDesc->getData();

		Image::BitmapDecoder bitmapDecoder;
		if (!bitmapDecoder.loadStream(*bmpStream))
			error("Could not decode Myst III bitmap");
		const Graphics::Surface *surfaceBGRA = bitmapDecoder.getSurface();
		Graphics::Surface *surfaceRGBA = surfaceBGRA->convertTo(Texture::getRGBAPixelFormat());

		delete bmpStream;

		// Apply the colorkey for transparency
		for (uint y = 0; y < surfaceRGBA->h; y++) {
			byte *pixels = (byte *)(surfaceRGBA->getBasePtr(0, y));
			for (uint x = 0; x < surfaceRGBA->w; x++) {
				byte *r = pixels + 0;
				byte *g = pixels + 1;
				byte *b = pixels + 2;
				byte *a = pixels + 3;

				if (*r == 0 && *g == 0xFF && *b == 0 && *a == 0xFF) {
					*g = 0;
					*a = 0;
				}

				pixels += 4;
			}
		}

		// Create and store the texture
		_textures.setVal(availableCursors[i].nodeID, _vm->_gfx->createTexture(surfaceRGBA));

		surfaceRGBA->free();
		delete surfaceRGBA;
	}
}

Cursor::~Cursor() {
	// Free cursors textures
	for (TextureMap::iterator it = _textures.begin(); it != _textures.end(); it++) {
		_vm->_gfx->freeTexture(it->_value);
	}
}

void Cursor::changeCursor(uint32 index) {
	if (index > 12)
		return;

	if (_vm->getPlatform() == Common::kPlatformXbox) {
		// The cursor is hidden when it is not hovering hotspots
		if ((index == 0 || index == 8) && _vm->_state->getViewType() != kCube)
			index = 12;
	}

	_currentCursorID = index;
}

double Cursor::getTransparencyForId(uint32 cursorId) {
	assert(cursorId < ARRAYSIZE(availableCursors));
	if (_vm->getPlatform() == Common::kPlatformXbox) {
		return availableCursors[cursorId].transparencyXbox;
	} else {
		return availableCursors[cursorId].transparency;
	}
}

void Cursor::lockPosition(bool lock) {
	if (_lockedAtCenter == lock)
		return;

	_lockedAtCenter = lock;

	g_system->lockMouse(lock);

	Common::Point center = _vm->_scene->getCenter();
	if (_lockedAtCenter) {
		// Locking, just move the cursor at the center of the screen
		_position = center;
	} else {
		// Unlocking, warp the actual mouse position to the cursor
		g_system->warpMouse(center.x, center.y);
	}
}

void Cursor::updatePosition(const Common::Point &mouse) {
	if (!_lockedAtCenter) {
		_position = mouse;
	} else {
		_position = _vm->_scene->getCenter();
	}
}

Common::Point Cursor::getPosition(bool scaled) {
	if (scaled) {
		Common::Rect viewport = _vm->_gfx->viewport();

		// The rest of the engine expects 640x480 coordinates
		Common::Point scaledPosition = _position;
		scaledPosition.x -= viewport.left;
		scaledPosition.y -= viewport.top;
		scaledPosition.x = CLIP<int16>(scaledPosition.x, 0, viewport.width());
		scaledPosition.y = CLIP<int16>(scaledPosition.y, 0, viewport.height());
		scaledPosition.x *= Renderer::kOriginalWidth / (float) viewport.width();
		scaledPosition.y *= Renderer::kOriginalHeight / (float) viewport.height();

		return scaledPosition;
	} else {
		return _position;
	}
}

void Cursor::draw() {
	assert(_currentCursorID < ARRAYSIZE(availableCursors));

	const CursorData &cursor = availableCursors[_currentCursorID];

	Texture *texture = _textures[cursor.nodeID];
	if (!texture) {
		error("No texture for cursor with id %d", cursor.nodeID);
	}

	// Rect where to draw the cursor
	Common::Rect screenRect = Common::Rect(texture->width, texture->height);
	screenRect.translate(_position.x - cursor.hotspotX, _position.y - cursor.hotspotY);

	// Rect where to draw the cursor
	Common::Rect textureRect = Common::Rect(texture->width, texture->height);

	float transparency = 1.0;

	int32 varTransparency = _vm->_state->getCursorTransparency();
	if (_lockedAtCenter || varTransparency == 0) {
		if (varTransparency >= 0)
			transparency = varTransparency / 100.0;
		else
			transparency = getTransparencyForId(_currentCursorID);
	}

	_vm->_gfx->drawTexturedRect2D(screenRect, textureRect, texture, transparency);
}

void Cursor::setVisible(bool show) {
	if (show)
		_hideLevel = MAX<int32>(0, --_hideLevel);
	else
		_hideLevel++;
}

bool Cursor::isVisible() {
	return !_hideLevel && !_vm->_state->getCursorHidden() && !_vm->_state->getCursorLocked();
}

void Cursor::getDirection(float &pitch, float &heading) {
	if (_lockedAtCenter) {
		pitch = _vm->_state->getLookAtPitch();
		heading = _vm->_state->getLookAtHeading();
	} else {
		_vm->_scene->screenPosToDirection(_position, pitch, heading);
	}
}

} // End of namespace Myst3