1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1995-1997 Presto Studios, Inc.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "common/macresman.h"
27 #include "common/stream.h"
28
29 #include "pegasus/elements.h"
30 #include "pegasus/graphics.h"
31 #include "pegasus/surface.h"
32
33 namespace Pegasus {
34
DisplayElement(const DisplayElementID id)35 DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) {
36 _elementIsDisplaying = false;
37 _elementIsVisible = false;
38 _elementOrder = 0;
39 _triggeredElement = this;
40 _nextElement = 0;
41 }
42
~DisplayElement()43 DisplayElement::~DisplayElement() {
44 if (isDisplaying())
45 ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
46 }
47
setDisplayOrder(const DisplayOrder order)48 void DisplayElement::setDisplayOrder(const DisplayOrder order) {
49 if (_elementOrder != order) {
50 _elementOrder = order;
51 if (isDisplaying()) {
52 ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
53 ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
54 triggerRedraw();
55 }
56 }
57 }
58
startDisplaying()59 void DisplayElement::startDisplaying() {
60 if (!isDisplaying()) {
61 ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
62 triggerRedraw();
63 }
64 }
65
stopDisplaying()66 void DisplayElement::stopDisplaying() {
67 if (isDisplaying()) {
68 triggerRedraw();
69 ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
70 }
71 }
72
setBounds(const CoordType left,const CoordType top,const CoordType right,const CoordType bottom)73 void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) {
74 setBounds(Common::Rect(left, top, right, bottom));
75 }
76
getBounds(Common::Rect & r) const77 void DisplayElement::getBounds(Common::Rect &r) const {
78 r = _bounds;
79 }
80
sizeElement(const CoordType h,const CoordType v)81 void DisplayElement::sizeElement(const CoordType h, const CoordType v) {
82 Common::Rect newBounds = _bounds;
83 newBounds.right = _bounds.left + h;
84 newBounds.bottom = _bounds.top + v;
85 setBounds(newBounds);
86 }
87
moveElementTo(const CoordType h,const CoordType v)88 void DisplayElement::moveElementTo(const CoordType h, const CoordType v) {
89 Common::Rect newBounds = _bounds;
90 newBounds.moveTo(h, v);
91 setBounds(newBounds);
92 }
93
moveElement(const CoordType dh,const CoordType dv)94 void DisplayElement::moveElement(const CoordType dh, const CoordType dv) {
95 Common::Rect newBounds = _bounds;
96 newBounds.translate(dh, dv);
97 setBounds(newBounds);
98 }
99
getLocation(CoordType & h,CoordType & v) const100 void DisplayElement::getLocation(CoordType &h, CoordType &v) const {
101 h = _bounds.left;
102 v = _bounds.top;
103 }
104
centerElementAt(const CoordType h,const CoordType v)105 void DisplayElement::centerElementAt(const CoordType h, const CoordType v) {
106 Common::Rect newBounds = _bounds;
107 newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2));
108 setBounds(newBounds);
109 }
110
getCenter(CoordType & h,CoordType & v) const111 void DisplayElement::getCenter(CoordType &h, CoordType &v) const {
112 h = (_bounds.left + _bounds.right) / 2;
113 v = (_bounds.top + _bounds.bottom) / 2;
114 }
115
setBounds(const Common::Rect & r)116 void DisplayElement::setBounds(const Common::Rect &r) {
117 if (r != _bounds) {
118 triggerRedraw();
119 _bounds = r;
120 triggerRedraw();
121 }
122 }
123
hide()124 void DisplayElement::hide() {
125 if (_elementIsVisible) {
126 triggerRedraw();
127 _elementIsVisible = false;
128 }
129 }
130
show()131 void DisplayElement::show() {
132 if (!_elementIsVisible) {
133 _elementIsVisible = true;
134 triggerRedraw();
135 }
136 }
137
138 // Only invalidates this element's bounding rectangle if all these conditions are true:
139 // -- The triggered element is this element.
140 // -- The element is displaying on the display list.
141 // -- The element is visible.
142 // -- The element is part of the active layer OR is one of the reserved items.
triggerRedraw()143 void DisplayElement::triggerRedraw() {
144 GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
145
146 if (_triggeredElement == this) {
147 if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer()))
148 gfx->invalRect(_bounds);
149 } else {
150 _triggeredElement->triggerRedraw();
151 }
152 }
153
setTriggeredElement(DisplayElement * element)154 void DisplayElement::setTriggeredElement(DisplayElement *element) {
155 if (element)
156 _triggeredElement = element;
157 else
158 _triggeredElement = this;
159 }
160
validToDraw(DisplayOrder backLayer,DisplayOrder frontLayer)161 bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) {
162 return isDisplaying() && _elementIsVisible &&
163 (getObjectID() <= kHighestReservedElementID ||
164 (getDisplayOrder() >= backLayer &&
165 getDisplayOrder() <= frontLayer));
166 }
167
DropHighlight(const DisplayElementID id)168 DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) {
169 _highlightColor = 0;
170 _thickness = 2;
171 _cornerDiameter = 0;
172 }
173
draw(const Common::Rect &)174 void DropHighlight::draw(const Common::Rect &) {
175 Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
176
177 // Since this is only used in two different ways, I'm only
178 // going to implement it in those two ways. Deal with it.
179
180 Common::Rect rect = _bounds;
181 rect.grow(-_thickness);
182 screen->frameRect(rect, _highlightColor);
183 rect.grow(1);
184 screen->frameRect(rect, _highlightColor);
185
186 if (_cornerDiameter == 8 && _thickness == 4) {
187 rect.grow(1);
188 screen->frameRect(rect, _highlightColor);
189 screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor);
190 screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor);
191 screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor);
192 screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor);
193 }
194 }
195
IdlerAnimation(const DisplayElementID id)196 IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) {
197 _lastTime = 0xffffffff;
198 }
199
startDisplaying()200 void IdlerAnimation::startDisplaying() {
201 if (!isDisplaying()) {
202 Animation::startDisplaying();
203 startIdling();
204 }
205 }
206
stopDisplaying()207 void IdlerAnimation::stopDisplaying() {
208 if (isDisplaying()) {
209 Animation::stopDisplaying();
210 stopIdling();
211 }
212 }
213
useIdleTime()214 void IdlerAnimation::useIdleTime() {
215 uint32 currentTime = getTime();
216
217 if (currentTime != _lastTime) {
218 _lastTime = currentTime;
219 timeChanged(_lastTime);
220 }
221 }
222
timeChanged(const TimeValue)223 void IdlerAnimation::timeChanged(const TimeValue) {
224 triggerRedraw();
225 }
226
FrameSequence(const DisplayElementID id)227 FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) {
228 _duration = 0;
229 _currentFrameNum = 0;
230 _resFork = new Common::MacResManager();
231 _numFrames = 0;
232 }
233
~FrameSequence()234 FrameSequence::~FrameSequence() {
235 delete _resFork;
236 }
237
useFileName(const Common::String & fileName)238 void FrameSequence::useFileName(const Common::String &fileName) {
239 _resFork->open(fileName);
240 }
241
openFrameSequence()242 void FrameSequence::openFrameSequence() {
243 if (!_resFork->hasResFork())
244 return;
245
246 Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80);
247
248 if (!res)
249 return;
250
251 uint32 scale = res->readUint32BE();
252 _bounds.top = res->readUint16BE();
253 _bounds.left = res->readUint16BE();
254 _bounds.bottom = res->readUint16BE();
255 _bounds.right = res->readUint16BE();
256 _numFrames = res->readUint16BE();
257 _duration = 0;
258
259 _frameTimes.clear();
260 for (uint32 i = 0; i < _numFrames; i++) {
261 TimeValue time = res->readUint32BE();
262 _frameTimes.push_back(_duration);
263 _duration += time;
264 }
265
266 setScale(scale);
267 setSegment(0, _duration);
268 setTime(0);
269 _currentFrameNum = 0;
270 newFrame(_currentFrameNum);
271 triggerRedraw();
272
273 delete res;
274 }
275
closeFrameSequence()276 void FrameSequence::closeFrameSequence() {
277 stop();
278 _resFork->close();
279 _duration = 0;
280 _numFrames = 0;
281 _frameTimes.clear();
282 }
283
timeChanged(const TimeValue time)284 void FrameSequence::timeChanged(const TimeValue time) {
285 int16 frameNum = 0;
286 for (int16 i = _numFrames - 1; i >= 0; i--) {
287 if (_frameTimes[i] < time) {
288 frameNum = i;
289 break;
290 }
291 }
292
293 if (frameNum != _currentFrameNum) {
294 _currentFrameNum = frameNum;
295 newFrame(_currentFrameNum);
296 triggerRedraw();
297 }
298 }
299
setFrameNum(const int16 frameNum)300 void FrameSequence::setFrameNum(const int16 frameNum) {
301 int16 f = CLIP<int>(frameNum, 0, _numFrames);
302
303 if (_currentFrameNum != f) {
304 _currentFrameNum = f;
305 setTime(_frameTimes[f]);
306 newFrame(f);
307 triggerRedraw();
308 }
309 }
310
isSequenceOpen() const311 bool FrameSequence::isSequenceOpen() const {
312 return _numFrames != 0;
313 }
314
Sprite(const DisplayElementID id)315 Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) {
316 _numFrames = 0;
317 _currentFrameNum = 0xffffffff;
318 _currentFrame = 0;
319 }
320
~Sprite()321 Sprite::~Sprite() {
322 discardFrames();
323 }
324
discardFrames()325 void Sprite::discardFrames() {
326 if (!_frameArray.empty()) {
327 for (uint32 i = 0; i < _numFrames; i++) {
328 SpriteFrame *frame = _frameArray[i].frame;
329 frame->_referenceCount--;
330 if (frame->_referenceCount == 0)
331 delete frame;
332 }
333
334 _frameArray.clear();
335 _numFrames = 0;
336 _currentFrame = 0;
337 _currentFrameNum = 0xffffffff;
338 setBounds(0, 0, 0, 0);
339 }
340 }
341
addPICTResourceFrame(const ResIDType pictID,bool transparent,const CoordType left,const CoordType top)342 void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) {
343 SpriteFrame *frame = new SpriteFrame();
344 frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent);
345 addFrame(frame, left, top);
346 }
347
addFrame(SpriteFrame * frame,const CoordType left,const CoordType top)348 uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) {
349 SpriteFrameRec frameRecord;
350 frameRecord.frame = frame;
351 frameRecord.frameLeft = left;
352 frameRecord.frameTop = top;
353 _frameArray.push_back(frameRecord);
354 _numFrames++;
355 frame->_referenceCount++;
356
357 Common::Rect frameBounds;
358 frame->getSurfaceBounds(frameBounds);
359
360 // 9/3/96
361 // BB Should this be + left or - left?
362 frameBounds.moveTo(_bounds.left + left, _bounds.top + top);
363
364 frameBounds.extend(_bounds);
365
366 if (_bounds != frameBounds)
367 setBounds(frameBounds);
368
369 return _numFrames - 1;
370 }
371
removeFrame(const uint32 frameNum)372 void Sprite::removeFrame(const uint32 frameNum) {
373 _frameArray[frameNum].frame->_referenceCount--;
374 if (_frameArray[frameNum].frame->_referenceCount == 0)
375 delete _frameArray[frameNum].frame;
376
377 // Calculate the new bounds
378 Common::Rect frameBounds;
379 for (uint32 i = 0; i < _numFrames; i++) {
380 if (i == frameNum)
381 continue;
382
383 Common::Rect r;
384 _frameArray[i].frame->getSurfaceBounds(r);
385 r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop);
386 frameBounds.extend(r);
387 }
388
389 _frameArray.remove_at(frameNum);
390
391 frameBounds.moveTo(_bounds.left, _bounds.top);
392 setBounds(frameBounds);
393
394 if (_currentFrameNum == frameNum)
395 triggerRedraw();
396 else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum)
397 --_currentFrameNum;
398 }
399
setCurrentFrameIndex(const int32 frameNum)400 void Sprite::setCurrentFrameIndex(const int32 frameNum) {
401 if (frameNum < 0) {
402 if (_currentFrameNum != 0xffffffff) {
403 _currentFrameNum = 0xffffffff;
404 _currentFrame = 0;
405 triggerRedraw();
406 }
407 } else if (_numFrames > 0) {
408 uint32 f = frameNum % _numFrames;
409 if (f != _currentFrameNum) {
410 _currentFrameNum = f;
411 _currentFrame = &_frameArray[f];
412 triggerRedraw();
413 }
414 }
415 }
416
getFrame(const int32 index)417 SpriteFrame *Sprite::getFrame(const int32 index) {
418 if (index < 0 || (uint32)index >= _numFrames)
419 return 0;
420
421 return _frameArray[index].frame;
422 }
423
draw(const Common::Rect & r)424 void Sprite::draw(const Common::Rect &r) {
425 if (_currentFrame) {
426 Common::Rect frameBounds;
427 _currentFrame->frame->getSurfaceBounds(frameBounds);
428
429 frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop);
430 Common::Rect r1 = frameBounds.findIntersectingRect(r);
431
432 Common::Rect r2 = frameBounds;
433 r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop);
434
435 _currentFrame->frame->drawImage(r2, r1);
436 }
437 }
438
SpriteSequence(const DisplayElementID id,const DisplayElementID spriteID)439 SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) :
440 FrameSequence(id), _sprite(spriteID), _transparent(false) {
441 }
442
openFrameSequence()443 void SpriteSequence::openFrameSequence() {
444 if (!isSequenceOpen()) {
445 FrameSequence::openFrameSequence();
446
447 if (isSequenceOpen()) {
448 uint32 numFrames = getNumFrames();
449
450 for (uint32 i = 0; i < numFrames; ++i) {
451 SpriteFrame *frame = new SpriteFrame();
452 frame->initFromPICTResource(_resFork, i + 0x80, _transparent);
453 _sprite.addFrame(frame, 0, 0);
454 }
455
456 _sprite.setBounds(_bounds);
457 }
458 }
459 }
460
closeFrameSequence()461 void SpriteSequence::closeFrameSequence() {
462 if (isSequenceOpen()) {
463 FrameSequence::closeFrameSequence();
464 _sprite.discardFrames();
465 }
466 }
467
setBounds(const Common::Rect & bounds)468 void SpriteSequence::setBounds(const Common::Rect &bounds) {
469 FrameSequence::setBounds(bounds);
470 _sprite.setBounds(_bounds);
471 }
472
draw(const Common::Rect & r)473 void SpriteSequence::draw(const Common::Rect &r) {
474 _sprite.draw(r);
475 }
476
newFrame(const uint16 frame)477 void SpriteSequence::newFrame(const uint16 frame) {
478 _sprite.setCurrentFrameIndex(frame);
479 }
480
481 #define DRAW_PIXEL() \
482 if (bytesPerPixel == 2) \
483 *((uint16 *)dst) = black; \
484 else \
485 *((uint32 *)dst) = black; \
486 dst += bytesPerPixel
487
488 #define SKIP_PIXEL() \
489 dst += bytesPerPixel
490
draw(const Common::Rect & r)491 void ScreenDimmer::draw(const Common::Rect &r) {
492 // We're going to emulate QuickDraw's srcOr+gray mode here
493 // In this mode, every other y column is all black (odd-columns).
494 // Basically, every row does three black and then one transparent
495 // repeatedly.
496
497 // The output is identical to the original
498
499 uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0);
500 Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
501 byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel;
502
503 // We're currently doing it to the whole screen to simplify the code
504
505 for (int y = 0; y < 480; y++) {
506 byte *dst = (byte *)screen->getBasePtr(0, y);
507
508 for (int x = 0; x < 640; x += 4) {
509 if (y & 1) {
510 DRAW_PIXEL();
511 DRAW_PIXEL();
512 SKIP_PIXEL();
513 DRAW_PIXEL();
514 } else {
515 SKIP_PIXEL();
516 DRAW_PIXEL();
517 DRAW_PIXEL();
518 DRAW_PIXEL();
519 }
520 }
521 }
522 }
523
524 #undef DRAW_PIXEL
525 #undef SKIP_PIXEL
526
SoundLevel(const DisplayElementID id)527 SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) {
528 _soundLevel = 0;
529 }
530
incrementLevel()531 void SoundLevel::incrementLevel() {
532 if (_soundLevel < 12) {
533 _soundLevel++;
534 triggerRedraw();
535 }
536 }
537
decrementLevel()538 void SoundLevel::decrementLevel() {
539 if (_soundLevel > 0) {
540 _soundLevel--;
541 triggerRedraw();
542 }
543 }
544
getSoundLevel()545 uint16 SoundLevel::getSoundLevel() {
546 return CLIP<int>(_soundLevel * 22, 0, 256);
547 }
548
setSoundLevel(uint16 level)549 void SoundLevel::setSoundLevel(uint16 level) {
550 uint16 newLevel = (level + 21) / 22;
551
552 if (newLevel != _soundLevel) {
553 _soundLevel = newLevel;
554 triggerRedraw();
555 }
556 }
557
draw(const Common::Rect & r)558 void SoundLevel::draw(const Common::Rect &r) {
559 Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom);
560 levelRect = r.findIntersectingRect(levelRect);
561
562 if (!levelRect.isEmpty()) {
563 Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
564 screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0));
565 }
566 }
567
568 } // End of namespace Pegasus
569