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/macbutton.h"
24
25 #include "director/director.h"
26 #include "director/cast.h"
27 #include "director/castmember.h"
28 #include "director/cursor.h"
29 #include "director/channel.h"
30 #include "director/movie.h"
31 #include "director/sound.h"
32 #include "director/sprite.h"
33 #include "director/score.h"
34 #include "director/window.h"
35 #include "director/lingo/lingo.h"
36 #include "director/lingo/lingo-builtins.h"
37 #include "director/lingo/lingo-code.h"
38 #include "director/lingo/lingo-object.h"
39 #include "director/lingo/lingo-the.h"
40 namespace Director {
41
42 class Sprite;
43
44 TheEntity entities[] = {
45 { kTheActorList, "actorList", false, 400, false }, // D4 property
46 { kTheBeepOn, "beepOn", false, 200, false }, // D2 p
47 { kTheButtonStyle, "buttonStyle", false, 200, false }, // D2 p
48 { kTheCast, "cast", true, 200, false }, // D2
49 { kTheCastMembers, "castmembers", false, 300, false }, // D3
50 { kTheCenterStage, "centerStage", false, 200, false }, // D2 p
51 { kTheCheckBoxAccess, "checkBoxAccess", false, 200, false }, // D2 p
52 { kTheCheckBoxType, "checkBoxType", false, 200, false }, // D2 p
53 { kTheChunk, "chunk", true, 300, false }, // D3
54 { kTheClickLoc, "clickLoc", false, 400, true }, // D4 function
55 { kTheClickOn, "clickOn", false, 200, true }, // D2 f
56 { kTheColorDepth, "colorDepth", false, 200, false }, // D2 p
57 { kTheColorQD, "colorQD", false, 200, true }, // D2 f
58 { kTheCommandDown, "commandDown", false, 200, true }, // D2 f
59 { kTheControlDown, "controlDown", false, 200, true }, // D2 f
60 { kTheDate, "date", false, 300, true }, // D3 f
61 { kTheDoubleClick, "doubleClick", false, 200, true }, // D2 f
62 { kTheExitLock, "exitLock", false, 200, false }, // D2 p
63 { kTheField, "field", true, 300, false }, // D3
64 { kTheFixStageSize, "fixStageSize", false, 200, false }, // D2 p
65 { kTheFloatPrecision, "floatPrecision", false, 300, false }, // D3 p
66 { kTheFrame, "frame", false, 200, true }, // D2 f
67 { kTheFrameLabel, "frameLabel", false, 400, false }, // D4 p
68 { kTheFrameScript, "frameScript", false, 400, false }, // D4 p
69 { kTheFramePalette, "framePalette", false, 400, false }, // D4 p
70 { kTheFrameTempo, "frameTempo", false, 400, true }, // D4 f
71 { kTheFreeBlock, "freeBlock", false, 200, true }, // D2 f
72 { kTheFreeBytes, "freeBytes", false, 200, true }, // D2 f
73 { kTheFullColorPermit, "fullColorPermit", false, 200, false }, // D2 p
74 { kTheImageDirect, "imageDirect", false, 200, false }, // D2 p
75 { kTheItemDelimiter, "itemDelimiter", false, 400, false }, // D4 p
76 { kTheKey, "key", false, 200, true }, // D2 f
77 { kTheKeyCode, "keyCode", false, 200, true }, // D2 f
78 { kTheKeyDownScript, "keyDownScript", false, 200, false }, // D2 p
79 { kTheKeyUpScript, "keyUpScript", false, 400, false }, // D4 p
80 { kTheLabelList, "labelList", false, 300, true }, // D3 f
81 { kTheLastClick, "lastClick", false, 200, true }, // D2 f
82 { kTheLastEvent, "lastEvent", false, 200, true }, // D2 f
83 { kTheLastFrame, "lastFrame", false, 400, false }, // D4 p
84 { kTheLastKey, "lastKey", false, 200, true }, // D2 f
85 { kTheLastRoll, "lastRoll", false, 200, true }, // D2 f
86 { kTheMachineType, "machineType", false, 200, true }, // D2 f
87 { kTheMaxInteger, "maxInteger", false, 300, true }, // D3.1 f
88 { kTheMemorySize, "memorySize", false, 200, true }, // D2 f
89 { kTheMenu, "menu", true, 300, false }, // D3 p
90 { kTheMenuItem, "menuitem", true, 300, false }, // D3 p
91 { kTheMenuItems, "menuitems", false, 300, true }, // D3 f
92 { kTheMouseCast, "mouseCast", false, 300, true }, // D3 f
93 { kTheMouseChar, "mouseChar", false, 300, true }, // D3 f
94 { kTheMouseDown, "mouseDown", false, 200, true }, // D2 f
95 { kTheMouseDownScript, "mouseDownScript", false, 200, false }, // D2 p
96 { kTheMouseH, "mouseH", false, 200, true }, // D2 f
97 { kTheMouseItem, "mouseItem", false, 300, true }, // D3 f
98 { kTheMouseLine, "mouseLine", false, 300, true }, // D3 f
99 { kTheMouseUp, "mouseUp", false, 200, true }, // D2 f
100 { kTheMouseUpScript, "mouseUpScript", false, 200, false }, // D2 p
101 { kTheMouseV, "mouseV", false, 200, true }, // D2 f
102 { kTheMouseWord, "mouseWord", false, 300, true }, // D3 f
103 { kTheMovie, "movie", false, 200, true }, // D2 f
104 { kTheMovieFileFreeSize,"movieFileFreeSize",false, 400, true }, // D4 f
105 { kTheMovieFileSize, "movieFileSize", false, 400, true }, // D4 f
106 { kTheMovieName, "movieName", false, 400, true }, // D4 f
107 { kTheMoviePath, "moviePath", false, 400, true }, // D4 f
108 { kTheMultiSound, "multiSound", false, 300, true }, // D3.1 f
109 { kTheOptionDown, "optionDown", false, 200, true }, // D2 f
110 { kTheParamCount, "paramCount", false, 400, true }, // D4 f
111 { kThePathName, "pathName", false, 200, true }, // D2 f
112 { kThePauseState, "pauseState", false, 200, true }, // D2 f
113 { kThePerFrameHook, "perFrameHook", false, 200, false }, // D2 p
114 { kThePreloadEventAbort,"preloadEventAbort",false, 400, false }, // D4 p
115 { kThePreLoadRAM, "preLoadRAM", false, 400, false }, // D4 p
116 { kThePi, "pi", false, 400, true }, // D4 f
117 { kTheQuickTimePresent, "quickTimePresent", false, 300, true }, // D3.1 f
118 { kTheRandomSeed, "randomSeed", false, 400, false }, // D4 p
119 { kTheResult, "result", false, 200, true }, // D2 f
120 { kTheRightMouseDown, "rightMouseDown", false, 500, true }, // D5 f
121 { kTheRightMouseUp, "rightMouseUp", false, 500, true }, // D5 f
122 { kTheRomanLingo, "romanLingo", false, 300, false }, // D3.1 p
123 { kTheScummvmVersion, "scummvmVersion", false, 200, true }, // ScummVM only
124 { kTheSearchCurrentFolder,"searchCurrentFolder",false,400, true },// D4 f
125 { kTheSearchPath, "searchPath", false, 400, true }, // D4 f
126 { kTheSelection, "selection", false, 200, true }, // D2 f
127 { kTheSelEnd, "selEnd", false, 200, false }, // D2 p
128 { kTheSelStart, "selStart", false, 200, false }, // D2 p
129 { kTheShiftDown, "shiftDown", false, 200, true }, // D2 f
130 { kTheSoundEnabled, "soundEnabled", false, 200, false }, // D2 p
131 { kTheSoundEntity, "sound", true, 300, false }, // D3 p
132 { kTheSoundLevel, "soundLevel", false, 200, false }, // D2 p
133 { kTheSprite, "sprite", true, 200, false }, // D4 p
134 { kTheStage, "stage", false, 400, false }, // D4 p
135 { kTheStageBottom, "stageBottom", false, 200, true }, // D2 f
136 { kTheStageColor, "stageColor", false, 300, false }, // D3 p
137 { kTheStageLeft, "stageLeft", false, 200, true }, // D2 f
138 { kTheStageRight, "stageRight", false, 200, true }, // D2 f
139 { kTheStageTop, "stageTop", false, 200, true }, // D2 f
140 { kTheStillDown, "stillDown", false, 200, true }, // D2 f
141 { kTheSwitchColorDepth, "switchColorDepth", false, 200, false }, // D2 p
142 { kTheTicks, "ticks", false, 200, true }, // D2 f
143 { kTheTime, "time", false, 300, true }, // D3 f
144 { kTheTimeoutKeyDown, "timeoutKeyDown", false, 200, false }, // D2 p
145 { kTheTimeoutLapsed, "timeoutLapsed", false, 200, false }, // D2 p
146 { kTheTimeoutLength, "timeoutLength", false, 200, false }, // D2 p
147 { kTheTimeoutMouse, "timeoutMouse", false, 200, false }, // D2 p
148 { kTheTimeoutPlay, "timeoutPlay", false, 200, false }, // D2 p
149 { kTheTimeoutScript, "timeoutScript", false, 200, false }, // D2 p
150 { kTheTimer, "timer", false, 200, false }, // D2 p
151 { kTheTrace, "trace", false, 400, false }, // D4 p
152 { kTheTraceLoad, "traceLoad", false, 400, false }, // D4 p
153 { kTheTraceLogFile, "traceLogFile", false, 400, false }, // D4 p
154 { kTheUpdateMovieEnabled,"updateMovieEnabled",false,400, false },// D4 p
155 { kTheWindow, "window", true, 400, false }, // D4
156 { kTheWindowList, "windowList", false, 400, false }, // D4 p
157 { kTheNOEntity, NULL, false, 0, false }
158 };
159
160 TheEntityField fields[] = {
161 { kTheSprite, "backColor", kTheBackColor, 200 },// D2 p
162 { kTheSprite, "blend", kTheBlend, 400 },// D4 p
163 { kTheSprite, "bottom", kTheBottom, 200 },// D2 p
164 { kTheSprite, "castNum", kTheCastNum, 200 },// D2 p
165 { kTheSprite, "constraint", kTheConstraint, 200 },// D2 p
166 { kTheSprite, "cursor", kTheCursor, 200 },// D2 p
167 { kTheSprite, "editableText", kTheEditableText,400 },// D4 p
168 { kTheSprite, "foreColor", kTheForeColor, 200 },// D2 p
169 { kTheSprite, "height", kTheHeight, 200 },// D2 p
170 { kTheSprite, "immediate", kTheImmediate, 200 },// D2 p
171 { kTheSprite, "ink", kTheInk, 200 },// D2 p
172 { kTheSprite, "left", kTheLeft, 200 },// D2 p
173 { kTheSprite, "lineSize", kTheLineSize, 200 },// D2 p
174 { kTheSprite, "loc", kTheLoc, 400 },// D4 p ???
175 { kTheSprite, "locH", kTheLocH, 200 },// D2 p
176 { kTheSprite, "locV", kTheLocV, 200 },// D2 p
177 { kTheSprite, "moveableSprite",kTheMoveableSprite,400 },// D4 p
178 { kTheSprite, "pattern", kThePattern, 200 },// D2 p
179 { kTheSprite, "puppet", kThePuppet, 200 },// D2 p
180 { kTheSprite, "rect", kTheRect, 400 },// D4 p ???
181 { kTheSprite, "right", kTheRight, 200 },// D2 p
182 { kTheSprite, "scoreColor", kTheScoreColor, 400 },// D4 p
183 { kTheSprite, "scriptNum", kTheScriptNum, 400 },// D4 p
184 { kTheSprite, "stretch", kTheStretch, 200 },// D2 p
185 { kTheSprite, "top", kTheTop, 200 },// D2 p
186 { kTheSprite, "trails", kTheTrails, 300 },// D3.1 p
187 { kTheSprite, "type", kTheType, 200 },// D2 p
188 { kTheSprite, "visibility", kTheVisibility, 300 },// D3.1 p
189 { kTheSprite, "visible", kTheVisible, 400 },// D4 p
190 { kTheSprite, "width", kTheWidth, 200 },// D2 p
191
192 // Common cast fields
193 { kTheCast, "backColor", kTheBackColor, 400 },// D4 p
194 { kTheCast, "castType", kTheCastType, 400 },// D4 p
195 { kTheCast, "filename", kTheFileName, 400 },// D4 p
196 { kTheCast, "foreColor", kTheForeColor, 400 },// D4 p
197 { kTheCast, "height", kTheHeight, 400 },// D4 p
198 { kTheCast, "loaded", kTheLoaded, 400 },// D4 p
199 { kTheCast, "modified", kTheModified, 400 },// D4 p
200 { kTheCast, "name", kTheName, 300 },// D3 p
201 { kTheCast, "number", kTheNumber, 300 },// D3 p
202 { kTheCast, "rect", kTheRect, 400 },// D4 p
203 { kTheCast, "purgePriority",kThePurgePriority,400 },// D4 p // 0 Never purge, 1 Purge Last, 2 Purge next, 2 Purge normal
204 { kTheCast, "scriptText", kTheScriptText, 400 },// D4 p
205 { kTheCast, "size", kTheSize, 300 },// D3.1 p
206 { kTheCast, "width", kTheWidth, 400 },// D4 p
207
208 // Digital video fields
209 { kTheCast, "center", kTheCenter, 400 },// D4 p
210 { kTheCast, "controller", kTheController, 300 },// D3.1 p
211 { kTheCast, "crop", kTheCrop, 400 },// D4 p
212 { kTheCast, "directToStage",kTheDirectToStage,300 },// D3.1 p
213 { kTheCast, "duration", kTheDuration, 300 },// D3.1 p
214 { kTheCast, "frameRate", kTheFrameRate, 400 },// D4 p
215 { kTheCast, "loop", kTheLoop, 300 },// D3.1 p
216 { kTheSprite, "movieRate", kTheMovieRate, 300 },// D3.1 P
217 { kTheSprite, "movieTime", kTheMovieTime, 300 },// D3.1 P
218 { kTheCast, "pausedAtStart",kThePausedAtStart,400 },// D4 p
219 { kTheCast, "preLoad", kThePreLoad, 300 },// D3.1 p
220 { kTheCast, "sound", kTheSound, 300 },// D3.1 p // 0-1 off-on
221 { kTheSprite, "startTime", kTheStartTime, 300 },// D3.1 p
222 { kTheSprite, "stopTime", kTheStopTime, 300 },// D3.1 p
223 { kTheCast, "video", kTheVideo, 400 },// D4 p
224 { kTheSprite, "volume", kTheVolume, 300 },// D3.1 p
225
226 // Bitmap fields
227 { kTheCast, "depth", kTheDepth, 400 },// D4 p
228 { kTheCast, "regPoint", kTheRegPoint, 400 },// D4 p
229 { kTheCast, "palette", kThePalette, 400 },// D4 p
230 { kTheCast, "picture", kThePicture, 300 },// D3 p
231
232 // TextCastMember fields
233 { kTheCast, "hilite", kTheHilite, 200 },// D2 p
234 { kTheCast, "text", kTheText, 200 },// D2 p
235 { kTheCast, "textAlign", kTheTextAlign, 300 },// D3 p
236 { kTheCast, "textFont", kTheTextFont, 300 },// D3 p
237 { kTheCast, "textHeight", kTheTextHeight, 300 },// D3 p
238 { kTheCast, "textSize", kTheTextSize, 300 },// D3 p
239 { kTheCast, "textStyle", kTheTextStyle, 300 },// D3 p
240
241 // Field fields
242 { kTheField, "foreColor", kTheForeColor, 400 },// D4 p
243 { kTheField, "hilite", kTheHilite, 200 },// D2 p
244 { kTheField, "name", kTheName, 300 },// D3 p
245 { kTheField, "text", kTheText, 200 },// D2 p
246 { kTheField, "textAlign", kTheTextAlign, 300 },// D3 p
247 { kTheField, "textFont", kTheTextFont, 300 },// D3 p
248 { kTheField, "textHeight", kTheTextHeight, 300 },// D3 p
249 { kTheField, "textSize", kTheTextSize, 300 },// D3 p
250 { kTheField, "textStyle", kTheTextStyle, 300 },// D3 p
251
252 // Chunk fields
253 { kTheChunk, "foreColor", kTheForeColor, 400 },// D4 p
254 { kTheChunk, "textFont", kTheTextFont, 300 },// D3 p
255 { kTheChunk, "textHeight", kTheTextHeight, 300 },// D3 p
256 { kTheChunk, "textSize", kTheTextSize, 300 },// D3 p
257 { kTheChunk, "textStyle", kTheTextStyle, 300 },// D3 p
258
259 { kTheWindow, "drawRect", kTheDrawRect, 400 },// D4 p
260 { kTheWindow, "fileName", kTheFileName, 400 },// D4 p
261 { kTheWindow, "modal", kTheModal, 400 },// D4 p
262 { kTheWindow, "rect", kTheRect, 400 },// D4 p
263 { kTheWindow, "title", kTheTitle, 400 },// D4 p
264 { kTheWindow, "titleVisible", kTheTitleVisible,400 },// D4 p
265 { kTheWindow, "sourceRect", kTheSourceRect, 400 },// D4 p
266 { kTheWindow, "visible", kTheVisible, 400 },// D4 p
267 { kTheWindow, "windowType", kTheWindowType, 400 },// D4 p
268
269 { kTheMenuItem, "checkmark", kTheCheckMark, 300 },// D3 p
270 { kTheMenuItem, "enabled", kTheEnabled, 300 },// D3 p
271 { kTheMenuItem, "name", kTheName, 300 },// D3 p
272 { kTheMenuItem, "script", kTheScript, 300 },// D3 p
273 { kTheMenuItems,"number", kTheNumber, 300 },// D3 p // number of menuitems of menu <xx>
274
275 { kTheMenu, "name", kTheName, 300 },// D3 p
276
277 { kTheCastMembers, "number", kTheNumber, 300 },// D3 p
278
279 { kTheDate, "short", kTheShort, 300 },// D3 f
280 { kTheDate, "long", kTheLong, 300 },// D3 f
281 { kTheDate, "abbreviated", kTheAbbr, 300 },// D3 f
282 { kTheDate, "abbrev", kTheAbbr, 300 },// D3 f
283 { kTheDate, "abbr", kTheAbbr, 300 },// D3 f
284 { kTheTime, "short", kTheShort, 300 },// D3 f
285 { kTheTime, "long", kTheLong, 300 },// D3 f
286 { kTheTime, "abbreviated", kTheAbbr, 300 },// D3 f
287 { kTheTime, "abbrev", kTheAbbr, 300 },// D3 f
288 { kTheTime, "abbr", kTheAbbr, 300 },// D3 f
289
290 { kTheSoundEntity,"volume", kTheVolume, 300 },// D3 p
291
292 { kTheNOEntity, NULL, kTheNOField, 0 }
293 };
294
initTheEntities()295 void Lingo::initTheEntities() {
296 _objectEntityId = kTheObject;
297
298 TheEntity *e = entities;
299 _entityNames.resize(kTheMaxTheEntityType);
300
301 while (e->entity != kTheNOEntity) {
302 if (e->version <= _vm->getVersion()) {
303 _theEntities[e->name] = e;
304
305 _entityNames[e->entity] = e->name;
306 }
307
308 e++;
309 }
310
311 TheEntityField *f = fields;
312 _fieldNames.resize(kTheMaxTheFieldType);
313
314 while (f->entity != kTheNOEntity) {
315 if (f->version <= _vm->getVersion()) {
316 _theEntityFields[Common::String::format("%d%s", f->entity, f->name)] = f;
317
318 _fieldNames[f->field] = f->name;
319 }
320
321 // Store all fields for kTheObject
322 _theEntityFields[Common::String::format("%d%s", _objectEntityId, f->name)] = f;
323
324 f++;
325 }
326 }
327
cleanUpTheEntities()328 void Lingo::cleanUpTheEntities() {
329 _entityNames.clear();
330 _fieldNames.clear();
331 }
332
entity2str(int id)333 const char *Lingo::entity2str(int id) {
334 static char buf[20];
335
336 if (id && id < kTheMaxTheEntityType && !_entityNames[id].empty())
337 return _entityNames[id].c_str();
338
339 snprintf(buf, 19, "#%d", id);
340
341 return (const char *)buf;
342 }
field2str(int id)343 const char *Lingo::field2str(int id) {
344 static char buf[20];
345
346 if (id && id < kTheMaxTheFieldType && !_fieldNames[id].empty())
347 return _fieldNames[id].c_str();
348
349 snprintf(buf, 19, "#%d", id);
350
351 return (const char *)buf;
352 }
353
354 #define getTheEntitySTUB(entity) \
355 warning("Lingo::getTheEntity(): Unprocessed getting entity %s", entity2str(entity));
356
getTheEntity(int entity,Datum & id,int field)357 Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
358 if (debugChannelSet(3, kDebugLingoExec)) {
359 debugC(3, kDebugLingoExec, "Lingo::getTheEntity(%s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field));
360 }
361
362 Datum d;
363 Movie *movie = _vm->getCurrentMovie();
364
365 if (!movie) {
366 warning("Lingo::getTheEntity(): Movie is missing");
367 d.type = VOID;
368
369 return d;
370 }
371
372 LingoArchive *mainArchive = movie->getMainLingoArch();
373 Score *score = movie->getScore();
374
375 switch (entity) {
376 case kTheActorList:
377 getTheEntitySTUB(kTheActorList);
378 break;
379 case kTheBeepOn:
380 getTheEntitySTUB(kTheBeepOn);
381 break;
382 case kTheButtonStyle:
383 d.type = INT;
384 d.u.i = g_director->_wm->_mode & Graphics::kWMModeButtonDialogStyle;
385 break;
386 case kTheCast:
387 d = getTheCast(id, field);
388 break;
389 case kTheCastMembers:
390 warning("STUB: Lingo::getTheEntity(): Unprocessed getting field %s of entity %s", field2str(field), entity2str(entity));
391 break;
392 case kTheCenterStage:
393 d.type = INT;
394 d.u.i = g_director->_centerStage;
395 break;
396 case kTheCheckBoxAccess:
397 d.type = INT;
398 d.u.i = g_director->getCurrentMovie()->_checkBoxAccess;
399 break;
400 case kTheCheckBoxType:
401 d.type = INT;
402 d.u.i = g_director->getCurrentMovie()->_checkBoxType;
403 break;
404 case kTheChunk:
405 d = getTheChunk(id, field);
406 break;
407 case kTheClickLoc:
408 d.u.farr = new FArray;
409
410 d.u.farr->arr.push_back(movie->_lastClickPos.x);
411 d.u.farr->arr.push_back(movie->_lastClickPos.y);
412 d.type = POINT;
413 break;
414 case kTheClickOn:
415 d.type = INT;
416 d.u.i = movie->_currentClickOnSpriteId;
417 break;
418 case kTheColorDepth:
419 // bpp. 1, 2, 4, 8, 32
420 d.type = INT;
421 d.u.i = _vm->_colorDepth;
422 break;
423 case kTheColorQD:
424 d.type = INT;
425 d.u.i = 1;
426 break;
427 case kTheCommandDown:
428 d.type = INT;
429 d.u.i = (movie->_keyFlags & Common::KBD_META) ? 1 : 0;
430 break;
431 case kTheControlDown:
432 d.type = INT;
433 d.u.i = (movie->_keyFlags & Common::KBD_CTRL) ? 1 : 0;
434 break;
435 case kTheDate:
436 d = getTheDate(field);
437 break;
438 case kTheDoubleClick:
439 getTheEntitySTUB(kTheDoubleClick);
440 break;
441 case kTheExitLock:
442 getTheEntitySTUB(kTheExitLock);
443 break;
444 case kTheField:
445 d = getTheField(id, field);
446 break;
447 case kTheFixStageSize:
448 getTheEntitySTUB(kTheFixStageSize);
449 break;
450 case kTheFloatPrecision:
451 d.type = INT;
452 d.u.i = _floatPrecision;
453 break;
454 case kTheFrame:
455 d.type = INT;
456 d.u.i = score->getCurrentFrame();
457 break;
458 case kTheFrameLabel:
459 d.type = STRING;
460 d.u.s = score->getFrameLabel(score->getCurrentFrame());
461 break;
462 case kTheFrameScript:
463 getTheEntitySTUB(kTheFrameScript);
464 break;
465 case kTheFramePalette:
466 d.type = INT;
467 d.u.i = score->getCurrentPalette();
468 break;
469 case kTheFrameTempo:
470 d.type = INT;
471 d.u.i = score->_currentFrameRate;
472 break;
473 case kTheFreeBlock:
474 case kTheFreeBytes:
475 d.type = INT;
476 d.u.i = 32 * 1024 * 1024; // Let's have 32 Mbytes
477 break;
478 case kTheFullColorPermit:
479 d.type = INT;
480 d.u.i = 1; // We always allow it in ScummVM
481 break;
482 case kTheImageDirect:
483 d.type = INT;
484 d.u.i = 1; // We always allow it in ScummVM
485 break;
486 case kTheItemDelimiter:
487 {
488 Common::U32String ch(g_lingo->_itemDelimiter);
489 d.type = STRING;
490 d.u.s = new Common::String(ch, Common::kUtf8);
491 }
492 break;
493 case kTheKey:
494 d.type = STRING;
495 d.u.s = new Common::String(movie->_key);
496 break;
497 case kTheKeyCode:
498 d.type = INT;
499 d.u.i = movie->_keyCode;
500 break;
501 case kTheKeyDownScript:
502 d.type = STRING;
503 if (mainArchive->primaryEventHandlers.contains(kEventKeyDown))
504 d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventKeyDown]);
505 else
506 d.u.s = new Common::String();
507 break;
508 case kTheKeyUpScript:
509 d.type = STRING;
510 if (mainArchive->primaryEventHandlers.contains(kEventKeyUp))
511 d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventKeyUp]);
512 else
513 d.u.s = new Common::String();
514 break;
515 case kTheLabelList:
516 d.type = STRING;
517 d.u.s = score->getLabelList();
518 break;
519 case kTheLastClick:
520 d.type = INT;
521 d.u.i = _vm->getMacTicks() - movie->_lastClickTime;
522 break;
523 case kTheLastEvent:
524 d.type = INT;
525 d.u.i = _vm->getMacTicks() - movie->_lastEventTime;
526 break;
527 case kTheLastFrame:
528 d.type = INT;
529 d.u.i = score->_frames.size() - 1;
530 break;
531 case kTheLastKey:
532 d.type = INT;
533 d.u.i = _vm->getMacTicks() - movie->_lastKeyTime;
534 break;
535 case kTheLastRoll:
536 d.type = INT;
537 d.u.i = _vm->getMacTicks() - movie->_lastRollTime;
538 break;
539 case kTheMachineType:
540 // 1 - Macintosh 512Ke D2
541 // 2 - Macintosh Plus D2
542 // 3 - Macintosh SE D2
543 // 4 - Macintosh II D2
544 // 5 - Macintosh IIx D2
545 // 6 - Macintosh IIcx D2
546 // 7 - Macintosh SE/30 D2
547 // 8 - Macintosh Portable D2
548 // 9 - Macintosh IIci D2
549 // 11 - Macintosh IIfx D3
550 // 15 - Macintosh Classic D3
551 // 16 - Macintosh IIsi D3
552 // 17 - Macintosh LC D3
553 // 18 - Macintosh Quadra 900 D3
554 // 19 - PowerBook 170 D3
555 // 20 - Macintosh Quadra 700 D3
556 // 21 - Classic II D3
557 // 22 - PowerBook 100 D3
558 // 23 - PowerBook 140 D3
559 // 24 - Macintosh Quadra 950 D4
560 // 25 - Macintosh LCIII D4
561 // 27 - PowerBook Duo 210 D4
562 // 28 - Macintosh Centris 650 D4
563 // 30 - PowerBook Duo 230 D4
564 // 31 - PowerBook 180 D4
565 // 32 - PowerBook 160 D4
566 // 33 - Macintosh Quadra 800 D4
567 // 35 - Macintosh LC II D4
568 // 42 - Macintosh IIvi D4
569 // 45 - Power Macintosh 7100/70 D5
570 // 46 - Macintosh IIvx D4
571 // 47 - Macintosh Color Classic D4
572 // 48 - PowerBook 165c D4
573 // 50 - Macintosh Centris 610 D4
574 // 52 - PowerBook 145 D4
575 // 53 - PowerComputing 8100/100 D5
576 // 70 - PowerBook 540C D6 // "Director 6 Demystified" p.818
577 // 73 - Power Macintosh 6100/60 D5
578 // 76 - Macintosh Quadra 840av D4
579 // 256 - IBM PC-type machine D3
580 d.type = INT;
581 d.u.i = _vm->_machineType;
582 break;
583 case kTheMaxInteger:
584 d.type = INT;
585 d.u.i = 2147483647; // (2^31)-1 [max 32bit signed integer]
586 break;
587 case kTheMemorySize:
588 d.type = INT;
589 d.u.i = 32 * 1024 * 1024; // Let's have 32 Mbytes
590 break;
591 case kTheMenu:
592 getTheEntitySTUB(kTheMenu);
593 break;
594 case kTheMenuItem:
595 getTheEntitySTUB(kTheMenuItem);
596 break;
597 case kTheMenuItems:
598 getTheEntitySTUB(kTheMenuItems);
599 break;
600 case kTheMouseCast:
601 {
602 // TODO: How is this handled with multiple casts in D5?
603 Common::Point pos = g_director->getCurrentWindow()->getMousePos();
604 uint16 spriteId = score->getSpriteIDFromPos(pos);
605 d.type = INT;
606 d.u.i = score->getSpriteById(spriteId)->_castId.member;
607 if (d.u.i == 0)
608 d.u.i = -1;
609 }
610 break;
611 case kTheMouseChar:
612 {
613 // maybe a better handling is iterate channels and check the text sprite that enclose the cursor
614 Common::Point pos = g_director->getCurrentWindow()->getMousePos();
615 uint16 spriteId = score->getSpriteIDFromPos(pos);
616 Channel *ch = score->getChannelById(spriteId);
617 d.u.i = ch->getMouseChar(pos.x, pos.y);
618 d.type = INT;
619 }
620 break;
621 case kTheMouseDown:
622 d.type = INT;
623 d.u.i = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_LEFT | 1 << Common::MOUSE_BUTTON_RIGHT) ? 1 : 0;
624 break;
625 case kTheMouseDownScript:
626 d.type = STRING;
627 if (mainArchive->primaryEventHandlers.contains(kEventMouseDown))
628 d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventMouseDown]);
629 else
630 d.u.s = new Common::String();
631 break;
632 case kTheMouseH:
633 d.type = INT;
634 d.u.i = g_director->getCurrentWindow()->getMousePos().x;
635 break;
636 case kTheMouseItem:
637 {
638 Common::Point pos = g_director->getCurrentWindow()->getMousePos();
639 uint16 spriteId = score->getSpriteIDFromPos(pos);
640 Channel *ch = score->getChannelById(spriteId);
641 d.u.i = ch->getMouseItem(pos.x, pos.y);
642 d.type = INT;
643 }
644 break;
645 case kTheMouseLine:
646 {
647 Common::Point pos = g_director->getCurrentWindow()->getMousePos();
648 uint16 spriteId = score->getSpriteIDFromPos(pos);
649 Channel *ch = score->getChannelById(spriteId);
650 d.u.i = ch->getMouseLine(pos.x, pos.y);
651 d.type = INT;
652 }
653 break;
654 case kTheMouseUp:
655 d.type = INT;
656 d.u.i = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_LEFT | 1 << Common::MOUSE_BUTTON_RIGHT) ? 0 : 1;
657 break;
658 case kTheMouseUpScript:
659 d.type = STRING;
660 if (mainArchive->primaryEventHandlers.contains(kEventMouseUp))
661 d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventMouseUp]);
662 else
663 d.u.s = new Common::String();
664 break;
665 case kTheMouseV:
666 d.type = INT;
667 d.u.i = g_director->getCurrentWindow()->getMousePos().y;
668 break;
669 case kTheMouseWord:
670 {
671 // same issue as MouseChar, check MouseChar above
672 Common::Point pos = g_director->getCurrentWindow()->getMousePos();
673 uint16 spriteId = score->getSpriteIDFromPos(pos);
674 Channel *ch = score->getChannelById(spriteId);
675 d.u.i = ch->getMouseWord(pos.x, pos.y);
676 d.type = INT;
677 }
678 break;
679 case kTheMovie:
680 case kTheMovieName:
681 d.type = STRING;
682 d.u.s = new Common::String(movie->getMacName());
683 break;
684 case kTheMovieFileFreeSize:
685 d.type = INT;
686 d.u.i = 0; // Let's pretend the movie is compactified
687 break;
688 case kTheMovieFileSize:
689 d.type = INT;
690 d.u.i = movie->getArchive()->getFileSize();
691 break;
692 case kTheMoviePath:
693 case kThePathName:
694 d.type = STRING;
695 d.u.s = new Common::String(_vm->getCurrentPath());
696 break;
697 case kTheMultiSound:
698 // We always support multiple sound channels!
699 d.type = INT;
700 d.u.i = 1;
701 break;
702 case kTheOptionDown:
703 d.type = INT;
704 d.u.i = (movie->_keyFlags & Common::KBD_ALT) ? 1 : 0;
705 break;
706 case kThePauseState:
707 getTheEntitySTUB(kThePauseState);
708 break;
709 case kThePerFrameHook:
710 d = _perFrameHook;
711 break;
712 case kThePreloadEventAbort:
713 getTheEntitySTUB(kThePreloadEventAbort);
714 break;
715 case kThePreLoadRAM:
716 d.u.i = 0; // We always have unlimited RAM
717 break;
718 case kThePi:
719 d.type = FLOAT;
720 d.u.f = M_PI;
721 break;
722 case kTheQuickTimePresent:
723 // QuickTime is always present for scummvm
724 d.type = INT;
725 d.u.i = 1;
726 break;
727 case kTheRandomSeed:
728 d.type = INT;
729 d.u.i = g_director->_rnd.getSeed();
730 break;
731 case kTheResult:
732 d = g_lingo->_theResult;
733 break;
734 case kTheRightMouseDown:
735 getTheEntitySTUB(kTheRightMouseDown);
736 break;
737 case kTheRightMouseUp:
738 getTheEntitySTUB(kTheRightMouseUp);
739 break;
740 case kTheRomanLingo:
741 getTheEntitySTUB(kTheRomanLingo);
742 break;
743 case kTheScummvmVersion:
744 d.type = INT;
745 d.u.i = _vm->getVersion();
746 break;
747 case kTheSearchCurrentFolder:
748 getTheEntitySTUB(kTheSearchCurrentFolder);
749 break;
750 case kTheSearchPath:
751 getTheEntitySTUB(kTheSearchPath);
752 break;
753 case kTheSelection:
754 if (movie->_currentEditableTextChannel) {
755 Channel *channel = score->_channels[movie->_currentEditableTextChannel];
756
757 if (channel->_widget) {
758 d.type = STRING;
759 d.u.s = new Common::String(Common::convertFromU32String(((Graphics::MacText *)channel->_widget)->getSelection()));
760 }
761 }
762 break;
763 case kTheSelEnd:
764 case kTheSelStart:
765 if (movie->_currentEditableTextChannel) {
766 Channel *channel = score->_channels[movie->_currentEditableTextChannel];
767
768 if (channel->_widget) {
769 d.type = INT;
770 d.u.i = ((Graphics::MacText *)channel->_widget)->getSelectionIndex(entity == kTheSelStart);
771 }
772 }
773 break;
774 case kTheShiftDown:
775 d.type = INT;
776 d.u.i = (movie->_keyFlags & Common::KBD_SHIFT) ? 1 : 0;
777 break;
778 case kTheSoundEnabled:
779 d.type = INT;
780 d.u.i = _vm->getCurrentWindow()->getSoundManager()->getSoundEnabled();
781 break;
782 case kTheSoundEntity:
783 {
784 switch (field) {
785 case kTheVolume:
786 {
787 SoundChannel *chan = _vm->getCurrentWindow()->getSoundManager()->getChannel(id.asInt());
788 if (chan) {
789 d.type = INT;
790 d.u.i = (int)chan->volume;
791 }
792 }
793 break;
794 default:
795 warning("Lingo::getTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
796 break;
797 }
798 }
799 break;
800 case kTheSoundLevel:
801 // getting sound level of channel 1, maybe need to be amended in higher version
802 d.type = INT;
803 d.u.i = _vm->getCurrentWindow()->getSoundManager()->getSoundLevel(1);
804 break;
805 case kTheSprite:
806 d = getTheSprite(id, field);
807 break;
808 case kTheStage:
809 d = _vm->getStage();
810 break;
811 case kTheStageBottom:
812 d.type = INT;
813 d.u.i = movie->_movieRect.bottom;
814 break;
815 case kTheStageColor:
816 getTheEntitySTUB(kTheStageColor);
817 break;
818 case kTheStageLeft:
819 d.type = INT;
820 d.u.i = movie->_movieRect.left;
821 break;
822 case kTheStageRight:
823 d.type = INT;
824 d.u.i = movie->_movieRect.right;
825 break;
826 case kTheStageTop:
827 d.type = INT;
828 d.u.i = movie->_movieRect.top;
829 break;
830 case kTheStillDown:
831 d.type = INT;
832 d.u.i = _vm->_wm->_mouseDown;
833 break;
834 case kTheSwitchColorDepth:
835 getTheEntitySTUB(kTheSwitchColorDepth);
836 break;
837 case kTheTicks:
838 d.type = INT;
839 d.u.i = _vm->getMacTicks();
840 break;
841 case kTheTime:
842 d = getTheTime(field);
843 break;
844 case kTheTimeoutKeyDown:
845 d.type = INT;
846 d.u.i = g_director->getCurrentMovie()->_timeOutKeyDown;
847 break;
848 case kTheTimeoutLapsed:
849 d.type = INT;
850 d.u.i = _vm->getMacTicks() - g_director->getCurrentMovie()->_lastTimeOut;
851 break;
852 case kTheTimeoutLength:
853 d.type = INT;
854 d.u.i = g_director->getCurrentMovie()->_timeOutLength;
855 break;
856 case kTheTimeoutMouse:
857 d.type = INT;
858 d.u.i = g_director->getCurrentMovie()->_timeOutMouse;
859 break;
860 case kTheTimeoutPlay:
861 d.type = INT;
862 d.u.i = g_director->getCurrentMovie()->_timeOutPlay;
863 break;
864 case kTheTimeoutScript:
865 d.type = STRING;
866 if (mainArchive->primaryEventHandlers.contains(kEventTimeout))
867 d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventTimeout]);
868 else
869 d.u.s = new Common::String();
870 break;
871 case kTheTimer:
872 d.type = INT;
873 d.u.i = _vm->getMacTicks() - movie->_lastTimerReset;
874 break;
875 case kTheTrace:
876 getTheEntitySTUB(kTheTrace);
877 break;
878 case kTheTraceLoad:
879 getTheEntitySTUB(kTheTraceLoad);
880 break;
881 case kTheTraceLogFile:
882 getTheEntitySTUB(kTheTraceLogFile);
883 break;
884 case kTheUpdateMovieEnabled:
885 getTheEntitySTUB(kTheUpdateMovieEnabled);
886 break;
887 case kTheWindow:
888 g_lingo->push(id);
889 LB::b_window(1);
890 d = g_lingo->pop().u.obj->getField(field);
891 break;
892 case kTheWindowList:
893 d = g_lingo->_windowList;
894 break;
895 default:
896 warning("Lingo::getTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
897 break;
898 }
899
900 return d;
901 }
902
903 #define setTheEntitySTUB(entity) \
904 warning("Lingo::setTheEntity(): Unprocessed setting entity %s", entity2str(entity));
905
906 #define setTheEntityReadOnly(entity) \
907 warning("Lingo::setTheEntity: Attempt to set read-only entity %s", entity2str(entity));
908
setTheEntity(int entity,Datum & id,int field,Datum & d)909 void Lingo::setTheEntity(int entity, Datum &id, int field, Datum &d) {
910 if (debugChannelSet(3, kDebugLingoExec)) {
911 debugC(3, kDebugLingoExec, "Lingo::setTheEntity(%s, %s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field), d.asString(true).c_str());
912 }
913
914 Movie *movie = _vm->getCurrentMovie();
915 Score *score = movie->getScore();
916
917 switch (entity) {
918 case kTheActorList:
919 setTheEntitySTUB(kTheActorList);
920 break;
921 case kTheBeepOn:
922 setTheEntitySTUB(kTheBeepOn);
923 break;
924 case kTheButtonStyle:
925 if (d.asInt())
926 g_director->_wm->_mode = Director::wmMode | Graphics::kWMModeButtonDialogStyle;
927 else
928 g_director->_wm->_mode = Director::wmMode;
929 break;
930 case kTheCast:
931 setTheCast(id, field, d);
932 break;
933 case kTheCenterStage:
934 g_director->_centerStage = d.asInt();
935 break;
936 case kTheCheckBoxAccess:
937 g_director->getCurrentMovie()->_checkBoxAccess = d.asInt();
938 break;
939 case kTheCheckBoxType:
940 g_director->getCurrentMovie()->_checkBoxType = d.asInt();
941 break;
942 case kTheChunk:
943 setTheChunk(id, field, d);
944 break;
945 case kTheColorDepth:
946 _vm->_colorDepth = d.asInt();
947
948 // bpp. 1, 2, 4, 8, 32
949 warning("STUB: Lingo::setTheEntity(): Set color depth to %d", _vm->_colorDepth);
950 break;
951 case kTheExitLock:
952 setTheEntitySTUB(kTheExitLock);
953 break;
954 case kTheFixStageSize:
955 setTheEntitySTUB(kTheFixStageSize);
956 break;
957 case kTheField:
958 setTheField(id, field, d);
959 break;
960 case kTheFloatPrecision:
961 _floatPrecision = d.asInt();
962 _floatPrecision = MAX(0, MIN(_floatPrecision, 19)); // 0 to 19
963 _floatPrecisionFormat = Common::String::format("%%.%df", _floatPrecision);
964 break;
965 case kTheFrameLabel:
966 setTheEntityReadOnly(kTheFrameLabel);
967 break;
968 case kTheFrameScript:
969 setTheEntitySTUB(kTheFrameScript);
970 break;
971 case kTheFramePalette:
972 setTheEntityReadOnly(kTheFramePalette);
973 break;
974 case kTheFrameTempo:
975 setTheEntityReadOnly(kTheFramePalette);
976 break;
977 case kTheFullColorPermit:
978 // No op in ScummVM. We always allow it
979 break;
980 case kTheImageDirect:
981 // No op in ScummVM. We always allow it
982 break;
983 case kTheItemDelimiter:
984 if (d.asString().size() == 0)
985 g_lingo->_itemDelimiter = 0;
986 else
987 g_lingo->_itemDelimiter = d.asString().decode(Common::kUtf8)[0];
988 break;
989 case kTheKeyDownScript:
990 movie->setPrimaryEventHandler(kEventKeyDown, d.asString());
991 break;
992 case kTheKeyUpScript:
993 movie->setPrimaryEventHandler(kEventKeyUp, d.asString());
994 break;
995 case kTheMenu:
996 setTheEntitySTUB(kTheMenu);
997 break;
998 case kTheMenuItem:
999 setTheEntitySTUB(kTheMenuItem);
1000 break;
1001 case kTheMouseDownScript:
1002 movie->setPrimaryEventHandler(kEventMouseDown, d.asString());
1003 break;
1004 case kTheMouseUpScript:
1005 movie->setPrimaryEventHandler(kEventMouseUp, d.asString());
1006 break;
1007 case kThePerFrameHook:
1008 _perFrameHook = d;
1009 break;
1010 case kThePreloadEventAbort:
1011 setTheEntitySTUB(kThePreloadEventAbort);
1012 break;
1013 case kThePreLoadRAM:
1014 // We always have the unlimited RAM, ignore
1015 break;
1016 case kTheRandomSeed:
1017 g_director->_rnd.setSeed(d.asInt());
1018 break;
1019 case kTheRomanLingo:
1020 setTheEntitySTUB(kTheRomanLingo);
1021 break;
1022 case kTheScummvmVersion:
1023 // Allow director version change: used for testing version differences via the lingo tests.
1024 _vm->setVersion(d.asInt());
1025 break;
1026 case kTheSelEnd:
1027 g_director->getCurrentMovie()->_selEnd = d.asInt();
1028 if (movie->_currentEditableTextChannel != 0) {
1029 Channel *channel = score->getChannelById(movie->_currentEditableTextChannel);
1030
1031 if (channel->_widget)
1032 (((Graphics::MacText *)channel->_widget)->setSelection(d.asInt(), false));
1033 }
1034 break;
1035 case kTheSelStart:
1036 g_director->getCurrentMovie()->_selStart = d.asInt();
1037 if (movie->_currentEditableTextChannel != 0) {
1038 Channel *channel = score->getChannelById(movie->_currentEditableTextChannel);
1039
1040 if (channel->_widget)
1041 (((Graphics::MacText *)channel->_widget)->setSelection(d.asInt(), true));
1042 }
1043 break;
1044 case kTheSoundEnabled:
1045 _vm->getCurrentWindow()->getSoundManager()->setSoundEnabled((bool)d.asInt());
1046 break;
1047 case kTheSoundEntity:
1048 {
1049 switch (field) {
1050 case kTheVolume:
1051 {
1052 SoundChannel *chan = _vm->getCurrentWindow()->getSoundManager()->getChannel(id.asInt());
1053 if (chan) {
1054 chan->volume = (byte)d.asInt();
1055 }
1056 }
1057 break;
1058 default:
1059 warning("Lingo::setTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1060 break;
1061 }
1062 }
1063 break;
1064 case kTheSoundLevel:
1065 // setting all of the channel for now
1066 _vm->getCurrentWindow()->getSoundManager()->setSouldLevel(-1, d.asInt());
1067 break;
1068 case kTheSprite:
1069 setTheSprite(id, field, d);
1070 break;
1071 case kTheStage:
1072 setTheEntitySTUB(kTheStage);
1073 break;
1074 case kTheStageColor:
1075 g_director->getCurrentWindow()->setStageColor(d.asInt());
1076
1077 // Queue an immediate update of the stage
1078 if (!score->getNextFrame())
1079 score->setCurrentFrame(score->getCurrentFrame());
1080 break;
1081 case kTheSwitchColorDepth:
1082 setTheEntitySTUB(kTheSwitchColorDepth);
1083 break;
1084 case kTheTimeoutKeyDown:
1085 g_director->getCurrentMovie()->_timeOutKeyDown = d.asInt();
1086 break;
1087 case kTheTimeoutLapsed:
1088 // timeOutLapsed can be set in D4, but can't in D3. see D3.1 interactivity manual p312 and D4 dictionary p296.
1089 setTheEntitySTUB(kTheTimeoutLapsed);
1090 break;
1091 case kTheTimeoutLength:
1092 g_director->getCurrentMovie()->_timeOutLength = d.asInt();
1093 break;
1094 case kTheTimeoutMouse:
1095 g_director->getCurrentMovie()->_timeOutMouse = d.asInt();
1096 break;
1097 case kTheTimeoutPlay:
1098 g_director->getCurrentMovie()->_timeOutPlay = d.asInt();
1099 break;
1100 case kTheTimeoutScript:
1101 movie->setPrimaryEventHandler(kEventTimeout, d.asString());
1102 break;
1103 case kTheTimer:
1104 // so value of the timer would be d.asInt()
1105 movie->_lastTimerReset = _vm->getMacTicks() - d.asInt();
1106 break;
1107 case kTheTrace:
1108 setTheEntitySTUB(kTheTrace);
1109 break;
1110 case kTheTraceLoad:
1111 setTheEntitySTUB(kTheTraceLoad);
1112 break;
1113 case kTheTraceLogFile:
1114 setTheEntitySTUB(kTheTraceLogFile);
1115 break;
1116 case kTheUpdateMovieEnabled:
1117 setTheEntitySTUB(kTheUpdateMovieEnabled);
1118 break;
1119 case kTheWindow:
1120 g_lingo->push(id);
1121 LB::b_window(1);
1122 g_lingo->pop().u.obj->setField(field, d);
1123 break;
1124 case kTheWindowList:
1125 if (d.type == ARRAY) {
1126 g_lingo->_windowList = d;
1127 } else {
1128 warning("Lingo::setTheEntity(): kTheWindowList must be a list");
1129 }
1130 break;
1131 default:
1132 warning("Lingo::setTheEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1133 }
1134 }
1135
getTheMenuItemEntity(int entity,Datum & menuId,int field,Datum & menuItemId)1136 Datum Lingo::getTheMenuItemEntity(int entity, Datum &menuId, int field, Datum &menuItemId) {
1137 Datum d;
1138
1139 switch(field) {
1140 case kTheCheckMark:
1141 if (menuId.type == STRING && menuItemId.type == STRING) {
1142 d.type = INT;
1143 d.u.i = g_director->_wm->getMenuItemCheckMark(menuId.asString(), menuItemId.asString());
1144 } else if (menuId.type == INT && menuItemId.type == INT) {
1145 d.type = INT;
1146 d.u.i = g_director->_wm->getMenuItemCheckMark(menuId.asInt(), menuItemId.asInt());
1147 } else
1148 warning("Lingo::getTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1149 break;
1150 case kTheEnabled:
1151 if (menuId.type == STRING && menuItemId.type == STRING) {
1152 d.type = INT;
1153 d.u.i = g_director->_wm->getMenuItemEnabled(menuId.asString(), menuItemId.asString());
1154 } else if (menuId.type == INT && menuItemId.type == INT) {
1155 d.type = INT;
1156 d.u.i = g_director->_wm->getMenuItemEnabled(menuId.asInt(), menuItemId.asInt());
1157 } else
1158 warning("Lingo::getTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1159 break;
1160 case kTheName:
1161 if (menuId.type == STRING && menuItemId.type == STRING) {
1162 d.type = STRING;
1163 d.u.s = new Common::String;
1164 *(d.u.s) = g_director->_wm->getMenuItemName(menuId.asString(), menuItemId.asString());
1165 } else if (menuId.type == INT && menuItemId.type == INT) {
1166 d.type = STRING;
1167 d.u.s = new Common::String;
1168 *(d.u.s) = g_director->_wm->getMenuItemName(menuId.asInt(), menuItemId.asInt());
1169 } else
1170 warning("Lingo::getTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1171 break;
1172 case kTheScript:
1173 if (menuId.type == STRING && menuItemId.type == STRING) {
1174 d.type = INT;
1175 d.u.i = g_director->_wm->getMenuItemAction(menuId.asString(), menuItemId.asString());
1176 } else if (menuId.type == INT && menuItemId.type == INT) {
1177 d.type = INT;
1178 d.u.i = g_director->_wm->getMenuItemAction(menuId.asInt(), menuItemId.asInt());
1179 } else
1180 warning("Lingo::getTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1181 break;
1182 default:
1183 warning("Lingo::getTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1184 break;
1185 }
1186
1187 return d;
1188 }
1189
setTheMenuItemEntity(int entity,Datum & menuId,int field,Datum & menuItemId,Datum & d)1190 void Lingo::setTheMenuItemEntity(int entity, Datum &menuId, int field, Datum &menuItemId, Datum &d) {
1191 switch(field) {
1192 case kTheCheckMark:
1193 if (menuId.type == STRING && menuItemId.type == STRING)
1194 g_director->_wm->setMenuItemCheckMark(menuId.asString(), menuItemId.asString(), d.asInt());
1195 else if (menuId.type == INT && menuItemId.type == INT)
1196 g_director->_wm->setMenuItemCheckMark(menuId.asInt() - 1, menuItemId.asInt() - 1, d.asInt());
1197 else
1198 warning("Lingo::setTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1199 break;
1200 case kTheEnabled:
1201 if (menuId.type == STRING && menuItemId.type == STRING)
1202 g_director->_wm->setMenuItemEnabled(menuId.asString(), menuItemId.asString(), d.asInt());
1203 else if (menuId.type == INT && menuItemId.type == INT)
1204 g_director->_wm->setMenuItemEnabled(menuId.asInt() - 1, menuItemId.asInt() - 1, d.asInt());
1205 else
1206 warning("Lingo::setTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1207 break;
1208 case kTheName:
1209 if (menuId.type == STRING && menuItemId.type == STRING)
1210 g_director->_wm->setMenuItemName(menuId.asString(), menuItemId.asString(), d.asString());
1211 else if (menuId.type == INT && menuItemId.type == INT)
1212 g_director->_wm->setMenuItemName(menuId.asInt() - 1, menuItemId.asInt() - 1, d.asString());
1213 else
1214 warning("Lingo::setTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1215 break;
1216 case kTheScript:
1217 {
1218 LingoArchive *mainArchive = g_director->getCurrentMovie()->getMainLingoArch();
1219 int commandId = 100;
1220 while (mainArchive->getScriptContext(kEventScript, commandId))
1221 commandId++;
1222 mainArchive->replaceCode(d.asString(), kEventScript, commandId);
1223
1224 if (menuId.type == STRING && menuItemId.type == STRING)
1225 g_director->_wm->setMenuItemAction(menuId.asString(), menuItemId.asString(), commandId);
1226 else if (menuId.type == INT && menuItemId.type == INT)
1227 g_director->_wm->setMenuItemAction(menuId.asInt() - 1, menuItemId.asInt() - 1, commandId);
1228 else
1229 warning("Lingo::setTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1230 }
1231 break;
1232 default:
1233 warning("Lingo::setTheMenuItemEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
1234 break;
1235 }
1236 }
1237
getTheSprite(Datum & id1,int field)1238 Datum Lingo::getTheSprite(Datum &id1, int field) {
1239 Datum d;
1240 int id = 0;
1241 Score *score = _vm->getCurrentMovie()->getScore();
1242
1243 if (!score) {
1244 warning("Lingo::getTheSprite(): The sprite %d field \"%s\" setting over non-active score", id, field2str(field));
1245 return d;
1246 }
1247
1248 if (id1.type == INT) {
1249 id = id1.u.i;
1250 } else {
1251 warning("Lingo::getTheSprite(): Unknown the sprite id type: %s", id1.type2str());
1252 return d;
1253 }
1254
1255 Channel *channel = score->getChannelById(id);
1256 if (!channel)
1257 return d;
1258
1259 Sprite *sprite = channel->_sprite;
1260 if (!sprite)
1261 return d;
1262
1263 d.type = INT;
1264
1265 switch (field) {
1266 case kTheBackColor:
1267 d.u.i = sprite->_backColor;
1268 break;
1269 case kTheBlend:
1270 d.u.i = sprite->_blend;
1271 break;
1272 case kTheBottom:
1273 d.u.i = channel->getBbox().bottom;
1274 break;
1275 case kTheCastNum:
1276 // TODO: How is this handled with multiple casts in D5?
1277 d.u.i = sprite->_castId.member;
1278 break;
1279 case kTheConstraint:
1280 d.u.i = channel->_constraint;
1281 break;
1282 case kTheCursor:
1283 d = channel->_cursor._cursorResId;
1284 break;
1285 case kTheEditableText:
1286 d.u.i = sprite->_editable;
1287 break;
1288 case kTheForeColor:
1289 d.u.i = sprite->_foreColor;
1290 break;
1291 case kTheHeight:
1292 d.u.i = channel->_height;
1293 break;
1294 case kTheImmediate:
1295 d.u.i = sprite->_immediate;
1296 break;
1297 case kTheInk:
1298 d.u.i = sprite->_ink;
1299 break;
1300 case kTheLeft:
1301 d.u.i = channel->getBbox().left;
1302 break;
1303 case kTheLineSize:
1304 d.u.i = sprite->_thickness & 0x3;
1305 break;
1306 case kTheLoc:
1307 d.type = POINT;
1308 d.u.farr = new FArray;
1309 d.u.farr->arr.push_back(channel->_currentPoint.x);
1310 d.u.farr->arr.push_back(channel->_currentPoint.y);
1311 break;
1312 case kTheLocH:
1313 d.u.i = channel->_currentPoint.x;
1314 break;
1315 case kTheLocV:
1316 d.u.i = channel->_currentPoint.y;
1317 break;
1318 case kTheMoveableSprite:
1319 d.u.i = sprite->_moveable;
1320 break;
1321 case kTheMovieRate:
1322 d.type = FLOAT;
1323 d.u.f = channel->_movieRate;
1324 if (debugChannelSet(-1, kDebugEndVideo))
1325 d.u.f = 0.0;
1326 break;
1327 case kTheMovieTime:
1328 d.u.i = channel->_movieTime;
1329 break;
1330 case kThePattern:
1331 d.u.i = sprite->getPattern();
1332 break;
1333 case kThePuppet:
1334 d.u.i = sprite->_puppet;
1335 break;
1336 case kTheRect:
1337 // let compiler to optimize this
1338 d.type = RECT;
1339 d.u.farr = new FArray;
1340 d.u.farr->arr.push_back(channel->getBbox().left);
1341 d.u.farr->arr.push_back(channel->getBbox().top);
1342 d.u.farr->arr.push_back(channel->getBbox().right);
1343 d.u.farr->arr.push_back(channel->getBbox().bottom);
1344 break;
1345 case kTheRight:
1346 d.u.i = channel->getBbox().right;
1347 break;
1348 case kTheScoreColor:
1349 warning("STUB: Lingo::getTheSprite(): Unprocessed getting field \"%s\" of sprite", field2str(field));
1350 break;
1351 case kTheScriptNum:
1352 warning("STUB: Lingo::getTheSprite(): Unprocessed getting field \"%s\" of sprite", field2str(field));
1353 break;
1354 case kTheStartTime:
1355 d.u.i = channel->_startTime;
1356 break;
1357 case kTheStopTime:
1358 d.u.i = channel->_stopTime;
1359 break;
1360 case kTheStretch:
1361 d.u.i = sprite->_stretch;
1362 break;
1363 case kTheTop:
1364 d.u.i = channel->getBbox().top;
1365 break;
1366 case kTheTrails:
1367 d.u.i = sprite->_trails;
1368 break;
1369 case kTheType:
1370 d.u.i = sprite->_spriteType;
1371 break;
1372 case kTheVisibility:
1373 case kTheVisible:
1374 d.u.i = (channel->_visible ? 1 : 0);
1375 break;
1376 case kTheVolume:
1377 d.u.i = sprite->_volume;
1378 break;
1379 case kTheWidth:
1380 d.u.i = channel->_width;
1381 break;
1382 default:
1383 warning("Lingo::getTheSprite(): Unprocessed getting field \"%s\" of sprite", field2str(field));
1384 d.type = VOID;
1385 }
1386
1387 return d;
1388 }
1389
setTheSprite(Datum & id1,int field,Datum & d)1390 void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
1391 int id = 0;
1392 Score *score = _vm->getCurrentMovie()->getScore();
1393
1394 if (!score) {
1395 warning("Lingo::setTheSprite(): The sprite %d field \"%s\" setting over non-active score", id, field2str(field));
1396 return;
1397 }
1398
1399 if (id1.type == INT) {
1400 id = id1.u.i;
1401 } else {
1402 warning("Lingo::setTheSprite(): Unknown the sprite id type: %s", id1.type2str());
1403 return;
1404 }
1405
1406 Channel *channel = score->getChannelById(id);
1407 if (!channel)
1408 return;
1409
1410 Sprite *sprite = channel->_sprite;
1411 if (!sprite)
1412 return;
1413
1414 if (!sprite->_enabled)
1415 sprite->_enabled = true;
1416
1417 switch (field) {
1418 case kTheBackColor:
1419 if ((uint32)d.asInt() != sprite->_backColor) {
1420 sprite->_backColor = d.asInt();
1421 channel->_dirty = true;
1422 }
1423 break;
1424 case kTheBlend:
1425 if (d.asInt() != sprite->_blend) {
1426 sprite->_blend = (d.asInt() == 100 ? 0 : d.asInt());
1427 channel->_dirty = true;
1428 }
1429 break;
1430 case kTheCastNum:
1431 {
1432 CastMemberID castId = d.asMemberID();
1433 CastMember *castMember = g_director->getCurrentMovie()->getCastMember(castId);
1434
1435 if (castMember && castMember->_type == kCastDigitalVideo) {
1436 Common::String path = castMember->getCast()->getVideoPath(castId.member);
1437 if (!path.empty()) {
1438 ((DigitalVideoCastMember *)castMember)->loadVideo(pathMakeRelative(path));
1439 ((DigitalVideoCastMember *)castMember)->startVideo(channel);
1440 // b_updateStage needs to have _videoPlayback set to render video
1441 // in the regular case Score::renderSprites sets it.
1442 // However Score::renderSprites is not in the current code path.
1443 g_director->getCurrentMovie()->_videoPlayback = true;
1444 }
1445 }
1446
1447 if (castId != sprite->_castId) {
1448 if (!sprite->_trails) {
1449 g_director->getCurrentMovie()->getWindow()->addDirtyRect(channel->getBbox());
1450 channel->_dirty = true;
1451 }
1452 channel->setCast(castId);
1453 channel->_dirty = true;
1454 }
1455 }
1456 break;
1457 case kTheConstraint:
1458 {
1459 int channelId = -1;
1460 if (d.type == CASTREF) {
1461 // Reference: CastMember ID
1462 // Find the first channel that uses this cast.
1463 CastMemberID memberID = *d.u.cast;
1464 for (uint i = 0; i < score->_channels.size(); i++) {
1465 if (score->_channels[i]->_sprite->_castId == memberID) {
1466 channelId = i;
1467 break;
1468 }
1469 }
1470 } else {
1471 channelId = d.asInt();
1472 }
1473 if (channelId != -1 && channelId != (int)channel->_constraint) {
1474 channel->_constraint = d.u.i;
1475 channel->_dirty = true;
1476 }
1477 }
1478 break;
1479 case kTheCursor:
1480 if (d.type == INT) {
1481 channel->_cursor.readFromResource(d);
1482 } else {
1483 channel->_cursor.readFromCast(d);
1484 }
1485 score->_cursorDirty = true;
1486 break;
1487 case kTheEditableText:
1488 channel->_sprite->_editable = d.asInt();
1489 break;
1490 case kTheForeColor:
1491 if ((uint32)d.asInt() != sprite->_foreColor) {
1492 sprite->_foreColor = d.asInt();
1493 channel->_dirty = true;
1494 }
1495 break;
1496 case kTheHeight:
1497 if (d.asInt() != channel->_height) {
1498 g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
1499 channel->setHeight(d.asInt());
1500 channel->_dirty = true;
1501 }
1502 break;
1503 case kTheImmediate:
1504 sprite->_immediate = (bool)d.asInt();
1505 break;
1506 case kTheInk:
1507 if (d.asInt() != sprite->_ink) {
1508 sprite->_ink = static_cast<InkType>(d.asInt());
1509 channel->_dirty = true;
1510 }
1511 break;
1512 case kTheLineSize:
1513 if (d.asInt() != sprite->_thickness) {
1514 sprite->_thickness = d.asInt();
1515 channel->_dirty = true;
1516 }
1517 break;
1518 case kTheLoc:
1519 if (channel->_currentPoint.x != d.asPoint().x || channel->_currentPoint.y != d.asPoint().y) {
1520 g_director->getCurrentMovie()->getWindow()->addDirtyRect(channel->getBbox());
1521 channel->_dirty = true;
1522 }
1523
1524 channel->_currentPoint.x = d.asPoint().x;
1525 channel->_currentPoint.y = d.asPoint().y;
1526 break;
1527 case kTheLocH:
1528 if (d.asInt() != channel->_currentPoint.x) {
1529 // adding the dirtyRect only when the trails is false. Other changes which will add dirtyRect may also apply this patch
1530 // this is for fixing the bug in jman-win. Currently, i've only patched the LocH, LocV and castNum since those are the only ones used in jman
1531 if (!channel->_sprite->_trails) {
1532 g_director->getCurrentMovie()->getWindow()->addDirtyRect(channel->getBbox());
1533 channel->_dirty = true;
1534 }
1535 channel->_currentPoint.x = d.asInt();
1536 }
1537 break;
1538 case kTheLocV:
1539 if (d.asInt() != channel->_currentPoint.y) {
1540 if (!channel->_sprite->_trails) {
1541 g_director->getCurrentMovie()->getWindow()->addDirtyRect(channel->getBbox());
1542 channel->_dirty = true;
1543 }
1544 channel->_currentPoint.y = d.asInt();
1545 }
1546 break;
1547 case kTheMoveableSprite:
1548 sprite->_moveable = (bool)d.asInt();
1549 break;
1550 case kTheMovieRate:
1551 channel->_movieRate = d.asFloat();
1552 if (sprite->_cast->_type == kCastDigitalVideo)
1553 ((DigitalVideoCastMember *)sprite->_cast)->setMovieRate(channel->_movieRate);
1554 else
1555 warning("Setting movieTime for non-digital video");
1556 break;
1557 case kTheMovieTime:
1558 channel->_movieTime = d.asInt();
1559 if (sprite->_cast->_type == kCastDigitalVideo)
1560 ((DigitalVideoCastMember *)sprite->_cast)->seekMovie(channel->_movieTime);
1561 else
1562 warning("Setting movieTime for non-digital video");
1563 break;
1564 case kThePattern:
1565 if (d.asInt() != sprite->getPattern()) {
1566 sprite->setPattern(d.asInt());
1567 channel->_dirty = true;
1568 }
1569 break;
1570 case kThePuppet:
1571 sprite->_puppet = (bool)d.asInt();
1572 if (!d.asInt()) {
1573 // TODO: Properly reset sprite properties after puppet disabled.
1574 sprite->_moveable = false;
1575 }
1576 break;
1577 case kTheStartTime:
1578 channel->_startTime = d.asInt();
1579 if (sprite->_cast->_type == kCastDigitalVideo)
1580 ((DigitalVideoCastMember *)sprite->_cast)->seekMovie(channel->_startTime);
1581 else
1582 warning("Setting startTime for non-digital video");
1583 break;
1584 case kTheStopTime:
1585 channel->_stopTime = d.asInt();
1586 if (sprite->_cast->_type == kCastDigitalVideo)
1587 ((DigitalVideoCastMember *)sprite->_cast)->setStopTime(channel->_stopTime);
1588 else
1589 warning("Setting stopTime for non-digital video");
1590 break;
1591 case kTheStretch:
1592 if (d.asInt() != sprite->_stretch) {
1593 g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
1594
1595 sprite->_stretch = d.asInt();
1596 channel->_dirty = true;
1597
1598 channel->_width = sprite->_width;
1599 channel->_height = sprite->_height;
1600 }
1601 break;
1602 case kTheTrails:
1603 sprite->_trails = d.asInt();
1604 break;
1605 case kTheType:
1606 if (d.asInt() != sprite->_spriteType) {
1607 sprite->_spriteType = static_cast<SpriteType>(d.asInt());
1608 channel->_dirty = true;
1609 }
1610 break;
1611 case kTheVisibility:
1612 case kTheVisible:
1613 if ((bool)d.asInt() != channel->_visible) {
1614 channel->_visible = (bool)d.asInt();
1615 channel->_dirty = true;
1616 }
1617 break;
1618 case kTheVolume:
1619 // TODO: Should changing digital video flags mark as dirty?
1620 sprite->_volume = d.asInt();
1621 break;
1622 case kTheWidth:
1623 if (d.asInt() != channel->_width) {
1624 g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
1625 channel->setWidth(d.asInt());
1626 channel->_dirty = true;
1627 }
1628 break;
1629 default:
1630 warning("Lingo::setTheSprite(): Unprocessed setting field \"%s\" of sprite", field2str(field));
1631 }
1632
1633 if (channel->_dirty && g_director->getCurrentMovie())
1634 g_director->getCurrentMovie()->getWindow()->addDirtyRect(channel->getBbox());
1635 }
1636
getTheCast(Datum & id1,int field)1637 Datum Lingo::getTheCast(Datum &id1, int field) {
1638 Datum d;
1639
1640 Movie *movie = _vm->getCurrentMovie();
1641 if (!movie) {
1642 warning("Lingo::getTheCast(): No movie loaded");
1643 return d;
1644 }
1645
1646 CastMemberID id = id1.asMemberID();
1647
1648 CastMember *member = movie->getCastMember(id);
1649 if (!member) {
1650 if (field == kTheLoaded) {
1651 d = 0;
1652 } else if (field == kTheNumber) {
1653 d = -1;
1654 } else {
1655 g_lingo->lingoError("Lingo::getTheCast(): CastMember %s not found", id1.asString().c_str());
1656 }
1657 return d;
1658 }
1659
1660 if (!member->hasField(field)) {
1661 warning("Lingo::getTheCast(): %s has no property '%s'", id.asString().c_str(), field2str(field));
1662 return d;
1663 }
1664
1665 d = member->getField(field);
1666
1667 return d;
1668 }
1669
setTheCast(Datum & id1,int field,Datum & d)1670 void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
1671 Movie *movie = _vm->getCurrentMovie();
1672 if (!movie) {
1673 warning("Lingo::setTheCast(): No movie loaded");
1674 return;
1675 }
1676
1677 CastMemberID id = id1.asMemberID();
1678
1679 CastMember *member = movie->getCastMember(id);
1680 if (!member) {
1681 g_lingo->lingoError("Lingo::setTheCast(): %s not found", id.asString().c_str());
1682 return;
1683 }
1684
1685 if (!member->hasField(field)) {
1686 warning("Lingo::setTheCast(): %s has no property '%s'", id.asString().c_str(), field2str(field));
1687 return;
1688 }
1689
1690 member->setField(field, d);
1691 }
1692
getTheField(Datum & id1,int field)1693 Datum Lingo::getTheField(Datum &id1, int field) {
1694 Datum d;
1695
1696 Movie *movie = _vm->getCurrentMovie();
1697 if (!movie) {
1698 warning("Lingo::getTheField(): No movie loaded");
1699 return d;
1700 }
1701
1702 CastMemberID id = id1.asMemberID();
1703
1704 CastMember *member = movie->getCastMember(id);
1705 if (!member) {
1706 if (field == kTheLoaded) {
1707 d = 0;
1708 } else {
1709 g_lingo->lingoError("Lingo::getTheField(): %s not found", id.asString().c_str());
1710 }
1711 return d;
1712 }
1713 if (member->_type != kCastText) {
1714 g_lingo->lingoError("Lingo::getTheField(): %s is not a field", id.asString().c_str());
1715 return d;
1716 }
1717
1718 if (!member->hasField(field)) {
1719 warning("Lingo::getTheField(): %s has no property '%s'", id.asString().c_str(), field2str(field));
1720 return d;
1721 }
1722
1723 d = member->getField(field);
1724
1725 return d;
1726 }
1727
setTheField(Datum & id1,int field,Datum & d)1728 void Lingo::setTheField(Datum &id1, int field, Datum &d) {
1729 Movie *movie = _vm->getCurrentMovie();
1730 if (!movie) {
1731 warning("Lingo::setTheField(): No movie loaded");
1732 return;
1733 }
1734
1735 CastMemberID id = id1.asMemberID();
1736
1737 CastMember *member = movie->getCastMember(id);
1738 if (!member) {
1739 g_lingo->lingoError("Lingo::setTheField(): %s not found", id.asString().c_str());
1740 return;
1741 }
1742 if (member->_type != kCastText) {
1743 g_lingo->lingoError("Lingo::setTheField(): %s is not a field", id.asString().c_str());
1744 return;
1745 }
1746
1747 if (!member->hasField(field)) {
1748 warning("Lingo::setTheField(): %s has no property '%s'", id.asString().c_str(), field2str(field));
1749 return;
1750 }
1751
1752 member->setField(field, d);
1753 }
1754
getTheChunk(Datum & chunk,int field)1755 Datum Lingo::getTheChunk(Datum &chunk, int field) {
1756 Datum d;
1757
1758 Movie *movie = _vm->getCurrentMovie();
1759 if (!movie) {
1760 warning("Lingo::getTheChunk(): No movie loaded");
1761 return d;
1762 }
1763
1764 if (chunk.type != CHUNKREF) {
1765 warning("BUILDBOT: Lingo::getTheChunk(): bad chunk ref type: %s", chunk.type2str());
1766 return d;
1767 }
1768
1769 int start, end;
1770 start = chunk.u.cref->start;
1771 end = chunk.u.cref->end;
1772 Datum src = chunk.u.cref->source;
1773 while (src.type == CHUNKREF) {
1774 start += src.u.cref->start;
1775 end += src.u.cref->start;
1776 src = src.u.cref->source;
1777 }
1778 if (!src.isCastRef()) {
1779 warning("BUILDBOT: Lingo::getTheChunk(): bad chunk ref field type: %s", src.type2str());
1780 return d;
1781 }
1782
1783 CastMemberID memberID = *src.u.cast;
1784 CastMember *member = movie->getCastMember(memberID);
1785 if (!member) {
1786 g_lingo->lingoError("Lingo::getTheChunk(): %s not found", memberID.asString().c_str());
1787 return d;
1788 }
1789 if (member->_type != kCastText) {
1790 g_lingo->lingoError("Lingo::getTheChunk(): %s is not a field", memberID.asString().c_str());
1791 return d;
1792 }
1793
1794 if (!((TextCastMember *)member)->hasChunkField(field)) {
1795 warning("Lingo::getTheChunk(): %s has no chunk property '%s'", memberID.asString().c_str(), field2str(field));
1796 return d;
1797 }
1798
1799 d = ((TextCastMember *)member)->getChunkField(field, start, end);
1800
1801 return d;
1802 }
1803
setTheChunk(Datum & chunk,int field,Datum & d)1804 void Lingo::setTheChunk(Datum &chunk, int field, Datum &d) {
1805 Movie *movie = _vm->getCurrentMovie();
1806 if (!movie) {
1807 warning("Lingo::setTheChunk(): No movie loaded");
1808 return;
1809 }
1810
1811 if (chunk.type != CHUNKREF) {
1812 warning("BUILDBOT: Lingo::setTheChunk(): bad chunk ref type: %s", chunk.type2str());
1813 return;
1814 }
1815
1816 int start, end;
1817 start = chunk.u.cref->start;
1818 end = chunk.u.cref->end;
1819 Datum src = chunk.u.cref->source;
1820 while (src.type == CHUNKREF) {
1821 start += src.u.cref->start;
1822 end += src.u.cref->start;
1823 src = src.u.cref->source;
1824 }
1825 if (!src.isCastRef()) {
1826 warning("BUILDBOT: Lingo::setTheChunk(): bad chunk ref field type: %s", src.type2str());
1827 return;
1828 }
1829
1830 CastMemberID memberID = *src.u.cast;
1831 CastMember *member = movie->getCastMember(memberID);
1832 if (!member) {
1833 g_lingo->lingoError("Lingo::setTheChunk(): %s not found", memberID.asString().c_str());
1834 return;
1835 }
1836 if (member->_type != kCastText) {
1837 g_lingo->lingoError("Lingo::setTheChunk(): %s is not a field", memberID.asString().c_str());
1838 return;
1839 }
1840
1841 if (!((TextCastMember *)member)->hasChunkField(field)) {
1842 warning("Lingo::setTheChunk(): %s has no chunk property '%s'", memberID.asString().c_str(), field2str(field));
1843 return;
1844 }
1845
1846 ((TextCastMember *)member)->setChunkField(field, start, end, d);
1847 }
1848
getObjectProp(Datum & obj,Common::String & propName)1849 void Lingo::getObjectProp(Datum &obj, Common::String &propName) {
1850 Datum d;
1851 if (obj.type == OBJECT) {
1852 if (obj.u.obj->hasProp(propName)) {
1853 d = obj.u.obj->getProp(propName);
1854 } else {
1855 g_lingo->lingoError("Lingo::getObjectProp: Object <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
1856 }
1857 g_lingo->push(d);
1858 return;
1859 }
1860 if (obj.type == PARRAY) {
1861 int index = LC::compareArrays(LC::eqData, obj, propName, true).u.i;
1862 if (index > 0) {
1863 d = obj.u.parr->arr[index - 1].v;
1864 }
1865 g_lingo->push(d);
1866 return;
1867 }
1868 if (obj.type == CASTREF) {
1869 Movie *movie = _vm->getCurrentMovie();
1870 if (!movie) {
1871 g_lingo->lingoError("Lingo::getObjectProp(): No movie loaded");
1872 g_lingo->push(d);
1873 return;
1874 }
1875
1876 CastMemberID id = *obj.u.cast;
1877 CastMember *member = movie->getCastMember(id);
1878 if (!member) {
1879 if (propName.equalsIgnoreCase("loaded")) {
1880 d = 0;
1881 } else {
1882 g_lingo->lingoError("Lingo::getObjectProp(): %s not found", id.asString().c_str());
1883 }
1884 g_lingo->push(d);
1885 return;
1886 }
1887
1888 if (member->hasProp(propName)) {
1889 d = member->getProp(propName);
1890 } else {
1891 g_lingo->lingoError("Lingo::getObjectProp(): %s has no property '%s'", id.asString().c_str(), propName.c_str());
1892 }
1893 g_lingo->push(d);
1894 return;
1895 }
1896 if (_builtinFuncs.contains(propName) && _builtinFuncs[propName].nargs == 1) {
1897 push(obj);
1898 LC::call(_builtinFuncs[propName], 1, true);
1899 return;
1900 }
1901 g_lingo->lingoError("Lingo::getObjectProp: Invalid object: %s", obj.asString(true).c_str());
1902 g_lingo->push(d);
1903 }
1904
setObjectProp(Datum & obj,Common::String & propName,Datum & val)1905 void Lingo::setObjectProp(Datum &obj, Common::String &propName, Datum &val) {
1906 if (obj.type == OBJECT) {
1907 if (obj.u.obj->hasProp(propName)) {
1908 obj.u.obj->setProp(propName, val);
1909 } else {
1910 g_lingo->lingoError("Lingo::setObjectProp: Object <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
1911 }
1912 } else if (obj.type == PARRAY) {
1913 int index = LC::compareArrays(LC::eqData, obj, propName, true).u.i;
1914 if (index > 0) {
1915 obj.u.parr->arr[index - 1].v = val;
1916 } else {
1917 PCell cell = PCell(propName, val);
1918 obj.u.parr->arr.push_back(cell);
1919 }
1920 } else if (obj.type == CASTREF) {
1921 Movie *movie = _vm->getCurrentMovie();
1922 if (!movie) {
1923 g_lingo->lingoError("Lingo::setObjectProp(): No movie loaded");
1924 return;
1925 }
1926
1927 CastMemberID id = *obj.u.cast;
1928 CastMember *member = movie->getCastMember(id);
1929 if (!member) {
1930 g_lingo->lingoError("Lingo::setObjectProp(): %s not found", id.asString().c_str());
1931 return;
1932 }
1933
1934 if (member->hasProp(propName)) {
1935 member->setProp(propName, val);
1936 } else {
1937 g_lingo->lingoError("Lingo::setObjectProp(): %s has no property '%s'", id.asString().c_str(), propName.c_str());
1938 }
1939 } else {
1940 g_lingo->lingoError("Lingo::setObjectProp: Invalid object: %s", obj.asString(true).c_str());
1941 }
1942 }
1943
1944 static const char *mfull[] = {
1945 "January", "February", "March", "April", "May", "June",
1946 "July", "August" "September", "October", "November", "December"
1947 };
1948
1949 static const char *wday[] = {
1950 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
1951 };
1952
getTheDate(int field)1953 Datum Lingo::getTheDate(int field) {
1954 TimeDate t;
1955 g_system->getTimeAndDate(t);
1956
1957 Common::String s;
1958
1959 Datum d;
1960 d.type = STRING;
1961
1962 const char *m = mfull[t.tm_mon];
1963 const char *w = wday[t.tm_wday];
1964
1965 switch (field) {
1966 case kTheAbbr: // "Sat, Sep 7, 1991"
1967 s = Common::String::format("%c%c%c, %c%c%c %d, %d", w[0], w[1], w[2], m[0], m[1], m[2], t.tm_mday, t.tm_year + 1900);
1968 break;
1969
1970 case kTheLong: // "Saturday, September 7, 1991"
1971 s = Common::String::format("%s, %s %d, %d", w, m, t.tm_mday, t.tm_year + 1900);
1972 break;
1973
1974 default: // "9/7/91"
1975 s = Common::String::format("%d/%d/%02d", t.tm_mday, t.tm_mon, t.tm_year % 100);
1976 break;
1977 }
1978
1979 d.u.s = new Common::String(s);
1980
1981 return d;
1982 }
1983
getTheTime(int field)1984 Datum Lingo::getTheTime(int field) {
1985 TimeDate t;
1986 g_system->getTimeAndDate(t);
1987
1988 Common::String s;
1989
1990 Datum d;
1991 d.type = STRING;
1992
1993 switch (field) {
1994 case kTheLong:
1995 s = Common::String::format("%d:%02d:%02d %s", t.tm_hour % 12, t.tm_min, t.tm_sec, t.tm_hour < 12 ? "AM" : "PM");
1996 break;
1997
1998 default:
1999 s = Common::String::format("%d:%02d %s", t.tm_hour % 12, t.tm_min, t.tm_hour < 12 ? "AM" : "PM");
2000 break;
2001 }
2002
2003 d.u.s = new Common::String(s);
2004
2005 return d;
2006 }
2007
2008 } // End of namespace Director
2009