Files
scummvm-cursorfix/engines/director/lingo/lingo-the.cpp
2026-02-02 04:50:13 +01:00

2831 lines
87 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/platform.h"
#include "director/types.h"
#include "graphics/macgui/macbutton.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/cursor.h"
#include "director/channel.h"
#include "director/debugger.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/sound.h"
#include "director/sprite.h"
#include "director/score.h"
#include "director/window.h"
#include "director/castmember/castmember.h"
#include "director/castmember/digitalvideo.h"
#include "director/castmember/text.h"
#include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h"
#include "director/lingo/lingo-the.h"
namespace Director {
class Sprite;
TheEntity entities[] = { // hasId ver. isFunction
{ kTheActiveWindow, "activeWindow", false, 500, false },// D5 property
{ kTheActorList, "actorList", false, 400, false },// D4 p
{ kTheAlertHook, "alertHook", false, 600, true }, // D6 p
{ kTheApplicationPath, "applicationPath", false, 600, true }, // D6 f
{ kTheBeepOn, "beepOn", false, 200, false },// D2 p
{ kTheButtonStyle, "buttonStyle", false, 200, false },// D2 p
{ kTheCast, "cast", true, 200, false },// D2
{ kTheCastLibs, "castLibs", false, 500, false },// D5 p
{ kTheCastMembers, "castmembers", false, 300, false },// D3
{ kTheCenterStage, "centerStage", false, 200, false },// D2 p
{ kTheCheckBoxAccess, "checkBoxAccess", false, 200, false },// D2 p
{ kTheCheckBoxType, "checkBoxType", false, 200, false },// D2 p
{ kTheChunk, "chunk", true, 300, false },// D3
{ kTheClickLoc, "clickLoc", false, 400, true }, // D4 function
{ kTheClickOn, "clickOn", false, 200, true }, // D2 f
{ kTheColorDepth, "colorDepth", false, 200, false },// D2 p
{ kTheColorQD, "colorQD", false, 200, true }, // D2 f
{ kTheCommandDown, "commandDown", false, 200, true }, // D2 f
{ kTheControlDown, "controlDown", false, 200, true }, // D2 f
{ kTheCpuHogTicks, "cpuHogTicks", false, 400, true }, // D4 p, documented in D6
{ kTheCurrentSpriteNum, "currentSpriteNum", false, 600, true }, // D6 p
{ kTheDate, "date", false, 300, true }, // D3 f
{ kTheDeskTopRectList, "deskTopRectList", false, 500, true }, // D5 p
{ kTheDigitalVideoTimeScale,"digitalVideoTimeScale",false, 500, false },// D5 p
{ kTheDoubleClick, "doubleClick", false, 200, true }, // D2 f
{ kTheEmulateMultiButtonMouse,"emulateMultiButtonMouse",false, 500, false },// D5 p
{ kTheExitLock, "exitLock", false, 200, false },// D2 p
{ kTheField, "field", true, 300, false },// D3
{ kTheFixStageSize, "fixStageSize", false, 200, false },// D2 p
{ kTheFloatPrecision, "floatPrecision", false, 300, false },// D3 p
{ kTheFrame, "frame", false, 200, true }, // D2 f
{ kTheFrameLabel, "frameLabel", false, 400, false },// D4 p
{ kTheFramePalette, "framePalette", false, 400, false },// D4 p
{ kTheFrameScript, "frameScript", false, 400, false },// D4 p
{ kTheFrameSound1, "frameSound1", false, 500, false },// D5 p
{ kTheFrameSound2, "frameSound2", false, 500, false },// D5 p
{ kTheFrameTempo, "frameTempo", false, 400, false },// D4 p
{ kTheFrameTransition, "frameTransition", false, 500, false },// D5 p
{ kTheFreeBlock, "freeBlock", false, 200, true }, // D2 f
{ kTheFreeBytes, "freeBytes", false, 200, true }, // D2 f
{ kTheFrontWindow, "frontWindow", false, 500, false },// D5 p
{ kTheFullColorPermit, "fullColorPermit", false, 200, false },// D2 p
{ kTheIdleHandlerPeriod,"idleHandlerPeriod",false, 500, false },// D5 p
{ kTheIdleLoadMode, "idleLoadMode", false, 500, false },// D5 p
{ kTheIdleLoadPeriod, "idleLoadPeriod", false, 500, false },// D5 p
{ kTheIdleLoadTag, "idleLoadTag", false, 500, false },// D5 p
{ kTheIdleReadChunkSize,"idleReadChunkSize",false, 500, false },// D5 p
{ kTheImageDirect, "imageDirect", false, 200, false },// D2 p
{ kTheItemDelimiter, "itemDelimiter", false, 400, false },// D4 p
{ kTheKey, "key", false, 200, true }, // D2 f
{ kTheKeyCode, "keyCode", false, 200, true }, // D2 f
{ kTheKeyDownScript, "keyDownScript", false, 200, false },// D2 p
{ kTheKeyPressed, "keyPressed", false, 500, false },// D5 p
{ kTheKeyUpScript, "keyUpScript", false, 400, false },// D4 p
{ kTheLabelList, "labelList", false, 300, true }, // D3 f
{ kTheLastClick, "lastClick", false, 200, true }, // D2 f
{ kTheLastEvent, "lastEvent", false, 200, true }, // D2 f
{ kTheLastFrame, "lastFrame", false, 400, false },// D4 p
{ kTheLastKey, "lastKey", false, 200, true }, // D2 f
{ kTheLastRoll, "lastRoll", false, 200, true }, // D2 f
{ kTheMachineType, "machineType", false, 200, true }, // D2 f
{ kTheMaxInteger, "maxInteger", false, 300, true }, // D3.1 f
{ kTheMemorySize, "memorySize", false, 200, true }, // D2 f
{ kTheMenu, "menu", true, 300, false },// D3 p
{ kTheMenuItem, "menuitem", true, 300, false },// D3 p
{ kTheMenuItems, "menuitems", false, 300, true }, // D3 f
{ kTheMouseCast, "mouseCast", false, 300, true }, // D3 f
{ kTheMouseChar, "mouseChar", false, 300, true }, // D3 f
{ kTheMouseDown, "mouseDown", false, 200, true }, // D2 f
{ kTheMouseDownScript, "mouseDownScript", false, 200, false },// D2 p
{ kTheMouseH, "mouseH", false, 200, true }, // D2 f
{ kTheMouseItem, "mouseItem", false, 300, true }, // D3 f
{ kTheMouseLine, "mouseLine", false, 300, true }, // D3 f
{ kTheMouseMember, "mouseMember", false, 600, true }, // D6 f
{ kTheMouseUp, "mouseUp", false, 200, true }, // D2 f
{ kTheMouseUpScript, "mouseUpScript", false, 200, false },// D2 p
{ kTheMouseV, "mouseV", false, 200, true }, // D2 f
{ kTheMouseWord, "mouseWord", false, 300, true }, // D3 f
{ kTheMovie, "movie", false, 200, true }, // D2 f
{ kTheMovieFileFreeSize,"movieFileFreeSize",false, 400, true }, // D4 f
{ kTheMovieFileSize, "movieFileSize", false, 400, true }, // D4 f
{ kTheMovieName, "movieName", false, 400, true }, // D4 f
{ kTheMoviePath, "moviePath", false, 400, true }, // D4 f
{ kTheMultiSound, "multiSound", false, 300, true }, // D3.1 f
{ kTheNetThrottleTicks, "netThrottleTicks", false, 600, true }, // D6 f, documented in D7
{ kTheOptionDown, "optionDown", false, 200, true }, // D2 f
{ kTheOrganizationName, "organizationName", false, 500, false },// D5 p, documented in D7
{ kTheParamCount, "paramCount", false, 400, true }, // D4 f
{ kThePathName, "pathName", false, 200, true }, // D2 f
{ kThePauseState, "pauseState", false, 200, true }, // D2 f
{ kThePerFrameHook, "perFrameHook", false, 200, false },// D2 p
{ kThePi, "pi", false, 400, true }, // D4 f
{ kThePlatform, "platform", false, 500, false },// D5 p
{ kThePreloadEventAbort,"preloadEventAbort",false, 400, false },// D4 p
{ kThePreLoadRAM, "preLoadRAM", false, 400, false },// D4 p
{ kTheProductName, "productName", false, 500, false },// D5 p, undocumented
{ kTheProductVersion, "productVersion", false, 500, false },// D5 p, documented in D8
{ kTheQuickTimePresent, "quickTimePresent", false, 300, true }, // D3.1 f
{ kTheRandomSeed, "randomSeed", false, 400, false },// D4 p
{ kTheResult, "result", false, 200, true }, // D2 f
{ kTheRightMouseDown, "rightMouseDown", false, 500, true }, // D5 f
{ kTheRightMouseUp, "rightMouseUp", false, 500, true }, // D5 f
{ kTheRollOver, "rollOver", false, 500, true }, // D5 f, undocumented
{ kTheRomanLingo, "romanLingo", false, 300, false },// D3.1 p
{ kTheRunMode, "runMode", false, 500, false },// D5 f, documented in D6
{ kTheSafePlayer, "safePlayer", false, 600, false },// D6 p, documented in D7
{ kTheScore, "score", false, 500, false },// D5 p
{ kTheScummvmVersion, "scummvmVersion", false, 200, true }, // ScummVM only
{ kTheSearchCurrentFolder,"searchCurrentFolder",false,400, true },// D4 f
{ kTheSearchPath, "searchPath", false, 400, true }, // D4 f
{ kTheSearchPaths, "searchPaths", false, 400, false },// D4 p, documented in D5
{ kTheSelection, "selection", false, 200, true }, // D2 f
{ kTheSelEnd, "selEnd", false, 200, false },// D2 p
{ kTheSelStart, "selStart", false, 200, false },// D2 p
{ kTheSerialNumber, "serialNumber", false, 500, false },// D5 p, documnted in D7
{ kTheShiftDown, "shiftDown", false, 200, true }, // D2 f
{ kTheSoundEnabled, "soundEnabled", false, 200, false },// D2 p
{ kTheSoundEntity, "sound", true, 300, false },// D3 p
{ kTheSoundKeepDevice, "soundKeepDevice", false, 600, false },// D6 p, documented in D7
{ kTheSoundLevel, "soundLevel", false, 200, false },// D2 p
{ kTheSprite, "sprite", true, 200, false },// D4 p
{ kTheStage, "stage", false, 400, false },// D4 p
{ kTheStageBottom, "stageBottom", false, 200, true }, // D2 f
{ kTheStageColor, "stageColor", false, 300, false },// D3 p
{ kTheStageLeft, "stageLeft", false, 200, true }, // D2 f
{ kTheStageRight, "stageRight", false, 200, true }, // D2 f
{ kTheStageTop, "stageTop", false, 200, true }, // D2 f
{ kTheStillDown, "stillDown", false, 200, true }, // D2 f
{ kTheSwitchColorDepth, "switchColorDepth", false, 200, false },// D2 p
{ kTheTicks, "ticks", false, 200, true }, // D2 f
{ kTheTime, "time", false, 300, true }, // D3 f
{ kTheTimeoutKeyDown, "timeoutKeyDown", false, 200, false },// D2 p
{ kTheTimeoutLapsed, "timeoutLapsed", false, 200, false },// D2 p
{ kTheTimeoutLength, "timeoutLength", false, 200, false },// D2 p
{ kTheTimeoutMouse, "timeoutMouse", false, 200, false },// D2 p
{ kTheTimeoutPlay, "timeoutPlay", false, 200, false },// D2 p
{ kTheTimeoutScript, "timeoutScript", false, 200, false },// D2 p
{ kTheTimer, "timer", false, 200, false },// D2 p
{ kTheTrace, "trace", false, 400, false },// D4 p
{ kTheTraceLoad, "traceLoad", false, 400, false },// D4 p
{ kTheTraceLogFile, "traceLogFile", false, 400, false },// D4 p
{ kTheUpdateMovieEnabled,"updateMovieEnabled",false,400, false },// D4 p
{ kTheUserName, "userName", false, 500, false },// D5 p, documented in D7
{ kTheVideoForWindowsPresent,"videoForWindowsPresent",false, 400, true },// D4 f
{ kTheWindow, "window", true, 400, false },// D4
{ kTheWindowList, "windowList", false, 400, false },// D4 p
{ kTheXtras, "xtras", false, 500, false },// D5 p
{ kTheNOEntity, nullptr, false, 0, false }
};
const TheEntityField fields[] = {
{ kTheSprite, "backColor", kTheBackColor, 200 },// D2 p
{ kTheSprite, "blend", kTheBlend, 400 },// D4 p
{ kTheSprite, "bottom", kTheBottom, 200 },// D2 p
{ kTheSprite, "castNum", kTheCastNum, 200 },// D2 p
{ kTheSprite, "castLibNum", kTheCastLibNum, 500 },// D5 p
{ kTheSprite, "constraint", kTheConstraint, 200 },// D2 p
{ kTheSprite, "currentTime", kTheCurrentTime,600 },// D6 p
{ kTheSprite, "cursor", kTheCursor, 200 },// D2 p
{ kTheSprite, "editableText", kTheEditableText,400 },// D4 p
{ kTheSprite, "flipH", kTheFlipH, 700 },// D7 p
{ kTheSprite, "flipV", kTheFlipV, 700 },// D7 p
{ kTheSprite, "foreColor", kTheForeColor, 200 },// D2 p
{ kTheSprite, "height", kTheHeight, 200 },// D2 p
{ kTheSprite, "immediate", kTheImmediate, 200 },// D2 p
{ kTheSprite, "ink", kTheInk, 200 },// D2 p
{ kTheSprite, "left", kTheLeft, 200 },// D2 p
{ kTheSprite, "lineSize", kTheLineSize, 200 },// D2 p
{ kTheSprite, "loc", kTheLoc, 400 },// D4 p ???
{ kTheSprite, "locH", kTheLocH, 200 },// D2 p
{ kTheSprite, "locV", kTheLocV, 200 },// D2 p
{ kTheSprite, "member", kTheMember, 500 },// D5 p
{ kTheSprite, "memberNum", kTheMemberNum, 500 },// D5 p
{ kTheSprite, "moveableSprite",kTheMoveableSprite,400 },// D4 p
{ kTheSprite, "mostRecentCuePoint",kTheMostRecentCuePoint,600 },// D6 p
{ kTheSprite, "name", kTheName, 600 },// D6 p
{ kTheSprite, "pattern", kThePattern, 200 },// D2 p
{ kTheSprite, "puppet", kThePuppet, 200 },// D2 p
{ kTheSprite, "rect", kTheRect, 400 },// D4 p ???
{ kTheSprite, "right", kTheRight, 200 },// D2 p
{ kTheSprite, "scoreColor", kTheScoreColor, 400 },// D4 p
{ kTheSprite, "scriptInstanceList",kTheScriptInstanceList,600 },// D6 p
{ kTheSprite, "scriptNum", kTheScriptNum, 400 },// D4 p
{ kTheSprite, "stretch", kTheStretch, 200 },// D2 p
{ kTheSprite, "top", kTheTop, 200 },// D2 p
{ kTheSprite, "trails", kTheTrails, 300 },// D3.1 p
{ kTheSprite, "tweened", kTheTweened, 600 },// D6 p
{ kTheSprite, "type", kTheType, 200 },// D2 p
{ kTheSprite, "visibility", kTheVisibility, 300 },// D3.1 p
{ kTheSprite, "visible", kTheVisible, 400 },// D4 p
{ kTheSprite, "width", kTheWidth, 200 },// D2 p
// Cast library fields
{ kTheCastLib, "fileName", kTheFileName, 500 },// D5 p
{ kTheCastLib, "name", kTheName, 500 },// D5 p
{ kTheCastLib, "number", kTheNumber, 500 },// D5 p
{ kTheCastLib, "preLoadMode", kThePreLoadMode,500 },// D5 p
{ kTheCastLib, "selection", kTheSelectionField,500 },// D5 p
// Common cast fields
{ kTheCast, "antiAlias", kTheAntiAlias, 500 },// D5 p (?), documented in D7
{ kTheCast, "backColor", kTheBackColor, 400 },// D4 p
{ kTheCast, "castLibNum", kTheCastLibNum, 500 },// D5 p
{ kTheCast, "castType", kTheCastType, 400 },// D4 p
{ kTheCast, "cuePointNames",kTheCuePointNames,600 },// D6 p
{ kTheCast, "cuePointTimes",kTheCuePointTimes,600 },// D6 p
{ kTheCast, "filename", kTheFileName, 400 },// D4 p
{ kTheCast, "foreColor", kTheForeColor, 400 },// D4 p
{ kTheCast, "height", kTheHeight, 400 },// D4 p
{ kTheCast, "loaded", kTheLoaded, 400 },// D4 p
{ kTheCast, "media", kTheMedia, 500 },// D5 p
{ kTheCast, "mediaReady", kTheMediaReady, 600 },// D6 p
{ kTheCast, "memberNum", kTheMemberNum, 500 },// D5 p
{ kTheCast, "modified", kTheModified, 400 },// D4 p
{ kTheCast, "name", kTheName, 300 },// D3 p
{ kTheCast, "number", kTheNumber, 300 },// D3 p
{ kTheCast, "rect", kTheRect, 400 },// D4 p
{ kTheCast, "purgePriority",kThePurgePriority,400 },// D4 p // 0 Never purge, 1 Purge Last, 2 Purge next, 2 Purge normal
{ kTheCast, "scriptText", kTheScriptText, 400 },// D4 p
{ kTheCast, "size", kTheSize, 300 },// D3.1 p
{ kTheCast, "type", kTheType, 500 },// D5 p
{ kTheCast, "width", kTheWidth, 400 },// D4 p
// Digital video fields
{ kTheCast, "center", kTheCenter, 400 },// D4 p
{ kTheCast, "controller", kTheController, 300 },// D3.1 p
{ kTheCast, "crop", kTheCrop, 400 },// D4 p
{ kTheCast, "digitalVideoType",kTheDigitalVideoType,500 },// D5 p
{ kTheCast, "directToStage",kTheDirectToStage,300 },// D3.1 p
{ kTheCast, "duration", kTheDuration, 300 },// D3.1 p
{ kTheCast, "frameRate", kTheFrameRate, 400 },// D4 p
{ kTheCast, "loop", kTheLoop, 300 },// D3.1 p
{ kTheSprite, "movieRate", kTheMovieRate, 300 },// D3.1 P
{ kTheSprite, "movieTime", kTheMovieTime, 300 },// D3.1 P
{ kTheCast, "pausedAtStart",kThePausedAtStart,400 },// D4 p
{ kTheCast, "preLoad", kThePreLoad, 300 },// D3.1 p
{ kTheSprite, "setTrackEnabled",kTheSetTrackEnabled, 500 },// D5 p
{ kTheCast, "sound", kTheSound, 300 },// D3.1 p // 0-1 off-on
{ kTheSprite, "startTime", kTheStartTime, 300 },// D3.1 p
{ kTheSprite, "stopTime", kTheStopTime, 300 },// D3.1 p
{ kTheCast, "timeScale", kTheTimeScale, 500 },// D5 p
{ kTheSprite, "trackEnabled", kTheTrackEnabled, 500 },// D5 p
{ kTheSprite, "trackNextKeyTime", kTheTrackNextKeyTime, 500 },// D5 p
{ kTheSprite, "trackNextSampleTime", kTheTrackNextSampleTime, 500 },// D5 p
{ kTheSprite, "trackPreviousKeyTime", kTheTrackPreviousKeyTime, 500 },// D5 p
{ kTheSprite, "trackPreviousSampleTime", kTheTrackPreviousKeyTime, 500 },//D5 p
{ kTheSprite, "trackText", kTheTrackText, 500 },// D5 p
{ kTheCast, "video", kTheVideo, 400 },// D4 p
{ kTheSprite, "volume", kTheVolume, 300 },// D3.1 p
// track, scontains track type, seems to be unused
// tracks, number of track, seems to be unused
// Movie fields
{ kTheCast, "paletteMapping", kThePaletteMapping, 500 },// D5 p
{ kTheCast, "scriptsEnabled", kTheScriptsEnabled, 500 },// D5 p
{ kTheCast, "scoreSelection", kTheScoreSelection, 500 },// D5 p
{ kTheCast, "updateLock", kTheUpdateLock, 500 },// D5 p
// Bitmap fields
{ kTheCast, "depth", kTheDepth, 400 },// D4 p
{ kTheCast, "regPoint", kTheRegPoint, 400 },// D4 p
{ kTheCast, "palette", kThePalette, 400 },// D4 p
{ kTheCast, "paletteRef", kThePaletteRef, 500 },// D4 p
{ kTheCast, "picture", kThePicture, 300 },// D3 p
// TextCastMember fields
{ kTheCast, "alignment", kTheTextAlign, 500 },// D5 p
{ kTheCast, "autoTab", kTheAutoTab, 500 },// D5 p
{ kTheCast, "border", kTheBorder, 500 },// D5 p
{ kTheCast, "boxDropShadow",kTheBoxDropShadow, 500 },// D5 p
{ kTheCast, "boxType", kTheBoxType, 500 },// D5 p
{ kTheCast, "dropShadow", kTheDropShadow, 500 },// D5 p
{ kTheCast, "editable", kTheEditable, 500 },// D5 p
{ kTheCast, "font", kTheTextFont, 500 },// D5 p
{ kTheCast, "fontSize", kTheTextSize, 500 },// D5 p
{ kTheCast, "fontStyle", kTheTextStyle, 500 },// D5 p
{ kTheCast, "lineCount", kTheLineCount, 500 },// D5 p
{ kTheCast, "lineHeight", kTheTextHeight, 500 },// D5 p
{ kTheCast, "hilite", kTheHilite, 200 },// D2 p
{ kTheCast, "margin", kTheMargin, 500 },// D5 p
{ kTheCast, "pageHeight", kThePageHeight, 500 },// D5 p
{ kTheCast, "text", kTheText, 200 },// D2 p
{ kTheCast, "textAlign", kTheTextAlign, 300 },// D3 p
{ kTheCast, "textFont", kTheTextFont, 300 },// D3 p
{ kTheCast, "textHeight", kTheTextHeight, 300 },// D3 p
{ kTheCast, "textSize", kTheTextSize, 300 },// D3 p
{ kTheCast, "textStyle", kTheTextStyle, 300 },// D3 p
{ kTheCast, "scrollTop", kTheScrollTop, 500 },// D5 p
{ kTheCast, "wordWrap", kTheWordWrap, 500 },// D5 p
// ButtonCastMember fields
{ kTheCast, "buttonType", kTheButtonType, 500 },// D5 p
// ScriptCastMember fields
{ kTheCast, "scriptType", kTheScriptType, 500 },// D5 p
// ShapeCastMember fields
{ kTheCast, "filled", kTheFilled, 500 },// D5 p
{ kTheCast, "lineSize", kTheLineSize, 500 },// D5 p
{ kTheCast, "pattern", kThePattern, 500 },// D5 p
{ kTheCast, "shapeType", kTheShapeType, 500 },// D5 p
// SoundCastMember fields
{ kTheCast, "channelCount", kTheChannelCount,500 },// D5 p
{ kTheCast, "sampleRate", kTheSampleRate, 500 },// D5 p
{ kTheCast, "sampleSize", kTheSampleSize, 500 },// D5 p
// TransitionCastMember fields
{ kTheCast, "changeArea", kTheChangeArea, 500 },// D5 p
{ kTheCast, "chunkSize", kTheChunkSize, 500 },// D5 p
{ kTheCast, "transitionType",kTheTransitionType,500 },// D5 p
// XtrasCastMember fields
{ kTheCast, "interface", kTheInterface, 500 },// D5 p
{ kTheCast, "mediaBusy", kTheMediaBusy, 600 },// D6 p
// Behavior (me) fields
{ kTheCast, "spriteNum", kTheSpriteNum, 600 },// D6 p
// Field fields
{ kTheField, "alignment", kTheTextAlign, 500 },// D5 p
{ kTheField, "font", kTheTextFont, 500 },// D5 p
{ kTheField, "fontSize", kTheTextSize, 500 },// D5 p
{ kTheField, "fontStyle", kTheTextStyle, 500 },// D5 p
{ kTheField, "foreColor", kTheForeColor, 400 },// D4 p
{ kTheField, "hilite", kTheHilite, 200 },// D2 p
{ kTheField, "lineHeight", kTheTextHeight, 500 },// D5 p
{ kTheField, "name", kTheName, 300 },// D3 p
{ kTheField, "text", kTheText, 200 },// D2 p
{ kTheField, "textAlign", kTheTextAlign, 300 },// D3 p
{ kTheField, "textFont", kTheTextFont, 300 },// D3 p
{ kTheField, "textHeight", kTheTextHeight, 300 },// D3 p
{ kTheField, "textSize", kTheTextSize, 300 },// D3 p
{ kTheField, "textStyle", kTheTextStyle, 300 },// D3 p
// Chunk fields
{ kTheChunk, "font", kTheTextFont, 500 },// D5 p
{ kTheChunk, "fontSize", kTheTextSize, 500 },// D5 p
{ kTheChunk, "fontStyle", kTheTextStyle, 500 },// D5 p
{ kTheChunk, "foreColor", kTheForeColor, 400 },// D4 p
{ kTheChunk, "lineHeight", kTheTextHeight, 500 },// D5 p
{ kTheChunk, "textFont", kTheTextFont, 300 },// D3 p
{ kTheChunk, "textHeight", kTheTextHeight, 300 },// D3 p
{ kTheChunk, "textSize", kTheTextSize, 300 },// D3 p
{ kTheChunk, "textStyle", kTheTextStyle, 300 },// D3 p
{ kTheWindow, "drawRect", kTheDrawRect, 400 },// D4 p
{ kTheWindow, "fileName", kTheFileName, 400 },// D4 p
{ kTheWindow, "modal", kTheModal, 400 },// D4 p
{ kTheWindow, "rect", kTheRect, 400 },// D4 p
{ kTheWindow, "title", kTheTitle, 400 },// D4 p
{ kTheWindow, "titleVisible", kTheTitleVisible,400 },// D4 p
{ kTheWindow, "sourceRect", kTheSourceRect, 400 },// D4 p
{ kTheWindow, "visible", kTheVisible, 400 },// D4 p
{ kTheWindow, "windowType", kTheWindowType, 400 },// D4 p
{ kTheMenuItem, "checkmark", kTheCheckMark, 300 },// D3 p
{ kTheMenuItem, "enabled", kTheEnabled, 300 },// D3 p
{ kTheMenuItem, "name", kTheName, 300 },// D3 p
{ kTheMenuItem, "script", kTheScript, 300 },// D3 p
{ kTheMenuItems,"number", kTheNumber, 300 },// D3 p // number of menuitems of menu <xx>
{ kTheMenu, "name", kTheName, 300 },// D3 p
{ kTheCastMembers, "number", kTheNumber, 300 },// D3 p
{ kTheCastLibs, "number", kTheNumber, 500 },// D5 p
{ kTheDate, "short", kTheShort, 300 },// D3 f
{ kTheDate, "long", kTheLong, 300 },// D3 f
{ kTheDate, "abbreviated", kTheAbbr, 300 },// D3 f
{ kTheDate, "abbrev", kTheAbbr, 300 },// D3 f
{ kTheDate, "abbr", kTheAbbr, 300 },// D3 f
{ kTheTime, "short", kTheShort, 300 },// D3 f
{ kTheTime, "long", kTheLong, 300 },// D3 f
{ kTheTime, "abbreviated", kTheAbbr, 300 },// D3 f
{ kTheTime, "abbrev", kTheAbbr, 300 },// D3 f
{ kTheTime, "abbr", kTheAbbr, 300 },// D3 f
{ kTheSoundEntity,"volume", kTheVolume, 300 },// D3 p
{ kTheNOEntity, nullptr, kTheNOField, 0 }
};
void Lingo::initTheEntities() {
_objectEntityId = kTheObject;
const TheEntity *e = entities;
_entityNames.resize(kTheMaxTheEntityType);
while (e->entity != kTheNOEntity) {
if (e->version <= _vm->getVersion()) {
_theEntities[e->name] = e;
_entityNames[e->entity] = e->name;
}
e++;
}
const TheEntityField *f = fields;
_fieldNames.resize(kTheMaxTheFieldType);
while (f->entity != kTheNOEntity) {
if (f->version <= _vm->getVersion()) {
_theEntityFields[Common::String::format("%d%s", f->entity, f->name)] = f;
_fieldNames[f->field] = f->name;
}
// Store all fields for kTheObject
_theEntityFields[Common::String::format("%d%s", _objectEntityId, f->name)] = f;
f++;
}
}
void Lingo::cleanUpTheEntities() {
_entityNames.clear();
_fieldNames.clear();
}
const char *Lingo::entity2str(int id) {
static char buf[20];
if (id && id < kTheMaxTheEntityType && !_entityNames[id].empty())
return _entityNames[id].c_str();
snprintf(buf, 19, "#%d", id);
return (const char *)buf;
}
const char *Lingo::field2str(int id) {
static char buf[20];
if (id && id < kTheMaxTheFieldType && !_fieldNames[id].empty())
return _fieldNames[id].c_str();
snprintf(buf, 19, "#%d", id);
return (const char *)buf;
}
#define getTheEntitySTUB(entity) \
warning("Lingo::getTheEntity(): Unprocessed getting entity %s", entity2str(entity));
Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
debugC(3, kDebugLingoThe, "Lingo::getTheEntity(%s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field));
debugC(3, kDebugLingoExec, "Lingo::getTheEntity(%s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field));
Datum d;
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::getTheEntity(): Movie is missing");
d.type = VOID;
return d;
}
g_debugger->entityReadHook(entity, field);
LingoArchive *mainArchive = movie->getMainLingoArch();
Score *score = movie->getScore();
switch (entity) {
case kTheActiveWindow:
{
Window *win = (Window *)g_director->_wm->getWindow(g_director->_wm->getActiveWindow());
if (win) {
d = Datum(win);
} else {
d.type = VOID;
}
}
break;
case kTheActorList:
d = g_lingo->_actorList;
break;
case kTheAlertHook:
warning("STUB: the alertHook");
break;
case kTheApplicationPath:
warning("STUB: the applicationPath");
break;
case kTheBeepOn:
d = (int)movie->_isBeepOn;
break;
case kTheButtonStyle:
d = (int)(g_director->_wm->_mode & Graphics::kWMModeButtonDialogStyle);
break;
case kTheCast:
d = getTheCast(id, field);
break;
case kTheCastLibs: // D5
d = getCastLibsNum();
break;
case kTheCastMembers:
{
uint16 castLibID = 0;
if (g_director->getVersion() >= 500) {
LB::b_castLib(1);
castLibID = (uint16)g_lingo->pop().u.i;
}
d = getMembersNum(castLibID);
}
break;
case kTheCenterStage:
d = g_director->_centerStage;
break;
case kTheCheckBoxAccess:
d = movie->_checkBoxAccess;
break;
case kTheCheckBoxType:
d = movie->_checkBoxType;
break;
case kTheChunk:
d = getTheChunk(id, field);
break;
case kTheClickLoc:
d.u.farr = new FArray;
d.u.farr->arr.push_back(movie->_lastClickPos.x);
d.u.farr->arr.push_back(movie->_lastClickPos.y);
d.type = POINT;
break;
case kTheClickOn:
// Even in D4, `the clickOn` uses the old "active" sprite instead of mouse sprite.
d = (int)movie->_lastClickedSpriteId;
break;
case kTheColorDepth:
// bpp. 1, 2, 4, 8, 32
d = _vm->_colorDepth;
break;
case kTheColorQD:
d = 1;
break;
case kTheCommandDown:
if (g_director->getPlatform() == Common::kPlatformWindows)
d = (movie->_keyFlags & Common::KBD_CTRL) ? 1 : 0;
else
d = (movie->_keyFlags & Common::KBD_META) ? 1 : 0;
break;
case kTheControlDown:
d = (movie->_keyFlags & Common::KBD_CTRL) ? 1 : 0;
break;
case kTheCpuHogTicks:
// Mac-onlym specifies how often Director yeilds to other applications.
// Default is 20 ticks (1/3 second)
d = 20;
break;
case kTheCurrentSpriteNum:
d = (int)movie->_currentSpriteNum;
break;
case kTheDate:
d = getTheDate(field);
break;
case kTheDeskTopRectList:
d = getTheDeskTopRectList();
break;
case kTheDoubleClick:
// Always measured against the last two clicks.
// 25 ticks seems to be the threshold for a double click.
d = (movie->_lastClickTime - movie->_lastClickTime2) <= 25 ? 1 : 0;
break;
case kTheEmulateMultiButtonMouse:
d = g_director->_emulateMultiButtonMouse ? 1 : 0;
break;
case kTheExitLock:
d = g_lingo->_exitLock;
break;
case kTheField:
d = getTheField(id, field);
break;
case kTheFixStageSize:
d = (int)g_director->_fixStageSize;
break;
case kTheFloatPrecision:
d = _floatPrecision;
break;
case kTheFrame:
d = (int)score->getCurrentFrameNum();
break;
case kTheFrameLabel:
d.type = STRING;
d.u.s = score->getFrameLabel(score->getCurrentFrameNum());
break;
case kTheFramePalette:
d = score->getCurrentPalette().toMultiplex();
break;
case kTheFrameScript:
d = score->_currentFrame->_mainChannels.actionId.toMultiplex();
break;
case kTheFrameSound1:
d = score->_currentFrame->_mainChannels.sound1.toMultiplex();
break;
case kTheFrameSound2:
d = score->_currentFrame->_mainChannels.sound2.toMultiplex();
break;
case kTheFrameTempo:
d = score->_currentFrameRate;
break;
case kTheFrameTransition:
d = score->_currentFrame->_mainChannels.trans.toMultiplex();
break;
case kTheFreeBlock:
case kTheFreeBytes:
d = 32 * 1024 * 1024; // Let's have 32 Mbytes
break;
case kTheFullColorPermit:
d = 1; // We always allow it in ScummVM
break;
case kTheImageDirect:
d = 1; // We always allow it in ScummVM
break;
case kTheItemDelimiter:
{
Common::U32String ch(g_lingo->_itemDelimiter);
d.type = STRING;
d.u.s = new Common::String(ch, Common::kUtf8);
}
break;
case kTheKey:
if (movie->_key < 0x80) {
d = Common::String::format("%c", (char)movie->_key);
} else {
d = Common::String();
}
break;
case kTheKeyCode:
d = movie->_keyCode;
break;
case kTheKeyDownScript:
d.type = STRING;
if (mainArchive->primaryEventHandlers.contains(kEventKeyDown))
d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventKeyDown]);
else
d.u.s = new Common::String();
break;
case kTheKeyPressed:
{
Common::U32String buf;
buf.insertChar(movie->_key, 0);
d = buf.encode();
}
break;
case kTheKeyUpScript:
d.type = STRING;
if (mainArchive->primaryEventHandlers.contains(kEventKeyUp))
d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventKeyUp]);
else
d.u.s = new Common::String();
break;
case kTheLabelList:
d.type = STRING;
d.u.s = score->getLabelList();
break;
case kTheLastClick:
d = (int)(_vm->getMacTicks() - movie->_lastClickTime);
break;
case kTheLastEvent:
d = (int)(_vm->getMacTicks() - movie->_lastEventTime);
break;
case kTheLastFrame:
d = (int)score->getFramesNum();
break;
case kTheLastKey:
d = (int)(_vm->getMacTicks() - movie->_lastKeyTime);
break;
case kTheLastRoll:
d = (int)(_vm->getMacTicks() - movie->_lastRollTime);
break;
case kTheMachineType:
// These are actually coming from Gestalt machineType
//
// 1 - Macintosh 512Ke D2
// 2 - Macintosh Plus D2
// 3 - Macintosh SE D2
// 4 - Macintosh II D2
// 5 - Macintosh IIx D2
// 6 - Macintosh IIcx D2
// 7 - Macintosh SE/30 D2
// 8 - Macintosh Portable D2
// 9 - Macintosh IIci D2
// 11 - Macintosh IIfx D3
// 15 - Macintosh Classic D3
// 16 - Macintosh IIsi D3
// 17 - Macintosh LC D3
// 18 - Macintosh Quadra 900 D3
// 19 - PowerBook 170 D3
// 20 - Macintosh Quadra 700 D3
// 21 - Classic II D3
// 22 - PowerBook 100 D3
// 23 - PowerBook 140 D3
// 24 - Macintosh Quadra 950 D4
// 25 - PowerBook Duo 210 D4
// 27 - Macintosh LCIII D4
// 28 - Macintosh Centris 650 D4
// 30 - PowerBook Duo 230 D4
// 31 - PowerBook 180 D4
// 32 - PowerBook 160 D4
// 33 - Macintosh Quadra 800 D4
// 35 - Macintosh LC II D4
// 42 - Macintosh IIvi D4
// 45 - Power Macintosh 7100/70 D5
// 46 - Macintosh IIvx D4
// 47 - Macintosh Color Classic D4
// 48 - PowerBook 165c D4
// 50 - Macintosh Centris 610 D4
// 52 - PowerBook 145 D4
// 53 - PowerComputing 8100/100 D5
// 70 - PowerBook 540C D6 // "Director 6 Demystified" p.818
// 73 - Power Macintosh 6100/60 D5
// 76 - Macintosh Quadra 840av D4
// 256 - IBM PC-type machine D3
d = _vm->_machineType;
break;
case kTheMaxInteger:
d = 2147483647; // (2^31)-1 [max 32bit signed integer]
break;
case kTheMemorySize:
d = 32 * 1024 * 1024; // Let's have 32 Mbytes
break;
case kTheMenu:
if (!g_director->_wm->getMenu()) {
warning("Lingo::getTheEntity(): Menu does not exist!");
break;
}
d.type = STRING;
Graphics::MacMenuItem *menuRef;
menuRef = nullptr;
if (id.u.menu->menuIdNum == -1) {
menuRef = g_director->_wm->getMenu()->getMenuItem(*id.u.menu->menuIdStr);
} else {
menuRef = g_director->_wm->getMenu()->getMenuItem(id.u.menu->menuIdNum - 1);
}
d.u.s = new Common::String();
*d.u.s = g_director->_wm->getMenu()->getName(menuRef);
break;
case kTheMenuItem:
if (!g_director->_wm->getMenu()) {
warning("Lingo::getTheEntity(): Menu does not exist!");
break;
}
Graphics::MacMenuItem *menu, *menuItem;
menu = nullptr, menuItem = nullptr;
if (id.u.menu->menuIdNum == -1) {
menu = g_director->_wm->getMenu()->getMenuItem(*id.u.menu->menuIdStr);
} else {
menu = g_director->_wm->getMenu()->getMenuItem(id.u.menu->menuIdNum - 1);
}
if (id.u.menu->menuItemIdNum == -1) {
menuItem = g_director->_wm->getMenu()->getSubMenuItem(menu, *id.u.menu->menuItemIdStr);
} else {
menuItem = g_director->_wm->getMenu()->getSubMenuItem(menu, id.u.menu->menuItemIdNum - 1);
}
switch (field) {
case kTheCheckMark:
d = g_director->_wm->getMenuItemCheckMark(menuItem);
break;
case kTheEnabled:
d = g_director->_wm->getMenuItemEnabled(menuItem);
break;
case kTheName:
d.type = STRING;
d.u.s = new Common::String;
*(d.u.s) = g_director->_wm->getMenuItemName(menuItem);
break;
case kTheScript:
d = g_director->_wm->getMenuItemAction(menuItem);
break;
default:
warning("Lingo::getTheEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
break;
}
break;
case kTheMenuItems:
d = getMenuItemsNum(id);
break;
case kTheMenus:
d = getMenuNum();
break;
case kTheMouseCast:
{
// TODO: How is this handled with multiple casts in D5?
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
if (spriteId) {
d = score->getSpriteById(spriteId)->_castId.toMultiplex();
} else {
d = 0;
}
}
break;
case kTheMouseChar:
{
// maybe a better handling is iterate channels and check the text sprite that enclose the cursor
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
Channel *ch = score->getChannelById(spriteId);
d = ch->getMouseChar(pos.x, pos.y);
}
break;
case kTheMouseDown:
d = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_LEFT | 1 << Common::MOUSE_BUTTON_RIGHT) ? 1 : 0;
break;
case kTheMouseDownScript:
d.type = STRING;
if (mainArchive->primaryEventHandlers.contains(kEventMouseDown))
d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventMouseDown]);
else
d.u.s = new Common::String();
break;
case kTheMouseH:
d = g_director->getCurrentWindow()->getMousePos().x;
break;
case kTheMouseItem:
{
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
Channel *ch = score->getChannelById(spriteId);
d = ch->getMouseItem(pos.x, pos.y);
}
break;
case kTheMouseLine:
{
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
Channel *ch = score->getChannelById(spriteId);
d = ch->getMouseLine(pos.x, pos.y);
}
break;
case kTheMouseMember:
{
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
if (spriteId) {
d = score->getSpriteById(spriteId)->_cast;
} else {
d = getVoid();
}
}
break;
case kTheMouseUp:
d = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_LEFT | 1 << Common::MOUSE_BUTTON_RIGHT) ? 0 : 1;
break;
case kTheMouseUpScript:
d.type = STRING;
if (mainArchive->primaryEventHandlers.contains(kEventMouseUp))
d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventMouseUp]);
else
d.u.s = new Common::String();
break;
case kTheMouseV:
d = g_director->getCurrentWindow()->getMousePos().y;
break;
case kTheMouseWord:
{
// same issue as MouseChar, check MouseChar above
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
uint16 spriteId = score->getSpriteIDFromPos(pos);
Channel *ch = score->getChannelById(spriteId);
d = ch->getMouseWord(pos.x, pos.y);
}
break;
case kTheMovie:
case kTheMovieName:
d.type = STRING;
d.u.s = new Common::String(movie->getMacName());
break;
case kTheMovieFileFreeSize:
d = 0; // Let's pretend the movie is compactified
break;
case kTheMovieFileSize:
d = (int)movie->getArchive()->getFileSize();
break;
case kTheMoviePath:
case kThePathName:
d.type = STRING;
d.u.s = new Common::String(_vm->getCurrentAbsolutePath());
break;
case kTheMultiSound:
// We always support multiple sound channels!
d = 1;
break;
case kTheNetThrottleTicks:
// This is default in Mac Director.
// Specifies the frequency of servicing to a network
d = 15;
break;
case kTheOptionDown:
d = (movie->_keyFlags & Common::KBD_ALT) ? 1 : 0;
break;
case kTheOrganizationName:
d = Common::String("ScummVM Team");
break;
case kTheParamCount:
d = g_lingo->_state->callstack[g_lingo->_state->callstack.size() - 1]->paramCount;
break;
case kThePauseState:
d = (int) g_director->_playbackPaused;
break;
case kThePerFrameHook:
d = _perFrameHook;
break;
case kThePlatform: // D5
// ScummVM doesn't track different OS versions;
// for now let's assume every game from D5 onwards was
// x86-32 or PowerPC.
if (g_director->getPlatform() == Common::kPlatformWindows) {
if (g_director->getVersion() >= 500) {
d = Datum("Windows,32");
} else {
d = Datum("Windows,16");
}
} else {
// Macintosh or pippin
if (g_director->getVersion() >= 500) {
d = Datum("Macintosh,PowerPC");
} else {
d = Datum("Macintosh,68k");
}
}
break;
case kThePreloadEventAbort:
d = g_lingo->_preLoadEventAbort;
break;
case kThePreLoadRAM:
d = 0; // We always have unlimited RAM
break;
case kThePi:
d = M_PI;
break;
case kTheProductName:
d = Common::String("Director");
break;
case kTheProductVersion:
d = Common::String::format("%d.%d.%d",
g_director->getVersion() / 100, (g_director->getVersion() / 10) % 10, g_director->getVersion() % 10);
break;
case kTheQuickTimePresent:
// QuickTime is always present for ScummVM
d = 1;
break;
case kTheRandomSeed:
d = (int)g_director->_rnd.getSeed();
break;
case kTheResult:
d = g_lingo->_theResult;
break;
case kTheRightMouseDown:
d = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_RIGHT) ? 1 : 0;
break;
case kTheRightMouseUp:
d = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_RIGHT) ? 0 : 1;
break;
case kTheRollOver:
d = score->getSpriteIDFromPos(g_director->getCurrentWindow()->getMousePos());
break;
case kTheRomanLingo:
d = g_lingo->_romanLingo;
warning("BUILDBOT: the romanLingo is get, value is %d", g_lingo->_romanLingo);
break;
case kTheRunMode:
if (ConfMan.hasKey("director_runMode"))
d = Datum(ConfMan.get("director_runMode"));
else {
d = Datum("Projector");
}
break;
case kTheScummvmVersion:
d = _vm->getVersion();
break;
case kTheSearchCurrentFolder:
//We always allow searching in current folder
warning("BUILDBOT: SearchCurrentFolder is queried");
d = 1;
break;
case kTheSearchPath:
case kTheSearchPaths:
d = g_lingo->_searchPath;
break;
case kTheSelection:
if (movie->_currentEditableTextChannel) {
Channel *channel = score->_channels[movie->_currentEditableTextChannel];
if (channel->_widget) {
d.type = STRING;
d.u.s = new Common::String(Common::convertFromU32String(((Graphics::MacText *)channel->_widget)->getSelection(false, false)));
}
}
break;
case kTheSelEnd:
case kTheSelStart:
if (movie->_currentEditableTextChannel) {
Channel *channel = score->_channels[movie->_currentEditableTextChannel];
if (channel->_widget) {
d = (int)((Graphics::MacText *)channel->_widget)->getSelectionIndex(entity == kTheSelStart);
}
}
break;
case kTheSerialNumber:
d = Common::String("DRW600-01234-56789-01234");
break;
case kTheShiftDown:
d = (movie->_keyFlags & Common::KBD_SHIFT) ? 1 : 0;
break;
case kTheSoundEnabled:
d = _vm->getCurrentWindow()->getSoundManager()->getSoundEnabled();
break;
case kTheSoundEntity:
{
switch (field) {
case kTheVolume:
d = _vm->getCurrentWindow()->getSoundManager()->getChannelVolume(id.asInt());
break;
default:
warning("Lingo::getTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
break;
}
}
break;
case kTheSoundKeepDevice:
// System property; for Windows only, prevents the sound driver from unloading
// and reloading each time a sound needs to play. The default value is TRUE.
d = 1;
break;
case kTheSoundLevel:
// getting sound level of channel 1, maybe need to be amended in higher version
d = _vm->getCurrentWindow()->getSoundManager()->getChannelVolume(1) / 32;
break;
case kTheSprite:
d = getTheSprite(id, field);
break;
case kTheStage:
d = _vm->getStage();
break;
case kTheStageBottom:
{
Window *window = _vm->getCurrentWindow();
d = window->_window->getInnerDimensions().bottom;
}
break;
case kTheStageColor:
// TODO: Provide proper reverse transform for non-indexed color
d = (int)g_director->transformColor(g_director->getCurrentWindow()->getStageColor());
break;
case kTheStageLeft:
{
Window *window = _vm->getCurrentWindow();
d = window->_window->getInnerDimensions().left;
}
break;
case kTheStageRight:
{
Window *window = _vm->getCurrentWindow();
d = window->_window->getInnerDimensions().right;
}
break;
case kTheStageTop:
{
Window *window = _vm->getCurrentWindow();
d = window->_window->getInnerDimensions().top;
}
break;
case kTheStillDown:
d = _vm->_wm->_mouseDown;
break;
case kTheSwitchColorDepth:
getTheEntitySTUB(kTheSwitchColorDepth);
break;
case kTheTicks:
d = (int)_vm->getMacTicks();
break;
case kTheTime:
d = getTheTime(field);
break;
case kTheTimeoutKeyDown:
d= movie->_timeOutKeyDown;
break;
case kTheTimeoutLapsed:
d = (int)(_vm->getMacTicks() - movie->_lastTimeOut);
break;
case kTheTimeoutLength:
d = (int)movie->_timeOutLength;
break;
case kTheTimeoutMouse:
d = movie->_timeOutMouse;
break;
case kTheTimeoutPlay:
d = movie->_timeOutPlay;
break;
case kTheTimeoutScript:
d.type = STRING;
if (mainArchive->primaryEventHandlers.contains(kEventTimeout))
d.u.s = new Common::String(mainArchive->primaryEventHandlers[kEventTimeout]);
else
d.u.s = new Common::String();
break;
case kTheTimer:
d = (int)(_vm->getMacTicks() - movie->_lastTimerReset);
break;
case kTheTrace:
d = (int) g_lingo->_trace;
break;
case kTheTraceLoad:
d = g_lingo->_traceLoad;
break;
case kTheTraceLogFile:
d.type = STRING;
d.u.s = new Common::String(g_director->_traceLogFile.toString(Common::Path::kNativeSeparator));
break;
case kTheUpdateMovieEnabled:
d = g_lingo->_updateMovieEnabled;
break;
case kTheUserName:
d = Common::String("ScummVM");
break;
case kTheVideoForWindowsPresent:
// Video For Windows is always present for ScummVM
d = 1;
break;
case kTheWindow:
g_lingo->push(id);
LB::b_window(1);
d = g_lingo->pop().u.obj->getField(field);
break;
case kTheWindowList:
d = g_lingo->_windowList;
break;
case kTheXtras: // D5
d = getXtrasNum();
break;
default:
warning("Lingo::getTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
break;
}
return d;
}
#define setTheEntitySTUB(entity) \
warning("Lingo::setTheEntity(): Unprocessed setting entity %s", entity2str(entity));
#define setTheEntityReadOnly(entity) \
warning("Lingo::setTheEntity: Attempt to set read-only entity %s", entity2str(entity));
void Lingo::setTheEntity(int entity, Datum &id, int field, Datum &d) {
debugC(3, kDebugLingoThe, "Lingo::setTheEntity(%s, %s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field), d.asString(true).c_str());
debugC(3, kDebugLingoExec, "Lingo::setTheEntity(%s, %s, %s, %s)", entity2str(entity), id.asString(true).c_str(), field2str(field), d.asString(true).c_str());
Movie *movie = _vm->getCurrentMovie();
Score *score = movie->getScore();
switch (entity) {
case kTheActorList:
g_lingo->_actorList = d;
break;
case kTheAlertHook:
warning("STUB: the alertHook");
break;
case kTheBeepOn:
movie->_isBeepOn = (bool)d.u.i;
break;
case kTheButtonStyle:
if (d.asInt())
g_director->_wm->_mode = g_director->_wmMode | Graphics::kWMModeButtonDialogStyle;
else
g_director->_wm->_mode = g_director->_wmMode;
break;
case kTheCast:
setTheCast(id, field, d);
break;
case kTheCenterStage:
g_director->_centerStage = d.asInt();
break;
case kTheCheckBoxAccess:
movie->_checkBoxAccess = d.asInt();
break;
case kTheCheckBoxType:
movie->_checkBoxType = d.asInt();
break;
case kTheChunk:
setTheChunk(id, field, d);
break;
case kTheColorDepth:
_vm->_colorDepth = d.asInt();
// bpp. 1, 2, 4, 8, 32
warning("STUB: Lingo::setTheEntity(): Set color depth to %d", _vm->_colorDepth);
break;
case kTheCpuHogTicks:
// We do not need to do anything special to yield to other applications
// so, ignore this setting
break;
case kTheExitLock:
g_lingo->_exitLock = bool(d.asInt());
break;
case kTheEmulateMultiButtonMouse:
g_director->_emulateMultiButtonMouse = (bool)d.asInt();
break;
case kTheFixStageSize:
g_director->_fixStageSize = (bool)d.u.i;
if (d.u.i) {
g_director->_fixStageRect = movie->_movieRect;
}
break;
case kTheField:
setTheField(id, field, d);
break;
case kTheFloatPrecision:
_floatPrecision = d.asInt();
_floatPrecision = MAX(0, MIN(_floatPrecision, 19)); // 0 to 19
_floatPrecisionFormat = Common::String::format("%%.%df", _floatPrecision);
break;
case kTheFrameLabel:
// TODO: All of these frame properties are settable during a score recording session in D5+
setTheEntityReadOnly(kTheFrameLabel);
break;
case kTheFramePalette:
setTheEntityReadOnly(kTheFramePalette);
break;
case kTheFrameScript:
setTheEntityReadOnly(kTheFrameScript);
break;
case kTheFrameSound1:
setTheEntityReadOnly(kTheFrameSound1);
break;
case kTheFrameSound2:
setTheEntityReadOnly(kTheFrameSound2);
break;
case kTheFrameTempo:
setTheEntityReadOnly(kTheFrameTempo);
break;
case kTheFrameTransition:
setTheEntityReadOnly(kTheFrameTransition);
break;
case kTheFullColorPermit:
// No op in ScummVM. We always allow it
break;
case kTheImageDirect:
// No op in ScummVM. We always allow it
break;
case kTheItemDelimiter:
if (d.asString().size() == 0)
g_lingo->_itemDelimiter = 0;
else
g_lingo->_itemDelimiter = d.asString().decode(Common::kUtf8)[0];
break;
case kTheKeyDownScript:
movie->setPrimaryEventHandler(kEventKeyDown, d.asString());
break;
case kTheKeyUpScript:
movie->setPrimaryEventHandler(kEventKeyUp, d.asString());
break;
case kTheMenu:
setTheEntitySTUB(kTheMenu);
break;
case kTheMenuItem:
Graphics::MacMenuItem *menu, *menuItem;
menu = nullptr, menuItem = nullptr;
if (!g_director->_wm->getMenu()) {
warning("Lingo::setTheEntity(): Menu does not exist!");
break;
}
if (id.u.menu->menuIdNum == -1) {
menu = g_director->_wm->getMenu()->getMenuItem(*id.u.menu->menuIdStr);
} else {
menu = g_director->_wm->getMenu()->getMenuItem(id.u.menu->menuIdNum - 1);
}
if (id.u.menu->menuItemIdNum == -1) {
menuItem = g_director->_wm->getMenu()->getSubMenuItem(menu, *id.u.menu->menuItemIdStr);
} else {
menuItem = g_director->_wm->getMenu()->getSubMenuItem(menu, id.u.menu->menuItemIdNum - 1);
}
if (!menuItem) {
warning("Wrong menuItem!");
break;
}
switch (field) {
case kTheCheckMark:
g_director->_wm->setMenuItemCheckMark(menuItem, d.asInt());
break;
case kTheEnabled:
g_director->_wm->setMenuItemEnabled(menuItem, d.asInt());
break;
case kTheName:
g_director->_wm->setMenuItemName(menuItem, d.asString());
break;
case kTheScript:
{
LingoArchive *mainArchive = movie->getMainLingoArch();
int commandId = 100;
while (mainArchive->getScriptContext(kEventScript, commandId))
commandId++;
mainArchive->replaceCode(d.asString(), kEventScript, commandId);
g_director->_wm->setMenuItemAction(menuItem, commandId);
}
break;
default:
warning("Lingo::setTheEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
break;
}
break;
case kTheMouseDownScript:
movie->setPrimaryEventHandler(kEventMouseDown, d.asString());
break;
case kTheMouseUpScript:
movie->setPrimaryEventHandler(kEventMouseUp, d.asString());
break;
case kTheNetThrottleTicks:
// No op, we always smooth on network operations
break;
case kThePerFrameHook:
_perFrameHook = d;
break;
case kThePlatform:
setTheEntityReadOnly(kThePlatform);
break;
case kThePreloadEventAbort:
g_lingo->_preLoadEventAbort = bool(d.asInt());
break;
case kThePreLoadRAM:
// We always have the unlimited RAM, ignore
break;
case kTheRandomSeed:
g_director->_rnd.setSeed(d.asInt());
break;
case kTheRomanLingo:
g_lingo->_romanLingo = bool(d.asInt());
// Catching when we set to Japanese
if (!g_lingo->_romanLingo) {
warning("BUILDBOT: the romanLingo is set to %d", g_lingo->_romanLingo);
setTheEntitySTUB(kTheRomanLingo);
}
break;
case kTheScummvmVersion:
// Allow director version change: used for testing version differences via the lingo tests.
_vm->setVersion(d.asInt());
break;
case kTheSearchCurrentFolder:
warning("BUILDBOT: Trying to set SearchCurrentFolder lingo property");
break;
case kTheSearchPath:
case kTheSearchPaths:
g_lingo->_searchPath = d;
break;
case kTheSelEnd:
movie->_selEnd = d.asInt();
if (movie->_currentEditableTextChannel != 0) {
Channel *channel = score->getChannelById(movie->_currentEditableTextChannel);
if (channel->_widget)
(((Graphics::MacText *)channel->_widget)->setSelection(d.asInt(), false));
}
break;
case kTheSelStart:
movie->_selStart = d.asInt();
if (movie->_currentEditableTextChannel != 0) {
Channel *channel = score->getChannelById(movie->_currentEditableTextChannel);
if (channel->_widget)
(((Graphics::MacText *)channel->_widget)->setSelection(d.asInt(), true));
}
break;
case kTheSoundEnabled:
_vm->getCurrentWindow()->getSoundManager()->setSoundEnabled((bool)d.asInt());
break;
case kTheSoundEntity:
{
switch (field) {
case kTheVolume:
{
_vm->getCurrentWindow()->getSoundManager()->setChannelVolume(id.asInt(), MAX(0, MIN(d.asInt(), 255)));
}
break;
default:
warning("Lingo::setTheEntity(): Unprocessed getting field \"%s\" of entity %s", field2str(field), entity2str(entity));
break;
}
}
break;
case kTheSoundKeepDevice:
// We do not need to unload the sound driver, so just ignore this.
break;
case kTheSoundLevel:
// setting all of the channel for now
_vm->getCurrentWindow()->getSoundManager()->setChannelVolume(-1, MAX(0, MIN(d.asInt() * 32, 255)));
break;
case kTheSprite:
setTheSprite(id, field, d);
break;
case kTheStage:
setTheEntitySTUB(kTheStage);
break;
case kTheStageColor:
g_director->getCurrentWindow()->setStageColor(g_director->transformColor(d.asInt()));
// Redraw the stage
score->updateSprites(kRenderForceUpdate);
g_director->getCurrentWindow()->render();
break;
case kTheSwitchColorDepth:
setTheEntitySTUB(kTheSwitchColorDepth);
break;
case kTheTimeoutKeyDown:
movie->_timeOutKeyDown = d.asInt();
break;
case kTheTimeoutLapsed:
// timeOutLapsed can be set in D4, but can't in D3. see D3.1 interactivity manual p312 and D4 dictionary p296.
if (g_director->getVersion() >= 400 && (d.type == INT || d.type == FLOAT)) {
g_director->_tickBaseline = (int)g_director->getMacTicks() - d.asInt();
}
if (d.type != INT) {
warning("Lingo::setTheEntity() : Wrong DatumType %d for setting of Lingo Property timeOutLapsed", d.type);
}
break;
case kTheTimeoutLength:
movie->_timeOutLength = d.asInt();
break;
case kTheTimeoutMouse:
movie->_timeOutMouse = d.asInt();
break;
case kTheTimeoutPlay:
movie->_timeOutPlay = d.asInt();
break;
case kTheTimeoutScript:
movie->setPrimaryEventHandler(kEventTimeout, d.asString());
break;
case kTheTimer:
// so value of the timer would be d.asInt()
movie->_lastTimerReset = _vm->getMacTicks() - d.asInt();
break;
case kTheTrace:
g_lingo->_trace = (bool) d.asInt();
break;
case kTheTraceLoad:
g_lingo->_traceLoad = d.asInt();
break;
case kTheTraceLogFile:
{
if (d.asString().size()) {
Common::Path logPath = ConfMan.getPath("path").appendComponent(d.asString());
Common::FSNode out(logPath);
if (!out.exists())
out.createWriteStream(false);
if (out.isWritable())
g_director->_traceLogFile = logPath;
else
warning("traceLogFile '%s' is not writeable", logPath.toString(Common::Path::kNativeSeparator).c_str());
} else {
g_director->_traceLogFile.clear();
}
}
break;
case kTheUpdateMovieEnabled:
g_lingo->_updateMovieEnabled = bool(d.asInt());
break;
case kTheWindow:
g_lingo->push(id);
LB::b_window(1);
g_lingo->pop().u.obj->setField(field, d);
break;
case kTheWindowList:
if (d.type == ARRAY) {
g_lingo->_windowList = d;
} else {
warning("Lingo::setTheEntity(): kTheWindowList must be a list");
}
break;
default:
warning("Lingo::setTheEntity(): Unprocessed setting field \"%s\" of entity %s", field2str(field), entity2str(entity));
}
g_debugger->entityWriteHook(entity, field);
}
int Lingo::getMenuNum() {
if (!g_director->_wm->getMenu()) {
warning("Lingo::getMenuNum(): Menu does not exist!");
return 0;
}
return g_director->_wm->getMenu()->numberOfMenus();
}
int Lingo::getCastLibsNum() {
return _vm->getCurrentMovie()->getCasts()->size();
}
int Lingo::getMembersNum(uint16 castLibID) {
Movie *movie = _vm->getCurrentMovie();
Cast *cast = movie->getCast(CastMemberID(0, castLibID));
if (cast) {
return MAX(cast->getCastMaxID(), (movie->_sharedCast ? movie->_sharedCast->getCastMaxID() : 0));
}
return 0;
}
int Lingo::getXtrasNum() {
if (_openXtraObjects.size() < _openXtras.size()) {
warning("Lingo::getXtrasNum(): Mismatch between openXtras (%d) and openXtraObjects (%d)!", _openXtras.size(), _openXtraObjects.size());
}
return _openXtras.size();
}
int Lingo::getMenuItemsNum(Datum &d) {
if (d.type != MENUREF) {
warning("Datum of wrong type: Expected MENUREF, got '%d'", d.type);
return 0;
}
Graphics::MacMenuItem *menu = nullptr;
if (!g_director->_wm->getMenu()) {
warning("Lingo::getMenuItemsNum(): Menu does not exist!");
return 0;
}
if (d.u.menu->menuIdNum == -1) {
menu = g_director->_wm->getMenu()->getMenuItem(*d.u.menu->menuIdStr);
} else {
menu = g_director->_wm->getMenu()->getMenuItem(d.u.menu->menuIdNum);
}
return g_director->_wm->getMenu()->numberOfMenuItems(menu);
}
Datum Lingo::getTheSprite(Datum &id1, int field) {
Datum d;
int id = 0;
Score *score = _vm->getCurrentMovie()->getScore();
if (!score) {
warning("Lingo::getTheSprite(): The sprite %d field \"%s\" setting over non-active score", id, field2str(field));
return d;
}
if ((id1.type == SPRITEREF) || (id1.type == INT)) {
id = id1.u.i;
} else {
warning("Lingo::getTheSprite(): Unknown the sprite id type: %s", id1.type2str());
return d;
}
Channel *channel = score->getChannelById(id);
if (!channel)
return d;
Sprite *sprite = channel->_sprite;
if (!sprite)
return d;
switch (field) {
case kTheBackColor:
// TODO: Provide proper reverse transform for non-indexed color
d = (int)g_director->transformColor(sprite->_backColor);
break;
case kTheBlend:
d = (255 - sprite->_blendAmount) * 100 / 255;
break;
case kTheBottom:
d = channel->getBbox().bottom;
break;
case kTheMember:
d = sprite->_castId;
break;
case kTheCastNum:
if (g_director->getVersion() >= 500) {
// For the castNum, D5 will multiplex the castLib ID into the result.
// the memberNum will just give you the member ID.
d = sprite->_castId.toMultiplex();
} else {
d = sprite->_castId.member;
}
break;
case kTheCastLibNum:
d = sprite->_castId.castLib;
break;
case kTheMemberNum:
d = sprite->_castId.member;
break;
case kTheConstraint:
d = (int)channel->_constraint;
break;
case kTheCursor:
d = channel->_cursor._cursorResId;
break;
case kTheEditableText:
d = sprite->_editable;
break;
case kTheFlipH: // D7
d = (sprite->_thickness & kTFlipH) ? 1 : 0;
break;
case kTheFlipV: // D7
d = (sprite->_thickness & kTFlipV) ? 1 : 0;
break;
case kTheForeColor:
// TODO: Provide proper reverse transform for non-indexed color
d = (int)g_director->transformColor(sprite->_foreColor);
break;
case kTheHeight:
d = channel->getHeight();
break;
case kTheImmediate:
d = sprite->_immediate;
break;
case kTheInk:
d = sprite->_ink;
break;
case kTheLeft:
d = channel->getBbox().left;
break;
case kTheLineSize:
d = (sprite->_thickness & kTThickness) - 1;
break;
case kTheLoc:
{
Common::Point position = channel->getPosition();
d.type = POINT;
d.u.farr = new FArray;
d.u.farr->arr.push_back(position.x);
d.u.farr->arr.push_back(position.y);
}
break;
case kTheLocH:
d = channel->getPosition().x;
break;
case kTheLocV:
d = channel->getPosition().y;
break;
case kTheMostRecentCuePoint:
warning("STUB: the mostRecentCuePoint");
d = 0;
break;
case kTheMoveableSprite:
d = sprite->_moveable;
break;
case kTheMovieRate:
d = channel->_movieRate;
if (debugChannelSet(-1, kDebugEndVideo))
d.u.f = 0.0;
break;
case kTheMovieTime:
d = channel->_movieTime;
break;
case kThePattern:
d = sprite->getPattern();
break;
case kThePuppet:
d = sprite->_puppet;
break;
case kTheRect:
// let compiler to optimize this
{
Common::Rect bbox = channel->getBbox();
d.type = RECT;
d.u.farr = new FArray;
d.u.farr->arr.push_back(bbox.left);
d.u.farr->arr.push_back(bbox.top);
d.u.farr->arr.push_back(bbox.right);
d.u.farr->arr.push_back(bbox.bottom);
}
break;
case kTheRight:
d = channel->getBbox().right;
break;
case kTheScoreColor:
//Check the last 3 bits of the _colorcode byte as value lies in 0 to 5
d = (int)(sprite->_colorcode & 0x7);
break;
case kTheScriptInstanceList:
warning("STUB: Getting the scriptInstanceList");
d.type = PARRAY;
d.u.parr = new PArray;
break;
case kTheScriptNum:
if (g_director->getVersion() >= 600) {
if (sprite->_behaviors.size() > 0)
d = sprite->_behaviors[0].memberID.toMultiplex(); // Return the first script in the list
else
d = 0;
} else {
d = sprite->_scriptId.member;
}
break;
case kTheSpriteNum:
warning("STUB: Getting the spriteNum");
d = 0;
break;
case kTheStartTime:
d = channel->_startTime;
break;
case kTheStopTime:
d = channel->_stopTime;
break;
case kTheStretch:
d = (sprite->_stretch ? 1 : 0);
break;
case kTheTop:
d = channel->getBbox().top;
break;
case kTheTrails:
d = (sprite->_trails ? 1 : 0);
break;
case kTheType:
d = sprite->_spriteType;
break;
case kTheTweened: // D6
d = (sprite->_thickness & kTTweened) ? 1 : 0;
break;
case kTheVisibility:
case kTheVisible:
d = (channel->_visible ? 1 : 0);
break;
case kTheVolume:
d = sprite->_volume;
break;
case kTheWidth:
d = channel->getWidth();
break;
default:
warning("Lingo::getTheSprite(): Unprocessed getting field \"%s\" of sprite", field2str(field));
d.type = VOID;
}
return d;
}
void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
Movie *movie = _vm->getCurrentMovie();
Score *score = movie->getScore();
int id = 0;
if (!score) {
warning("Lingo::setTheSprite(): The sprite %d field \"%s\" setting over non-active score", id, field2str(field));
return;
}
if ((id1.type == SPRITEREF) || (id1.type == INT)) {
id = id1.u.i;
} else {
warning("Lingo::setTheSprite(): Unknown the sprite id type: %s", id1.type2str());
return;
}
Channel *channel = score->getChannelById(id);
if (!channel)
return;
Sprite *sprite = channel->_sprite;
if (!sprite)
return;
if (!sprite->_enabled)
sprite->_enabled = true;
switch (field) {
case kTheBackColor:
{
uint32 newColor = g_director->transformColor(d.asInt());
if (newColor != sprite->_backColor) {
sprite->_backColor = newColor;
channel->_dirty = true;
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPBackColor, true);
}
}
break;
case kTheBlend:
{
// Convert from (0, 100) range to (0xff, 0x00)
int blend = (100 - CLIP(d.asInt(), 0, 100)) * 255 / 100;
if (blend != sprite->_blendAmount) {
sprite->_blendAmount = blend;
channel->_dirty = true;
}
if (d.asInt() == 0)
sprite->_thickness &= ~kTHasBlend;
else
sprite->_thickness |= kTHasBlend;
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPBlend, true);
sprite->setAutoPuppet(kAPThickness, true);
}
break;
case kTheMember:
{
CastMemberID targetMember = d.asMemberID();
if (targetMember != sprite->_castId) {
movie->getWindow()->addDirtyRect(channel->getBbox());
channel->setCast(targetMember);
// Ensure the new sprite, whether larger or smaller, appears correctly on the screen
movie->getWindow()->addDirtyRect(channel->getBbox());
channel->_dirty = true;
}
}
break;
case kTheCastNum:
case kTheCastLibNum:
case kTheMemberNum:
{
CastMemberID castId = d.asMemberID();
if (field == kTheMemberNum) {
// If the number is multiplexed with a castLib 2 or higher, the castLib will be used.
// Otherwise the existing castLib will be preserved.
if (castId.castLib == 1) {
castId = CastMemberID(castId.member, sprite->_castId.castLib);
}
} else if (field == kTheCastLibNum) {
castId = CastMemberID(sprite->_castId.member, d.asInt());
}
CastMember *castMember = movie->getCastMember(castId);
if (castMember && castMember->_type == kCastDigitalVideo) {
if (((DigitalVideoCastMember *)castMember)->loadVideoFromCast()) {
((DigitalVideoCastMember *)castMember)->setChannel(channel);
((DigitalVideoCastMember *)castMember)->startVideo();
// b_updateStage needs to have _videoPlayback set to render video
// in the regular case Score::updateSprites sets it.
// However Score::updateSprites is not in the current code path.
movie->_videoPlayback = true;
}
}
// Since Digital Video dimensions get clarified after loading,
// we enforce them here
if (castId != sprite->_castId || (castMember && castMember->_type == kCastDigitalVideo)) {
if (!sprite->_trails) {
movie->getWindow()->addDirtyRect(channel->getBbox());
channel->_dirty = true;
}
channel->setCast(castId);
channel->_dirty = true;
}
}
break;
case kTheConstraint:
{
int channelId = -1;
if (d.type == CASTREF) {
// Reference: CastMember ID
// Find the first channel that uses this cast.
CastMemberID memberID = *d.u.cast;
for (uint i = 0; i < score->_channels.size(); i++) {
if (score->_channels[i]->_sprite->_castId == memberID) {
channelId = i;
break;
}
}
} else {
channelId = d.asInt();
}
if (channelId != -1 && channelId != (int)channel->_constraint) {
channel->_constraint = d.u.i;
channel->_dirty = true;
}
}
break;
case kTheCursor:
if (d.type == INT) {
channel->_cursor.readFromResource(d);
} else {
channel->_cursor.readFromCast(d);
}
score->_cursorDirty = true;
break;
case kTheEditableText:
channel->_sprite->_editable = d.asInt();
break;
case kTheFlipH: // D7
sprite->_thickness = (sprite->_thickness & ~kTFlipH) | ((d.asInt() ? kTFlipH : 0));
channel->_dirty = true;
sprite->setAutoPuppet(kAPThickness, true);
warning("STUB: Sprite flipH was set to %d", d.asInt());
break;
case kTheFlipV: // D7
sprite->_thickness = (sprite->_thickness & ~kTFlipV) | ((d.asInt() ? kTFlipV : 0));
channel->_dirty = true;
sprite->setAutoPuppet(kAPThickness, true);
warning("STUB: Sprite flipV was set to %d", d.asInt());
break;
case kTheForeColor:
{
uint32 newColor = g_director->transformColor(d.asInt());
if (newColor != sprite->_foreColor) {
sprite->_foreColor = newColor;
channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPForeColor, true);
}
break;
case kTheHeight:
if (d.asInt() != channel->getHeight()) {
g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
channel->setHeight(d.asInt());
channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPHeight, true);
break;
case kTheImmediate:
sprite->_immediate = (bool)d.asInt();
break;
case kTheInk:
if (d.asInt() != sprite->_ink) {
sprite->_ink = static_cast<InkType>(d.asInt());
channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPInk, true);
break;
case kTheLineSize:
sprite->_thickness = (sprite->_thickness & ~kTThickness) | ((d.asInt() + 1) & kTThickness);
channel->_dirty = true;
sprite->setAutoPuppet(kAPThickness, true);
break;
case kTheLoc:
if (channel->getPosition() != d.asPoint()) {
movie->getWindow()->addDirtyRect(channel->getBbox());
channel->_dirty = true;
}
channel->setPosition(d.asPoint().x, d.asPoint().y);
break;
case kTheLocH:
if (d.asInt() != channel->getPosition().x) {
// Only add a dirty rectangle for the original position if we're not rendering in trails mode.
// Otherwise, it will erase the trail.
if (!channel->_sprite->_trails) {
movie->getWindow()->addDirtyRect(channel->getBbox());
}
channel->_dirty = true;
channel->setPosition(d.asInt(), channel->getPosition().y);
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPLocH, true);
break;
case kTheLocV:
if (d.asInt() != channel->getPosition().y) {
if (!channel->_sprite->_trails) {
movie->getWindow()->addDirtyRect(channel->getBbox());
}
channel->_dirty = true;
channel->setPosition(channel->getPosition().x, d.asInt());
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPLocV, true);
break;
case kTheMoveableSprite:
sprite->_moveable = (bool)d.asInt();
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPMoveable, true);
break;
case kTheMovieRate:
channel->_movieRate = d.asFloat();
if (sprite->_cast && sprite->_cast->_type == kCastDigitalVideo)
((DigitalVideoCastMember *)sprite->_cast)->setMovieRate(channel->_movieRate);
else
warning("Setting movieTime for non-digital video");
break;
case kTheMovieTime:
channel->_movieTime = d.asInt();
if (sprite->_cast->_type == kCastDigitalVideo)
((DigitalVideoCastMember *)sprite->_cast)->seekMovie(channel->_movieTime);
else
warning("Setting movieTime for non-digital video");
break;
case kThePattern:
if (d.asInt() != sprite->getPattern()) {
sprite->setPattern(d.asInt());
channel->_dirty = true;
}
break;
case kThePuppet:
sprite->_puppet = (bool)d.asInt();
if (!d.asInt()) {
// TODO: Properly reset sprite properties after puppet disabled.
sprite->_moveable = false;
}
break;
case kTheRect:
if (d.type == RECT || (d.type == ARRAY && d.u.farr->arr.size() >= 4)) {
score->updateSprites(kRenderForceUpdate);
channel->setBbox(
d.u.farr->arr[0].u.i, d.u.farr->arr[1].u.i,
d.u.farr->arr[2].u.i, d.u.farr->arr[3].u.i
);
channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPRect, true);
break;
case kTheScriptInstanceList:
warning("STUB: Setting the scriptInstanceList");
break;
case kTheStartTime:
channel->_startTime = d.asInt();
if (sprite->_cast->_type == kCastDigitalVideo)
((DigitalVideoCastMember *)sprite->_cast)->seekMovie(channel->_startTime);
else
warning("Setting startTime for non-digital video");
break;
case kTheStopTime:
channel->_stopTime = d.asInt();
if (sprite->_cast->_type == kCastDigitalVideo)
((DigitalVideoCastMember *)sprite->_cast)->setStopTime(channel->_stopTime);
else
warning("Setting stopTime for non-digital video");
break;
case kTheStretch:
channel->setStretch(d.asInt() != 0);
break;
case kTheTrails:
sprite->_trails = (d.asInt() ? true : false);
break;
case kTheType:
if (d.asInt() != sprite->_spriteType) {
sprite->_spriteType = static_cast<SpriteType>(d.asInt());
channel->_dirty = true;
}
break;
case kTheTweened: // D6
sprite->_thickness = (sprite->_thickness & ~kTTweened) | ((d.asInt() ? kTTweened : 0));
channel->_dirty = true;
sprite->setAutoPuppet(kAPThickness, true);
warning("STUB: Sprite tweened was set to %d", d.asInt());
break;
case kTheVisibility:
case kTheVisible:
if ((bool)d.asInt() != channel->_visible) {
channel->_visible = (bool)d.asInt();
channel->_dirty = true;
}
break;
case kTheVolume:
// TODO: Should changing digital video flags mark as dirty?
sprite->_volume = d.asInt();
break;
case kTheWidth:
if (d.asInt() != channel->getWidth()) {
g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
channel->setWidth(d.asInt());
channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPWidth, true);
break;
default:
warning("Lingo::setTheSprite(): Unprocessed setting field \"%s\" of sprite", field2str(field));
}
if (channel->_dirty)
movie->getWindow()->addDirtyRect(channel->getBbox());
}
Datum Lingo::getTheCast(Datum &id1, int field) {
Datum d;
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::getTheCast(): No movie loaded");
return d;
}
CastMemberID id = id1.asMemberID();
CastMember *member = movie->getCastMember(id);
if (!member) {
if (field == kTheLoaded) {
d = 0;
} else if (field == kTheNumber) {
d = -1;
} else if (id.member <= getMembersNum(id.castLib)) {
// If a cast member with the ID doesn't exist,
// but the ID isn't greater than the biggest cast member ID,
// Lingo will not crash, and instead return a VOID.
return d;
} else {
g_lingo->lingoError("Lingo::getTheCast(): CastMember %s not found", id1.asString().c_str());
}
return d;
}
if (field == kTheMedia) {
// every time "the media" is invoked, a new copy is made.
member->load();
d = Datum(member->duplicate(nullptr, 0));
return d;
}
if (!member->hasField(field)) {
warning("Lingo::getTheCast(): %s has no property '%s'", id.asString().c_str(), field2str(field));
return d;
}
d = member->getField(field);
return d;
}
void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::setTheCast(): No movie loaded");
return;
}
CastMemberID id = id1.asMemberID();
CastMember *member = movie->getCastMember(id);
if (!member) {
g_lingo->lingoError("Lingo::setTheCast(): %s not found", id.asString().c_str());
return;
}
if (field == kTheMedia) {
if (d.type != MEDIA) {
warning("Lingo::setTheCast(): setting the media with a non-MEDIA object, ignoring");
return;
}
CastMember *replacement = (CastMember *)d.u.obj;
Cast *cast = movie->getCast(id);
cast->duplicateCastMember(replacement, nullptr, id.member);
return;
}
if (!member->hasField(field)) {
warning("Lingo::setTheCast(): %s has no property '%s'", id.asString().c_str(), field2str(field));
return;
}
member->setField(field, d);
}
Datum Lingo::getTheCastLib(Datum &id1, int field) {
Datum d;
if (id1.type != CASTLIBREF) {
warning("Lingo::getTheCastLib(): Expected CASTLIBREF, not %s", id1.type2str());
return d;
}
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::getTheCast(): No movie loaded");
return d;
}
Cast *cast = movie->getCast(CastMemberID(0, id1.u.i));
if (!cast) {
g_lingo->lingoError("Lingo::getTheCastLib(): Cast lib %s not found", id1.asString().c_str());
return d;
}
switch (field) {
case kTheFileName:
d = cast->getArchive()->getPathName().toString(g_director->_dirSeparator);
break;
case kTheName:
d = cast->getCastName();
break;
case kTheNumber:
d = cast->_castLibID;
break;
case kThePreLoadMode:
warning("STUB: Lingo::getTheCastLib(): preLoadMode not implemented");
break;
case kTheSelectionField:
warning("STUB: Lingo::getTheCastLib(): selection not implemented");
d.type = ARRAY;
d.u.farr = new FArray();
d.u.farr->arr.push_back(1);
d.u.farr->arr.push_back(1);
break;
default:
break;
}
return d;
}
void Lingo::setTheCastLib(Datum &id1, int field, Datum &d) {
if (id1.type != CASTLIBREF) {
warning("Lingo::setTheCastLib(): Expected CASTLIBREF, not %s", id1.type2str());
return;
}
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::setTheCastLib(): No movie loaded");
return;
}
Cast *cast = movie->getCast(CastMemberID(0, id1.u.i));
if (!cast) {
g_lingo->lingoError("Lingo::setTheCastLib(): Cast lib %s not found", id1.asString().c_str());
return;
}
switch (field) {
case kTheFileName:
{
Common::Path castPath = findMoviePath(d.asString());
movie->loadCastLibFrom(id1.u.i, castPath);
}
break;
case kTheName:
movie->setCastLibName(d.asString(), id1.u.i);
break;
case kTheNumber:
warning("Lingo::setTheCastLib(): number is read-only");
break;
case kThePreLoadMode:
warning("STUB: Lingo::setTheCastLib(): preLoadMode not implemented");
break;
case kTheSelectionField:
warning("STUB: Lingo::setTheCastLib(): selection not implemented");
break;
default:
warning("STUB: Lingo::setTheCastLib(): unknown property %d", field);
break;
}
}
Datum Lingo::getTheField(Datum &id1, int field) {
Datum d;
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::getTheField(): No movie loaded");
return d;
}
CastMemberID id = id1.asMemberID();
CastMember *member = movie->getCastMember(id);
if (!member) {
if (field == kTheLoaded) {
d = 0;
} else {
g_lingo->lingoError("Lingo::getTheField(): %s not found", id.asString().c_str());
}
return d;
}
if (member->_type != kCastText) {
g_lingo->lingoError("Lingo::getTheField(): %s is not a field", id.asString().c_str());
return d;
}
if (!member->hasField(field)) {
warning("Lingo::getTheField(): %s has no property '%s'", id.asString().c_str(), field2str(field));
return d;
}
d = member->getField(field);
return d;
}
void Lingo::setTheField(Datum &id1, int field, Datum &d) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::setTheField(): No movie loaded");
return;
}
CastMemberID id = id1.asMemberID();
CastMember *member = movie->getCastMember(id);
if (!member) {
g_lingo->lingoError("Lingo::setTheField(): %s not found", id.asString().c_str());
return;
}
if (member->_type != kCastText) {
g_lingo->lingoError("Lingo::setTheField(): %s is not a field", id.asString().c_str());
return;
}
if (!member->hasField(field)) {
warning("Lingo::setTheField(): %s has no property '%s'", id.asString().c_str(), field2str(field));
return;
}
member->setField(field, d);
}
Datum Lingo::getTheChunk(Datum &chunk, int field) {
Datum d;
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::getTheChunk(): No movie loaded");
return d;
}
if (chunk.type != CHUNKREF) {
warning("BUILDBOT: Lingo::getTheChunk(): bad chunk ref type: %s", chunk.type2str());
return d;
}
int start, end;
start = chunk.u.cref->start;
end = chunk.u.cref->end;
Datum src = chunk.u.cref->source;
while (src.type == CHUNKREF) {
start += src.u.cref->start;
end += src.u.cref->start;
src = src.u.cref->source;
}
if (!src.isCastRef()) {
warning("BUILDBOT: Lingo::getTheChunk(): bad chunk ref field type: %s", src.type2str());
return d;
}
CastMemberID memberID = *src.u.cast;
CastMember *member = movie->getCastMember(memberID);
if (!member) {
g_lingo->lingoError("Lingo::getTheChunk(): %s not found", memberID.asString().c_str());
return d;
}
if (member->_type != kCastText) {
g_lingo->lingoError("Lingo::getTheChunk(): %s is not a field", memberID.asString().c_str());
return d;
}
if (!((TextCastMember *)member)->hasChunkField(field)) {
warning("Lingo::getTheChunk(): %s has no chunk property '%s'", memberID.asString().c_str(), field2str(field));
return d;
}
d = ((TextCastMember *)member)->getChunkField(field, start, end);
return d;
}
void Lingo::setTheChunk(Datum &chunk, int field, Datum &d) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
warning("Lingo::setTheChunk(): No movie loaded");
return;
}
if (chunk.type != CHUNKREF) {
warning("BUILDBOT: Lingo::setTheChunk(): bad chunk ref type: %s", chunk.type2str());
return;
}
int start, end;
start = chunk.u.cref->start;
end = chunk.u.cref->end;
Datum src = chunk.u.cref->source;
while (src.type == CHUNKREF) {
start += src.u.cref->start;
end += src.u.cref->start;
src = src.u.cref->source;
}
if (!src.isCastRef()) {
warning("BUILDBOT: Lingo::setTheChunk(): bad chunk ref field type: %s", src.type2str());
return;
}
CastMemberID memberID = *src.u.cast;
CastMember *member = movie->getCastMember(memberID);
if (!member) {
g_lingo->lingoError("Lingo::setTheChunk(): %s not found", memberID.asString().c_str());
return;
}
if (member->_type != kCastText) {
g_lingo->lingoError("Lingo::setTheChunk(): %s is not a field", memberID.asString().c_str());
return;
}
if (!((TextCastMember *)member)->hasChunkField(field)) {
warning("Lingo::setTheChunk(): %s has no chunk property '%s'", memberID.asString().c_str(), field2str(field));
return;
}
((TextCastMember *)member)->setChunkField(field, start, end, d);
}
void Lingo::getObjectProp(Datum &obj, Common::String &propName) {
Datum d;
if (obj.type == OBJECT) {
if (obj.u.obj->hasProp(propName)) {
d = obj.u.obj->getProp(propName);
} else {
g_lingo->lingoError("Lingo::getObjectProp: Object <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
g_lingo->push(d);
g_debugger->propReadHook(propName);
return;
} else if (obj.type == PARRAY) {
int index = LC::compareArrays(LC::eqData, obj, propName, true).u.i;
if (index > 0) {
d = obj.u.parr->arr[index - 1].v;
}
g_lingo->push(d);
g_debugger->propReadHook(propName);
return;
} else if (obj.type == POINT) {
if (propName.equalsIgnoreCase("locH")) {
d = obj.u.farr->arr[0];
} else if (propName.equalsIgnoreCase("locV")) {
d = obj.u.farr->arr[1];
} else {
g_lingo->lingoError("Lingo::getObjectProp: Point <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
g_lingo->push(d);
g_debugger->propReadHook(propName);
return;
} else if (obj.type == RECT) {
if (propName.equalsIgnoreCase("left")) {
d = obj.u.farr->arr[0];
} else if (propName.equalsIgnoreCase("top")) {
d = obj.u.farr->arr[1];
} else if (propName.equalsIgnoreCase("right")) {
d = obj.u.farr->arr[2];
} else if (propName.equalsIgnoreCase("bottom")) {
d = obj.u.farr->arr[3];
} else {
g_lingo->lingoError("Lingo::getObjectProp: Rect <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
g_lingo->push(d);
g_debugger->propReadHook(propName);
return;
} else if (obj.type == CASTREF) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
g_lingo->lingoError("Lingo::getObjectProp(): No movie loaded");
g_lingo->push(d);
return;
}
CastMemberID id = *obj.u.cast;
CastMember *member = movie->getCastMember(id);
if (!member) {
// No matching cast member. Many of the fields are accessible
// to indicate the cast member is empty, however the
// rest will throw a Lingo error.
Common::String key = Common::String::format("%d%s", kTheCast, propName.c_str());
bool emptyAllowed = false;
if (_theEntityFields.contains(key)) {
emptyAllowed = true;
switch (_theEntityFields[key]->field) {
case kTheCastType:
case kTheType:
d = Datum("empty");
d.type = SYMBOL;
break;
case kTheFileName:
case kTheScriptText:
d = Datum("");
break;
case kTheHeight:
case kTheLoaded:
case kTheModified:
case kThePurgePriority:
case kTheWidth:
case kTheCenter:
case kTheFrameRate:
case kThePausedAtStart:
case kThePreLoad:
case kTheDepth:
case kThePalette:
d = Datum(0);
break;
case kTheCrop:
case kTheVideo:
d = Datum(1);
break;
case kTheRect:
d = Datum(Common::Rect(0, 0, 0, 0));
break;
case kTheRegPoint:
d = Datum(Common::Point(0, 0));
break;
case kTheNumber:
if (g_director->getVersion() >= 500) {
d = Datum(id.toMultiplex());
} else {
d = Datum(id.member);
}
break;
default:
emptyAllowed = false;
break;
}
}
if (id.member <= getMembersNum(id.castLib)) {
// Cast member ID is within range (i.e. less than max)
// In real Director, accessing -any- of the properties will
// be allowed, but return garbage.
warning("Lingo::getObjectProp(): %s not found, but within cast ID range", id.asString().c_str());
} else if (!emptyAllowed) {
// Cast member ID is out of range, throw a Lingo error
g_lingo->lingoError("Lingo::getObjectProp(): %s not found and out of cast ID range", id.asString().c_str());
}
g_lingo->push(d);
return;
}
if (propName.equals("media")) {
// every time "the media" is invoked, a new copy is made.
member->load();
d = Datum(member->duplicate(nullptr, 0));
g_lingo->push(d);
return;
}
if (member->hasProp(propName)) {
d = member->getProp(propName);
} else {
g_lingo->lingoError("Lingo::getObjectProp(): %s has no property '%s'", id.asString().c_str(), propName.c_str());
}
g_lingo->push(d);
return;
} else if (obj.type == CASTLIBREF) {
Common::String key = Common::String::format("%d%s", kTheCastLib, propName.c_str());
if (_theEntityFields.contains(key)) {
d = getTheCastLib(obj, _theEntityFields[key]->field);
}
g_lingo->push(d);
return;
} else if (obj.type == SPRITEREF) {
Common::String key = Common::String::format("%d%s", kTheSprite, propName.c_str());
if (_theEntityFields.contains(key)) {
d = getTheSprite(obj, _theEntityFields[key]->field);
}
g_lingo->push(d);
return;
}
if (_builtinFuncs.contains(propName) && _builtinFuncs[propName].nargs == 1) {
push(obj);
LC::call(_builtinFuncs[propName], 1, true);
return;
}
g_lingo->lingoError("Lingo::getObjectProp: Invalid object: %s", obj.asString(true).c_str());
g_lingo->push(d);
}
void Lingo::setObjectProp(Datum &obj, Common::String &propName, Datum &val) {
if (obj.type == OBJECT) {
if (obj.u.obj->hasProp(propName)) {
obj.u.obj->setProp(propName, val);
g_debugger->propWriteHook(propName);
} else {
g_lingo->lingoError("Lingo::setObjectProp: Object <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
} else if (obj.type == PARRAY) {
int index = LC::compareArrays(LC::eqData, obj, propName, true).u.i;
if (index > 0) {
obj.u.parr->arr[index - 1].v = val;
} else {
PCell cell = PCell(propName, val);
obj.u.parr->arr.push_back(cell);
}
g_debugger->propWriteHook(propName);
} else if (obj.type == POINT) {
if (propName.equalsIgnoreCase("locH")) {
obj.u.farr->arr[0] = val.asInt();
} else if (propName.equalsIgnoreCase("locV")) {
obj.u.farr->arr[1] = val.asInt();
} else {
g_lingo->lingoError("Lingo::setObjectProp: Point <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
g_debugger->propWriteHook(propName);
return;
} else if (obj.type == RECT) {
if (propName.equalsIgnoreCase("left")) {
obj.u.farr->arr[0] = val.asInt();
} else if (propName.equalsIgnoreCase("top")) {
obj.u.farr->arr[1] = val.asInt();
} else if (propName.equalsIgnoreCase("right")) {
obj.u.farr->arr[2] = val.asInt();
} else if (propName.equalsIgnoreCase("bottom")) {
obj.u.farr->arr[3] = val.asInt();
} else {
g_lingo->lingoError("Lingo::setObjectProp: Rect <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
}
g_debugger->propWriteHook(propName);
return;
} else if (obj.type == CASTREF) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
g_lingo->lingoError("Lingo::setObjectProp(): No movie loaded");
return;
}
CastMemberID id = *obj.u.cast;
CastMember *member = movie->getCastMember(id);
if (!member) {
Common::String key = Common::String::format("%d%s", kTheCast, propName.c_str());
bool emptyAllowed = false;
if (_theEntityFields.contains(key)) {
switch (_theEntityFields[key]->field) {
case kTheFileName:
case kTheScriptText:
case kTheLoaded:
case kThePurgePriority:
case kTheCenter:
case kTheFrameRate:
case kThePausedAtStart:
case kThePreLoad:
case kThePalette:
case kTheCrop:
case kTheRegPoint:
emptyAllowed = true;
break;
default:
break;
}
}
if (emptyAllowed) {
return;
}
g_lingo->lingoError("Lingo::setObjectProp(): %s not found", id.asString().c_str());
return;
}
if (propName.equals("media")) {
if (val.type != MEDIA) {
warning("Lingo::setObjectProp(): setting the media with a non-MEDIA object, ignoring");
return;
}
CastMember *replacement = (CastMember *)val.u.obj;
Cast *cast = movie->getCast(id);
cast->duplicateCastMember(replacement, nullptr, id.member);
return;
}
if (member->hasProp(propName)) {
member->setProp(propName, val);
} else {
g_lingo->lingoError("Lingo::setObjectProp(): %s has no property '%s'", id.asString().c_str(), propName.c_str());
}
} else if (obj.type == CASTLIBREF) {
Common::String key = Common::String::format("%d%s", kTheCastLib, propName.c_str());
if (_theEntityFields.contains(key)) {
setTheCastLib(obj, _theEntityFields[key]->field, val);
}
} else if (obj.type == SPRITEREF) {
Common::String key = Common::String::format("%d%s", kTheSprite, propName.c_str());
if (_theEntityFields.contains(key)) {
setTheSprite(obj, _theEntityFields[key]->field, val);
}
} else {
g_lingo->lingoError("Lingo::setObjectProp: Invalid object: %s", obj.asString(true).c_str());
}
}
static const char *mfull[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static const char *wday[] = {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
Datum Lingo::getTheDate(int field) {
TimeDate t;
g_system->getTimeAndDate(t);
if (g_director->_forceDate.tm_year != -1) {
// Override date portion
t.tm_year = g_director->_forceDate.tm_year;
t.tm_mon = g_director->_forceDate.tm_mon;
t.tm_wday = g_director->_forceDate.tm_wday;
t.tm_mday = g_director->_forceDate.tm_mday;
}
Common::String s;
Datum d;
d.type = STRING;
const char *m = mfull[t.tm_mon];
const char *w = wday[t.tm_wday];
switch (field) {
case kTheAbbr: // "Sat, Sep 7, 1991"
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);
break;
case kTheLong: // "Saturday, September 7, 1991"
s = Common::String::format("%s, %s %d, %d", w, m, t.tm_mday, t.tm_year + 1900);
break;
default: // "9/7/91"
s = Common::String::format("%d/%d/%02d", t.tm_mday, t.tm_mon, t.tm_year % 100);
break;
}
d.u.s = new Common::String(s);
return d;
}
Datum Lingo::getTheTime(int field) {
TimeDate t;
g_system->getTimeAndDate(t);
Common::String s;
Datum d;
d.type = STRING;
switch (field) {
case kTheLong:
s = Common::String::format("%d:%02d:%02d %s", t.tm_hour % 12, t.tm_min, t.tm_sec, t.tm_hour < 12 ? "AM" : "PM");
break;
default:
s = Common::String::format("%d:%02d %s", t.tm_hour % 12, t.tm_min, t.tm_hour < 12 ? "AM" : "PM");
break;
}
d.u.s = new Common::String(s);
return d;
}
Datum Lingo::getTheDeskTopRectList() {
// Returns dimensions of each monitor
Datum monitorSize;
monitorSize.type = RECT;
monitorSize.u.farr = new FArray;
monitorSize.u.farr->arr.push_back(0);
monitorSize.u.farr->arr.push_back(0);
monitorSize.u.farr->arr.push_back(g_director->getMacWindowManager()->getWidth());
monitorSize.u.farr->arr.push_back(g_director->getMacWindowManager()->getHeight());
Datum d;
d.type = ARRAY;
d.u.farr = new FArray;
d.u.farr->arr.push_back(monitorSize);
return d;
}
} // End of namespace Director