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 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "graphics/macgui/macwidget.h"
24
25 #include "director/director.h"
26 #include "director/castmember.h"
27 #include "director/frame.h"
28 #include "director/movie.h"
29 #include "director/score.h"
30 #include "director/sprite.h"
31 #include "director/lingo/lingo.h"
32 #include "director/lingo/lingo-object.h"
33
34 namespace Director {
35
Sprite(Frame * frame)36 Sprite::Sprite(Frame *frame) {
37 _frame = frame;
38 _score = _frame->getScore();
39 _movie = _score->getMovie();
40
41 _scriptId = CastMemberID(0, 0);
42 _colorcode = 0;
43 _blendAmount = 0;
44 _unk3 = 0;
45
46 _enabled = false;
47 _castId = CastMemberID(0, 0);
48 _pattern = 0;
49
50 _spriteType = kInactiveSprite;
51 _inkData = 0;
52 _ink = kInkTypeCopy;
53 _trails = 0;
54
55 _matte = nullptr;
56 _cast = nullptr;
57
58 _thickness = 0;
59 _width = 0;
60 _height = 0;
61 _moveable = false;
62 _editable = false;
63 _puppet = false;
64 _immediate = false;
65 _backColor = g_director->_wm->_colorWhite;
66 _foreColor = g_director->_wm->_colorBlack;
67
68 _blend = 0;
69
70 _volume = 0;
71 _stretch = 0;
72 }
73
~Sprite()74 Sprite::~Sprite() {
75 delete _matte;
76 }
77
isQDShape()78 bool Sprite::isQDShape() {
79 return _spriteType == kRectangleSprite ||
80 _spriteType == kRoundedRectangleSprite ||
81 _spriteType == kOvalSprite ||
82 _spriteType == kLineTopBottomSprite ||
83 _spriteType == kLineBottomTopSprite ||
84 _spriteType == kOutlinedRectangleSprite ||
85 _spriteType == kOutlinedRoundedRectangleSprite ||
86 _spriteType == kOutlinedOvalSprite ||
87 _spriteType == kThickLineSprite;
88 }
89
createQDMatte()90 void Sprite::createQDMatte() {
91 Graphics::ManagedSurface tmp;
92 tmp.create(_width, _height, g_director->_pixelformat);
93 tmp.clear(g_director->_wm->_colorWhite);
94
95 Common::Rect srcRect(_width, _height);
96
97 Common::Rect fillAreaRect((int)srcRect.width(), (int)srcRect.height());
98 Graphics::MacPlotData plotFill(&tmp, nullptr, &g_director->getPatterns(), getPattern(), 0, 0, 1, g_director->_wm->_colorBlack);
99
100 // it's the same for filled and outlined qd shape when we are using floodfill, so we use filled rect directly since it won't be affected by line size.
101 switch (_spriteType) {
102 case kOutlinedRectangleSprite:
103 case kRectangleSprite:
104 Graphics::drawFilledRect(fillAreaRect, g_director->_wm->_colorBlack, g_director->_wm->getDrawPixel(), &plotFill);
105 break;
106 case kOutlinedRoundedRectangleSprite:
107 case kRoundedRectangleSprite:
108 Graphics::drawRoundRect(fillAreaRect, 12, g_director->_wm->_colorBlack, true, g_director->_wm->getDrawPixel(), &plotFill);
109 break;
110 case kOutlinedOvalSprite:
111 case kOvalSprite:
112 Graphics::drawEllipse(fillAreaRect.left, fillAreaRect.top, fillAreaRect.right, fillAreaRect.bottom, g_director->_wm->_colorBlack, true, g_director->_wm->getDrawPixel(), &plotFill);
113 break;
114 case kLineBottomTopSprite:
115 case kLineTopBottomSprite:
116 warning("Sprite::createQDMatte doesn't support creating matte for type %d", _spriteType);
117 break;
118 default:
119 warning("Sprite::createQDMatte Expected shape type but got type %d", _spriteType);
120 }
121
122 Graphics::Surface surface;
123 surface.create(_width, _height, g_director->_pixelformat);
124 surface.copyFrom(tmp);
125
126 _matte = new Graphics::FloodFill(&surface, g_director->_wm->_colorWhite, 0, true);
127
128 for (int yy = 0; yy < surface.h; yy++) {
129 _matte->addSeed(0, yy);
130 _matte->addSeed(surface.w - 1, yy);
131 }
132
133 for (int xx = 0; xx < surface.w; xx++) {
134 _matte->addSeed(xx, 0);
135 _matte->addSeed(xx, surface.h - 1);
136 }
137
138 _matte->fillMask();
139 tmp.free();
140 surface.free();
141 }
142
getQDMatte()143 Graphics::Surface *Sprite::getQDMatte() {
144 if (!isQDShape() || _ink != kInkTypeMatte)
145 return nullptr;
146 if (!_matte)
147 createQDMatte();
148 return _matte ? _matte->getMask() : nullptr;
149 }
150
updateEditable()151 void Sprite::updateEditable() {
152 if (!_cast)
153 return;
154
155 if (!_puppet)
156 _editable = _editable || _cast->isEditable();
157 }
158
respondsToMouse()159 bool Sprite::respondsToMouse() {
160 if (_moveable)
161 return true;
162
163 if (_cast && _cast->_type == kCastButton)
164 return true;
165
166 ScriptContext *spriteScript = _movie->getScriptContext(kScoreScript, _scriptId);
167 if (spriteScript && (spriteScript->_eventHandlers.contains(kEventGeneric)
168 || spriteScript->_eventHandlers.contains(kEventMouseDown)
169 || spriteScript->_eventHandlers.contains(kEventMouseUp)))
170 return true;
171
172 ScriptContext *castScript = _movie->getScriptContext(kCastScript, _castId);
173 if (castScript && (castScript->_eventHandlers.contains(kEventMouseDown)
174 || castScript->_eventHandlers.contains(kEventMouseUp)))
175 return true;
176
177 return false;
178 }
179
isActive()180 bool Sprite::isActive() {
181 if (_cast && _cast->_type == kCastButton)
182 return true;
183
184 return _movie->getScriptContext(kScoreScript, _scriptId) != nullptr
185 || _movie->getScriptContext(kCastScript, _castId) != nullptr;
186 }
187
shouldHilite()188 bool Sprite::shouldHilite() {
189 if (!isActive())
190 return false;
191
192 if (_moveable)
193 return false;
194
195 if (_puppet)
196 return false;
197
198 if (_cast) {
199 // Restrict to bitmap cast members.
200 // Buttons also hilite on click, but they have their own check.
201 if (_cast->_type != kCastBitmap)
202 return false;
203
204 if (g_director->getVersion() >= 300) {
205 // The Auto Hilite flag was introduced in D3.
206
207 CastMemberInfo *castInfo = _cast->getInfo();
208 if (castInfo)
209 return castInfo->autoHilite;
210
211 // If there's no cast info, fall back to the old matte check.
212 // In D4 or above, there should always be a cast info,
213 // but in D3, it is not present unless you set a cast member's
214 // name, script, etc.
215 }
216 } else {
217 // QuickDraw shapes may also hilite on click.
218 if (!isQDShape())
219 return false;
220 }
221
222 return _ink == kInkTypeMatte;
223 }
224
getPattern()225 uint16 Sprite::getPattern() {
226 if (!_cast) {
227 if (isQDShape())
228 return _pattern;
229 } else if (_cast->_type == kCastShape) {
230 return ((ShapeCastMember *)_cast)->_pattern;
231 }
232
233 return 0;
234 }
235
setPattern(uint16 pattern)236 void Sprite::setPattern(uint16 pattern) {
237 switch (_spriteType) {
238 case kRectangleSprite:
239 case kRoundedRectangleSprite:
240 case kOvalSprite:
241 case kLineTopBottomSprite:
242 case kLineBottomTopSprite:
243 case kOutlinedRectangleSprite:
244 case kOutlinedRoundedRectangleSprite:
245 case kOutlinedOvalSprite:
246 _pattern = pattern;
247 break;
248
249 case kCastMemberSprite:
250 // TODO
251 warning("Sprite::setPattern(): kCastMemberSprite");
252 return;
253
254 default:
255 return;
256 }
257 }
258
checkSpriteType()259 bool Sprite::checkSpriteType() {
260 // check whether the sprite type match the cast type
261 // if it doesn't match, then we treat it as transparent
262 // this happens in warlock-mac data/stambul/c up
263 if (_spriteType == kBitmapSprite && _cast->_type != kCastBitmap) {
264 if (debugChannelSet(2, kDebugImages))
265 warning("Sprite::checkSpriteType: Didn't render sprite due to the sprite type mismatch with cast type");
266 return false;
267 }
268 return true;
269 }
270
setCast(CastMemberID memberID)271 void Sprite::setCast(CastMemberID memberID) {
272 /**
273 * There are two things we need to take into account here:
274 * 1. The cast member's type
275 * 2. The sprite's type
276 * If the two types do not align, the sprite should not render.
277 *
278 * Before D4, you needed to manually set a sprite's type along
279 * with its castNum.
280 *
281 * Starting in D4, setting a sprite's castNum also set its type
282 * to an appropriate default.
283 */
284
285 _castId = memberID;
286 _cast = _movie->getCastMember(_castId);
287 if (g_director->getVersion() >= 400)
288 _spriteType = kCastMemberSprite;
289
290 if (_cast) {
291 if (g_director->getVersion() >= 400) {
292 // Set the sprite type to be more specific ONLY for bitmap or text.
293 // Others just use the generic kCastMemberSprite in D4.
294 switch (_cast->_type) {
295 case kCastBitmap:
296 _spriteType = kBitmapSprite;
297 break;
298 case kCastText:
299 _spriteType = kTextSprite;
300 break;
301 default:
302 break;
303 }
304 }
305
306 // TODO: Respect sprite width/height settings. Need to determine how to read
307 // them properly.
308 Common::Rect dims = _cast->getInitialRect();
309 // strange logic here, need to be fixed
310 if (_cast->_type == kCastBitmap) {
311 // for the stretched sprites, we need the original size to get the correct bbox offset.
312 // there are two stretch situation here.
313 // 1. stretch happened when creating the widget, there is no lingo participated. we will use the original sprite size to create widget. check copyStretchImg
314 // 2. stretch set by lingo. this time we need to store the original dims because we will create the original sprite and stretch it when bliting. check inkBlitStretchSurface
315 if (!(_inkData & 0x80) || _stretch) {
316 _width = dims.width();
317 _height = dims.height();
318 }
319 } else if (_cast->_type != kCastShape && _cast->_type != kCastText) {
320 _width = dims.width();
321 _height = dims.height();
322 }
323
324 } else {
325 if (_castId.member != 0 && debugChannelSet(kDebugImages, 2))
326 warning("Sprite::setCast(): %s is null", memberID.asString().c_str());
327 }
328 }
329
330 } // End of namespace Director
331