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