File: picture_mickey_winnie.cpp

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (348 lines) | stat: -rw-r--r-- 9,658 bytes parent folder | download
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/* 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 "agi/agi.h"
#include "agi/graphics.h"
#include "agi/picture.h"

#include "agi/preagi/picture_mickey_winnie.h"

namespace Agi {

// PictureMgr_Mickey_Winnie decodes and draws picture resources in Mickey's
// Space Adventure (DOS) and Winnie the Pooh (DOS/Amiga/A2/C64/CoCo).
//
// Mickey and Winnie DOS/Amiga use the same format. The picture code in
// their executables appears to be the same.
//
// The A2/C64/CoCo versions of Winnie use a completely different format, but
// they do support the same features. These games start in ScummVM but they
// don't really work yet. TODO: display the right colors, figure out the line
// differences, support these versions.
//
// Both formats support lines, flood fills, and patterns. No priority screen.
//
// Unique features to these formats:
//
// 1. Pictures can be drawn on top of others at arbitrary locations. Used to
//    draw items in rooms, and to draw room pictures with a buffer on each side
//    in DOS/Amiga. The pictures don't fill the screen width because they were
//    designed for the Apple II.
//
// 2. Mickey's crystals animate. Most of the work is done in MickeyEngine;
//    this class just allows the engine to set a maximum number of picture
//    instructions to execute. Unclear if this is same effect as the original.
//
// 3. The pattern opcode draws solid circles in up to 17 sizes.
//
// Mickey features animating spaceship lights, but the engine handles that.
// The lights are a picture whose instructions are modified before drawing.
//
// TODO: There are extremely minor inaccuracies in several Winnie pictures.
// The F1 opcode's effects are not fully understood, and it creates subtle
// discrepancies. It may be related to dithering. However, so few pictures
// contain F3, and even fewer are affected by ignoring it or not, and only
// by a few pixels, that it doesn't matter except for completeness.
// See: picture 34 door handles (Rabbit's kitchen)

PictureMgr_Mickey_Winnie::PictureMgr_Mickey_Winnie(AgiBase *agi, GfxMgr *gfx) :
	PictureMgr(agi, gfx) {

	switch (agi->getPlatform()) {
	case Common::kPlatformAmiga:
	case Common::kPlatformDOS:
		_isDosOrAmiga = true;
		break;
	default:
		_isDosOrAmiga = false;
		_minCommand = 0xe0;
		break;
	}

	_xOffset = 0;
	_yOffset = 0;
	_maxStep = 0;
}

void PictureMgr_Mickey_Winnie::drawPicture() {
	debugC(kDebugLevelPictures, "Drawing picture");

	_dataOffset = 0;
	_dataOffsetNibble = false;
	_patCode = 0;
	_patNum = 0;
	_priOn = false;
	_scrOn = false;
	_priColor = 4;

	if (_isDosOrAmiga) {
		_scrColor = 15;
		drawPicture_DOS_Amiga();
	} else {
		_scrColor = 0;
		drawPicture_A2_C64_CoCo();
	}
}

void PictureMgr_Mickey_Winnie::drawPicture_DOS_Amiga() {
	int step = 0;
	while (_dataOffset < _dataSize) {
		byte curByte = getNextByte();

		switch (curByte) {
		case 0xf0:
			draw_SetColor();
			_scrOn = true;
			break;
		case 0xf1:
			_scrOn = false;
			break;
		case 0xf4:
			yCorner();
			break;
		case 0xf5:
			xCorner();
			break;
		case 0xf6:
			draw_LineAbsolute();
			break;
		case 0xf7:
			draw_LineShort();
			break;
		case 0xf8: {
			// The screen-on flag does not prevent PreAGI flood fills.
			// Winnie picture 7 (Roo) contains F1 before several fills.
			byte prevScrOn = _scrOn;
			_scrOn = true;
			PictureMgr::draw_Fill();
			_scrOn = prevScrOn;
			break;
		}
		case 0xf9:
			plotBrush();
			break;
		case 0xff: // end of data
			return;
		default:
			warning("Unknown picture opcode %02x at %04x", curByte, _dataOffset - 1);
			break;
		}

		// Limit drawing to the optional maximum number of opcodes.
		// Used by Mickey for crystal animation.
		step++;
		if (step == _maxStep) {
			return;
		}
	}
}

void PictureMgr_Mickey_Winnie::drawPicture_A2_C64_CoCo() {
	while (_dataOffset < _dataSize) {
		byte curByte = getNextByte();

		if ((curByte >= 0xF0) && (curByte <= 0xFE)) {
			_scrColor = curByte & 0x0F;
			continue;
		}

		switch (curByte) {
		case 0xe0:  // x-corner
			xCorner();
			break;
		case 0xe1:  // y-corner
			yCorner();
			break;
		case 0xe2:  // dynamic draw lines
			draw_LineShort();
			break;
		case 0xe3:  // absolute draw lines
			draw_LineAbsolute();
			break;
		case 0xe4:  // fill
			draw_SetColor();
			PictureMgr::draw_Fill();
			break;
		case 0xe5:  // enable screen drawing
			_scrOn = true;
			break;
		case 0xe6:  // plot brush
			plotBrush();
			break;
		case 0xff: // end of data
			return;
		default:
			warning("Unknown picture opcode %02x at %04x", curByte, _dataOffset - 1);
			break;
		}
	}
}

/**
 * plotBrush (PreAGI)
 *
 * Plots the given brush pattern. All brushes are solid circles.
 */
void PictureMgr_Mickey_Winnie::plotBrush() {
	_patCode = getNextByte();
	if (_patCode > 12) {
		_patCode = 12;
	}

	for (;;) {
		byte x, y;
		if (!getNextCoordinates(x, y))
			break;

		plotPattern(x, y);
	}
}

/**
 * plotPattern
 *
 * Draws a solid circle. Size is determined by the pattern code.
 */
void PictureMgr_Mickey_Winnie::plotPattern(byte x, byte y) {
	// PreAGI patterns are 13 solid circles
	static const byte circleData[] = {
		0x00,
		0x01, 0x01,
		0x01, 0x02, 0x02,
		0x01, 0x02, 0x03, 0x03,
		0x02, 0x03, 0x04, 0x04, 0x04,
		0x02, 0x03, 0x04, 0x05, 0x05, 0x05,
		0x02, 0x04, 0x05, 0x05, 0x06, 0x06, 0x06,
		0x02, 0x04, 0x05, 0x06, 0x06, 0x07, 0x07, 0x07,
		0x02, 0x04, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x08,
		0x03, 0x05, 0x06, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
		0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a,
		0x03, 0x05, 0x07, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
		0x03, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c
	};

	int circleDataIndex = (_patCode * (_patCode + 1)) / 2;

	// draw the circle by drawing its vertical lines two at a time, starting at the
	// left and right edges and working inwards. circles have odd widths, so the
	// final iteration draws the middle line twice.
	for (int i = _patCode; i >= 0; i--) {
		const byte height = circleData[circleDataIndex++];
		int16 x1, y1, x2, y2;

		// left vertical line
		x1 = x - i;
		x2 = x1;
		y1 = y - height;
		y2 = y + height;
		draw_Line(x1, y1, x2, y2);

		// right vertical line
		x1 = x + i;
		x2 = x1;
		draw_Line(x1, y1, x2, y2);
	}
}

/**
 * Flood fills from a start position, with a clipped height.
 */
void PictureMgr_Mickey_Winnie::draw_Fill(int16 x, int16 y) {
	// Flood fill does extra height clipping, and pictures rely on this.
	// The get-coordinates routine clips to (139, 159) and then the
	// flood fill routine checks if y >= 159 and decrements to 158.
	// The flood fill clip is not in in Apple II/C64/CoCo versions
	// of Winnie, as can be seen by the table edge being a different
	// color than Winnie's shirt in the first room, but the same
	// color as the shirt in DOS/Amiga. (Picture 28)
	if (_isDosOrAmiga) {
		if (y >= _height) { // 159
			debugC(kDebugLevelPictures, "clipping %c from %d to %d", 'y', y, _height - 1);
			y = _height - 1; // 158
		}
	}

	PictureMgr::draw_Fill(x, y);
}

/**
 * Gets the next x coordinate in the current picture instruction,
 * and clip it to the picture width. Many Winnie pictures contain
 * out of bounds coordinates and rely on this clipping.
 */
bool PictureMgr_Mickey_Winnie::getNextXCoordinate(byte &x) {
	if (!getNextParamByte(x)) {
		return false;
	}

	if (_isDosOrAmiga) {
		if (x >= _width) { // 140
			debugC(kDebugLevelPictures, "clipping %c from %d to %d", 'x', x, _width - 1);
			x = _width - 1; // 139
		}
	}

	return true;
}

/**
 * Gets the next y coordinate in the current picture instruction,
 * and clip it to the picture height. Many Winnie pictures contain
 * out of bounds coordinates and rely on this clipping.
 */
bool PictureMgr_Mickey_Winnie::getNextYCoordinate(byte &y) {
	if (!getNextParamByte(y)) {
		return false;
	}

	if (_isDosOrAmiga) {
		// note that this is a different clip than for the x coordinate
		if (y > _height) { // 159
			debugC(kDebugLevelPictures, "clipping %c from %d to %d", 'y', y, _height);
			y = _height; // 159
		}
	}

	return true;
}

/**
 * Validates picture coordinates and translates them to GfxMgr coordinates.
 *
 * This function applies the current picture object and validates that the
 * graphics coordinates are within GfxMgr's boundaries. Validation is necessary
 * because Winnie places tall objects at the bottom of the screen in several
 * rooms, and GfxMgr does not validate coordinates.
 */
bool PictureMgr_Mickey_Winnie::getGraphicsCoordinates(int16 &x, int16 &y) {
	// validate that the coordinates are within the picture's boundaries
	if (!PictureMgr::getGraphicsCoordinates(x, y)) {
		return false;
	}

	x += _xOffset;
	y += _yOffset;

	// validate that the offset coordinates are within the screen's boundaries
	return (x < SCRIPT_WIDTH && y < SCRIPT_HEIGHT);
}

} // End of namespace Agi