6956 lines
230 KiB
C++
6956 lines
230 KiB
C++
/**********************************************************************
|
||
|
||
Filename : GFxSprite.cpp
|
||
Content : Implementation of MovieClip character
|
||
Created :
|
||
Authors : Michael Antonov, Artem Bolgar
|
||
|
||
Copyright : (c) 2001-2009 Scaleform Corp. All Rights Reserved.
|
||
|
||
Notes :
|
||
|
||
Licensees may use this file in accordance with the valid Scaleform
|
||
Commercial License Agreement provided with the software.
|
||
|
||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
||
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
|
||
|
||
**********************************************************************/
|
||
|
||
#include "GAlgorithm.h"
|
||
#include "GListAlloc.h"
|
||
#include "GList.h"
|
||
#include "GFxSprite.h"
|
||
#include "GFxAction.h"
|
||
#include "GFxButton.h"
|
||
#include "GFxLoaderImpl.h"
|
||
#include "AS/GASTransformObject.h"
|
||
#include "GFxFontResource.h"
|
||
#include "GFxLog.h"
|
||
#include "GFxMorphCharacter.h"
|
||
#include "GFxShape.h"
|
||
#include "AS/GASArrayObject.h"
|
||
#include "AS/GASMatrixObject.h"
|
||
#include "GFxStream.h"
|
||
#include "GFxStyles.h"
|
||
#include "GFxDlist.h"
|
||
//#include "AS/GASTimers.h"
|
||
#include "GFxPlayer.h"
|
||
#include "GFxAudio.h"
|
||
#include "GFxSound.h"
|
||
#include "GFxString.h"
|
||
|
||
#include "GFxLoadProcess.h"
|
||
#include "GFxDisplayContext.h"
|
||
|
||
#include "AS/GASBitmapData.h"
|
||
#include "GASSoundObject.h"
|
||
|
||
#include <GFxIMEManager.h>
|
||
#include "GFxVideoBase.h"
|
||
|
||
#include "AS/GASTextSnapshot.h"
|
||
#include "AMP/GFxAmpViewStats.h"
|
||
|
||
#include "GMsgFormat.h"
|
||
|
||
#include <string.h> // for memset
|
||
#include <float.h>
|
||
#include <stdlib.h>
|
||
#ifdef GFC_MATH_H
|
||
#include GFC_MATH_H
|
||
#else
|
||
#include <math.h>
|
||
#endif
|
||
|
||
// RAGE - add some diag context for actionscript calls
|
||
#include "../../../../base/src/diag/output.h"
|
||
|
||
#ifdef GFC_BUILD_DEBUG
|
||
//#define GFX_TRACE_TIMELINE_EXECUTION
|
||
//#define GFX_DUMP_DISPLAYLIST
|
||
//#define GFX_TRACE_DIPLAYLIST_EVERY_FRAME
|
||
#endif
|
||
|
||
// Version numbers for $version and getVersion()
|
||
// TBD: Perhaps these should be moved into a different header?
|
||
#if defined(GFC_OS_WIN32)
|
||
#define GFX_FLASH_VERSION "WIN 8,0,0,0"
|
||
#elif defined(GFC_OS_MAC)
|
||
#define GFX_FLASH_VERSION "MAC 8,0,0,0"
|
||
#elif defined(GFC_OS_LINUX)
|
||
#define GFX_FLASH_VERSION "LINUX 8,0,0,0"
|
||
#elif defined(GFC_OS_XBOX360)
|
||
#define GFX_FLASH_VERSION "XBOX360 8,0,0,0"
|
||
#elif defined(GFC_OS_XBOX)
|
||
#define GFX_FLASH_VERSION "XBOX 8,0,0,0"
|
||
#elif defined(GFC_OS_PSP)
|
||
#define GFX_FLASH_VERSION "PSP 8,0,0,0"
|
||
#elif defined(GFC_OS_PS2)
|
||
#define GFX_FLASH_VERSION "PS2 8,0,0,0"
|
||
#elif defined(GFC_OS_PS3)
|
||
#define GFX_FLASH_VERSION "PS3 8,0,0,0"
|
||
#elif defined(GFC_OS_WII)
|
||
#define GFX_FLASH_VERSION "WII 8,0,0,0"
|
||
#else
|
||
#define GFX_FLASH_VERSION "GFX 8,0,0,0"
|
||
#endif
|
||
|
||
|
||
// This class is designed to assemble and execute so called "timeline snapshot".
|
||
// It is used for gotoFrame backward and forward. It actually is being assembled
|
||
// from ExecuteTags - PlaceObject and RemoveObject.
|
||
class GFxTimelineSnapshot
|
||
{
|
||
public:
|
||
enum PlaceType
|
||
{
|
||
Place_Add = GFxPlaceObject::Place_Add,
|
||
Place_Move = GFxPlaceObject::Place_Move,
|
||
Place_Replace = GFxPlaceObject::Place_Replace,
|
||
Place_Remove,
|
||
Place_RemoveAndAdd,
|
||
|
||
Place_Unknown = 255
|
||
};
|
||
enum FlagsType
|
||
{
|
||
Flags_NoReplaceAllowed = 0x1, // TagType=4, no replaces allowed
|
||
Flags_DeadOnArrival = 0x2 // Character is released, but has unload handler
|
||
};
|
||
struct SourceTags
|
||
{
|
||
GFxPlaceObjectBase* pMainTag;
|
||
GFxPlaceObjectBase* pMatrixTag;
|
||
GFxPlaceObjectBase* pCxFormTag;
|
||
GFxPlaceObjectBase* pFiltersTag;
|
||
GFxPlaceObjectBase* pBlendModeTag;
|
||
GFxPlaceObjectBase* pDepthTag;
|
||
GFxPlaceObjectBase* pClipDepthTag;
|
||
GFxPlaceObjectBase* pRatioTag;
|
||
GFxPlaceObjectBase* pCharIdTag;
|
||
SourceTags()
|
||
: pMainTag(0), pMatrixTag(0), pCxFormTag(0), pFiltersTag(0),
|
||
pBlendModeTag(0), pDepthTag(0), pClipDepthTag(0),
|
||
pRatioTag(0), pCharIdTag(0) {}
|
||
GINLINE bool HasMainTag() { return (pMainTag != 0); }
|
||
GINLINE void Assign(GFxPlaceObjectBase* ptag)
|
||
{
|
||
pMainTag = pMatrixTag = pCxFormTag = pFiltersTag =
|
||
pBlendModeTag = pDepthTag = pClipDepthTag = pRatioTag =
|
||
pCharIdTag = ptag;
|
||
}
|
||
GINLINE void Union(GFxPlaceObjectBase* ptag)
|
||
{
|
||
GFxCharPosInfoFlags flags = ptag->GetFlags();
|
||
if (flags.HasMatrix()) pMatrixTag = ptag;
|
||
if (flags.HasCxform()) pCxFormTag = ptag;
|
||
if (flags.HasFilters()) pFiltersTag = ptag;
|
||
if (flags.HasBlendMode()) pBlendModeTag = ptag;
|
||
if (flags.HasDepth()) pDepthTag = ptag;
|
||
if (flags.HasClipDepth()) pClipDepthTag = ptag;
|
||
if (flags.HasRatio()) pRatioTag = ptag;
|
||
if (flags.HasCharacterId()) pCharIdTag = ptag;
|
||
}
|
||
GINLINE void Unpack(GFxPlaceObjectBase::UnpackedData& data) const
|
||
{
|
||
GASSERT(pMainTag);
|
||
pMainTag->Unpack(data);
|
||
|
||
// Override tag data for Depth, CharId, Matrix, CxForm, BlendMode, ClipDepth, Ratio, TextFilter
|
||
GFxPlaceObjectBase::UnpackedData unpackedData[8];
|
||
|
||
UPInt dataIdx;
|
||
if (pDepthTag != pMainTag)
|
||
{
|
||
pDepthTag->Unpack(unpackedData[0]);
|
||
data.Pos.Depth = unpackedData[0].Pos.Depth;
|
||
data.Pos.SetDepthFlag();
|
||
}
|
||
if (pCharIdTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pCharIdTag) dataIdx = 0;
|
||
else { pCharIdTag->Unpack(unpackedData[1]); dataIdx = 1; }
|
||
data.Pos.CharacterId = unpackedData[dataIdx].Pos.CharacterId;
|
||
data.Pos.SetCharacterIdFlag();
|
||
}
|
||
if (pMatrixTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pMatrixTag) dataIdx = 0;
|
||
else if (pCharIdTag == pMatrixTag) dataIdx = 1;
|
||
else { pMatrixTag->Unpack(unpackedData[2]); dataIdx = 2; }
|
||
data.Pos.Matrix_1 = unpackedData[dataIdx].Pos.Matrix_1;
|
||
data.Pos.SetMatrixFlag();
|
||
}
|
||
if (pCxFormTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pCxFormTag) dataIdx = 0;
|
||
else if (pCharIdTag == pCxFormTag) dataIdx = 1;
|
||
else if (pMatrixTag == pCxFormTag) dataIdx = 2;
|
||
else { pCxFormTag->Unpack(unpackedData[3]); dataIdx = 3; }
|
||
data.Pos.ColorTransform = unpackedData[dataIdx].Pos.ColorTransform;
|
||
data.Pos.SetCxFormFlag();
|
||
}
|
||
if (pBlendModeTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pBlendModeTag) dataIdx = 0;
|
||
else if (pCharIdTag == pBlendModeTag) dataIdx = 1;
|
||
else if (pMatrixTag == pBlendModeTag) dataIdx = 2;
|
||
else if (pCxFormTag == pBlendModeTag) dataIdx = 3;
|
||
else { pBlendModeTag->Unpack(unpackedData[4]); dataIdx = 4; }
|
||
data.Pos.BlendMode = unpackedData[dataIdx].Pos.BlendMode;
|
||
data.Pos.SetBlendModeFlag();
|
||
}
|
||
if (pClipDepthTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pClipDepthTag) dataIdx = 0;
|
||
else if (pCharIdTag == pClipDepthTag) dataIdx = 1;
|
||
else if (pMatrixTag == pClipDepthTag) dataIdx = 2;
|
||
else if (pCxFormTag == pClipDepthTag) dataIdx = 3;
|
||
else if (pBlendModeTag == pClipDepthTag) dataIdx = 4;
|
||
else { pClipDepthTag->Unpack(unpackedData[5]); dataIdx = 5; }
|
||
data.Pos.ClipDepth = unpackedData[dataIdx].Pos.ClipDepth;
|
||
data.Pos.SetClipDepthFlag();
|
||
}
|
||
if (pRatioTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pRatioTag) dataIdx = 0;
|
||
else if (pCharIdTag == pRatioTag) dataIdx = 1;
|
||
else if (pMatrixTag == pRatioTag) dataIdx = 2;
|
||
else if (pCxFormTag == pRatioTag) dataIdx = 3;
|
||
else if (pBlendModeTag == pRatioTag) dataIdx = 4;
|
||
else if (pClipDepthTag == pRatioTag) dataIdx = 5;
|
||
else { pRatioTag->Unpack(unpackedData[6]); dataIdx = 6; }
|
||
data.Pos.Ratio = unpackedData[dataIdx].Pos.Ratio;
|
||
data.Pos.SetRatioFlag();
|
||
}
|
||
if (pFiltersTag != pMainTag)
|
||
{
|
||
if (pDepthTag == pFiltersTag) dataIdx = 0;
|
||
else if (pCharIdTag == pFiltersTag) dataIdx = 1;
|
||
else if (pMatrixTag == pFiltersTag) dataIdx = 2;
|
||
else if (pCxFormTag == pFiltersTag) dataIdx = 3;
|
||
else if (pBlendModeTag == pFiltersTag) dataIdx = 4;
|
||
else if (pClipDepthTag == pFiltersTag) dataIdx = 5;
|
||
else if (pRatioTag == pFiltersTag) dataIdx = 6;
|
||
else { pFiltersTag->Unpack(unpackedData[7]); dataIdx = 7; }
|
||
data.Pos.Filters = unpackedData[dataIdx].Pos.Filters;
|
||
data.Pos.SetFiltersFlag();
|
||
}
|
||
}
|
||
};
|
||
struct SnapshotElement
|
||
{
|
||
SnapshotElement* pPrev;
|
||
SnapshotElement* pNext;
|
||
|
||
UInt CreateFrame;
|
||
int Depth;
|
||
|
||
// Matrices, depth and character index.
|
||
SourceTags Tags;
|
||
|
||
UInt8 PlaceType;
|
||
UInt8 Flags;
|
||
|
||
SnapshotElement()
|
||
:CreateFrame(~0u), PlaceType(Place_Unknown), Flags(0) {}
|
||
SnapshotElement(int depth)
|
||
:CreateFrame(~0u), PlaceType(Place_Unknown), Flags(0) { Depth = depth; }
|
||
~SnapshotElement() {}
|
||
};
|
||
struct SnapshotElementComparator
|
||
{
|
||
int Compare(const SnapshotElement& p1, int depth) const
|
||
{
|
||
return (int)p1.Depth - (int)depth;
|
||
}
|
||
};
|
||
// a heap for elements
|
||
GListAllocDH<SnapshotElement, 50> SnapshotHeap;
|
||
// a sorted by depth array of pointers to elements, ascending order
|
||
GArrayDH_POD<SnapshotElement*> SnapshotSortedArray;
|
||
// a doubly-linked list of elements, in order of addition
|
||
GList<SnapshotElement> SnapshotList;
|
||
// the owner sprite
|
||
GFxSprite* pOwnerSprite;
|
||
|
||
// finds the last element at the specified depth (upper-bound)
|
||
SnapshotElement* FindDepth(int depth, UPInt* pidx = NULL)
|
||
{
|
||
UPInt i = G_UpperBound(SnapshotSortedArray, depth, DepthLess);
|
||
if (i != 0)
|
||
{
|
||
--i;
|
||
if (SnapshotSortedArray[UPInt(i)]->Depth == depth)
|
||
{
|
||
if (pidx)
|
||
*pidx = i;
|
||
return SnapshotSortedArray[UPInt(i)];
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
private:
|
||
static int DepthLess(int depth, const SnapshotElement* pse)
|
||
{
|
||
return (depth < pse->Depth);
|
||
}
|
||
public:
|
||
enum DirectionType
|
||
{
|
||
Direction_Forward,
|
||
Direction_Backward
|
||
} Direction;
|
||
GFxTimelineSnapshot(DirectionType dir, GMemoryHeap* pheap, GFxSprite* powner)
|
||
: SnapshotHeap(pheap), SnapshotSortedArray(pheap), pOwnerSprite(powner), Direction(dir) {}
|
||
|
||
~GFxTimelineSnapshot()
|
||
{
|
||
G_FreeListElements(SnapshotList, SnapshotHeap);
|
||
}
|
||
|
||
// Add an element at the specified depth
|
||
SnapshotElement* Add(int depth)
|
||
{
|
||
SnapshotElement* pe = SnapshotHeap.Alloc();
|
||
if (pe)
|
||
{
|
||
SnapshotList.PushBack(pe);
|
||
pe->Depth = depth;
|
||
|
||
UPInt i = G_UpperBound(SnapshotSortedArray, depth, DepthLess);
|
||
GASSERT(i == SnapshotSortedArray.GetSize() || SnapshotSortedArray[i]->Depth != depth);
|
||
SnapshotSortedArray.InsertAt((UPInt)i, pe);
|
||
return pe;
|
||
}
|
||
return NULL;
|
||
}
|
||
// Remove the last (upper-bound) element at the specified depth
|
||
void Remove(int depth)
|
||
{
|
||
UPInt idx;
|
||
SnapshotElement* pe = FindDepth(depth, &idx);
|
||
GASSERT(pe);
|
||
if (pe)
|
||
{
|
||
SnapshotList.Remove(pe);
|
||
SnapshotSortedArray.RemoveAt(idx);
|
||
SnapshotHeap.Free(pe);
|
||
}
|
||
}
|
||
// Remove element at the specified index
|
||
void RemoveAtIndex(UPInt idx)
|
||
{
|
||
SnapshotElement* pe = SnapshotSortedArray[idx];
|
||
SnapshotList.Remove(pe);
|
||
SnapshotSortedArray.RemoveAt(idx);
|
||
SnapshotHeap.Free(pe);
|
||
}
|
||
};
|
||
|
||
//
|
||
// ***** GFxSpriteDef
|
||
//
|
||
|
||
GFxSpriteDef::GFxSpriteDef(GFxMovieDataDef* pmd)
|
||
:
|
||
pMovieDef(pmd),
|
||
FrameCount(0),
|
||
LoadingFrame(0),
|
||
pScale9Grid(0),
|
||
//pSoundStream(NULL),
|
||
Flags(0)
|
||
{
|
||
GASSERT(pMovieDef);
|
||
}
|
||
|
||
GFxSpriteDef::~GFxSpriteDef()
|
||
{
|
||
// Destroy frame data; the actual de-allocation of tag memory
|
||
// takes place in the GFxMovieDataDef's tag block allocator.
|
||
UInt i;
|
||
for(i=0; i<Playlist.GetSize(); i++)
|
||
Playlist[i].DestroyTags();
|
||
delete pScale9Grid;
|
||
//#ifndef GFC_NO_SOUND
|
||
// if (pSoundStream)
|
||
// pSoundStream->Release();
|
||
//#endif
|
||
}
|
||
|
||
// Create A (mutable) instance of our definition. The instance is created to
|
||
// live (temporarily) on some level on the parent GFxASCharacter's display list.
|
||
GFxCharacter* GFxSpriteDef::CreateCharacterInstance(GFxASCharacter* parent, GFxResourceId id,
|
||
GFxMovieDefImpl *pbindingImpl)
|
||
{
|
||
// Most of the time pbindingImpl will be ResourceMovieDef come from parents scope.
|
||
// In some cases we call CreateCharacterInstance with a different binding context
|
||
// then the parent sprite; this can happen for import-bound characters, for example.
|
||
GFxMovieRoot* proot = parent->GetMovieRoot();
|
||
GFxSprite* psi =
|
||
GHEAP_NEW(proot->GetMovieHeap()) GFxSprite(this, pbindingImpl, proot, parent, id);
|
||
return psi;
|
||
}
|
||
|
||
|
||
// Labels the frame currently being loaded with the
|
||
// given name. A copy of the name string is made and
|
||
// kept in this object.
|
||
void GFxSpriteDef::AddFrameName(const GString& name, GFxLog *plog)
|
||
{
|
||
GASSERT(LoadingFrame >= 0 && LoadingFrame < FrameCount);
|
||
|
||
UInt currentlyAssigned = 0;
|
||
if (NamedFrames.GetAlt(name, ¤tlyAssigned) == true)
|
||
{
|
||
if (plog)
|
||
plog->LogError("AddFrameName(%d, '%s') -- frame name already assigned to frame %d; overriding\n",
|
||
LoadingFrame, name.ToCStr(), currentlyAssigned);
|
||
}
|
||
// check if we have predefined special labels, such as "_up", "_down", "_over".
|
||
if (name.GetLength() > 0 && name[0] == '_')
|
||
{
|
||
if (name == "_up")
|
||
Flags |= Flags_Has_Frame_up;
|
||
else if (name == "_down")
|
||
Flags |= Flags_Has_Frame_down;
|
||
else if (name == "_over")
|
||
Flags |= Flags_Has_Frame_over;
|
||
}
|
||
NamedFrames.Set(name, LoadingFrame); // stores 0-based frame #
|
||
}
|
||
|
||
|
||
// Read the sprite info. Consists of a series of tags.
|
||
void GFxSpriteDef::Read(GFxLoadProcess* p, GFxResourceId charId)
|
||
{
|
||
GFxStream* pin = p->GetStream();
|
||
UInt32 tagEnd = pin->GetTagEndPosition();
|
||
|
||
|
||
p->EnterSpriteDef(this);
|
||
|
||
FrameCount = pin->ReadU16();
|
||
|
||
// ALEX: some SWF files have been seen that have 0-frame sprites.
|
||
// The Macromedia player behaves as if they have 1 frame.
|
||
if (FrameCount < 1)
|
||
FrameCount = 1;
|
||
Playlist.Resize(FrameCount); // need a playlist for each frame
|
||
|
||
pin->LogParse(" frames = %d\n", FrameCount);
|
||
|
||
LoadingFrame = 0;
|
||
|
||
while ((UInt32) pin->Tell() < tagEnd)
|
||
{
|
||
GFxTagInfo tagInfo;
|
||
GFxTagType tagType = pin->OpenTag(&tagInfo);
|
||
GFxLoaderImpl::LoaderFunction lf = NULL;
|
||
|
||
#ifdef GFC_DEBUG_COUNT_TAGS
|
||
p->CountTag(tagType);
|
||
#endif
|
||
|
||
#if 0 // RAGE - turn off progress reporting. Mainly because this would allocate a new GString each time it was called.
|
||
p->ReportProgress(p->GetFileURL(), tagInfo, true);
|
||
#endif
|
||
|
||
if (tagType == GFxTag_EndFrame)
|
||
{
|
||
// NOTE: In very rare cases FrameCount can LIE, containing
|
||
// more frames then was reported in sprite header (wizardy.swf).
|
||
if (LoadingFrame == (int)Playlist.GetSize())
|
||
{
|
||
Playlist.Resize(Playlist.GetSize()+1);
|
||
pin->LogError("An extra frame is found for sprite id = %d, framecnt = %d, actual frames = %d\n",
|
||
(int)charId.GetIdIndex(), (int)FrameCount, (int)LoadingFrame+1);
|
||
}
|
||
|
||
p->CommitFrameTags();
|
||
|
||
// show frame tag -- advance to the next frame.
|
||
pin->LogParse(" ShowFrame (sprite, char id = %d)\n", charId.GetIdIndex());
|
||
LoadingFrame++;
|
||
}
|
||
else if (GFxLoaderImpl::GetTagLoader(tagType, &lf))
|
||
{
|
||
// call the tag loader. The tag loader should add
|
||
// characters or tags to the GFxASCharacter data structure.
|
||
(*lf)(p, tagInfo);
|
||
}
|
||
else
|
||
{
|
||
// no tag loader for this tag type.
|
||
pin->LogParse("*** no tag loader for type %d\n", tagType);
|
||
}
|
||
|
||
pin->CloseTag();
|
||
}
|
||
|
||
// In general, we should have EndFrame
|
||
if (p->FrameTagsAvailable())
|
||
{
|
||
// NOTE: In very rare cases FrameCount can LIE, containing
|
||
// more frames then was reported in sprite header (wizardy.swf).
|
||
if (LoadingFrame == (int)Playlist.GetSize())
|
||
{
|
||
Playlist.Resize(Playlist.GetSize()+1);
|
||
pin->LogError("An extra frame is found for sprite id = %d, framecnt = %d, actual frames = %d\n",
|
||
(int)charId.GetIdIndex(), (int)FrameCount, (int)LoadingFrame+1);
|
||
}
|
||
|
||
p->CommitFrameTags();
|
||
// ??
|
||
// LoadingFrame++;
|
||
}
|
||
|
||
|
||
p->LeaveSpriteDef();
|
||
|
||
pin->LogParse(" -- sprite END, char id = %d --\n", charId.GetIdIndex());
|
||
}
|
||
|
||
// Initialize an empty clip.
|
||
void GFxSpriteDef::InitEmptyClipDef()
|
||
{
|
||
// Set FrameCount = 1; that is the default for an empty clip.
|
||
FrameCount = 1;
|
||
Playlist.Resize(FrameCount);
|
||
}
|
||
|
||
bool GFxSpriteDef::DefPointTestLocal(const GPointF &pt, bool testShape, const GFxCharacter* pinst) const
|
||
{
|
||
GUNUSED3(pt, testShape, pinst);
|
||
return false;
|
||
}
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
|
||
GFxSoundStreamDef* GFxSpriteDef::GetSoundStream() const
|
||
{
|
||
return pSoundStream;
|
||
}
|
||
void GFxSpriteDef::SetSoundStream(GFxSoundStreamDef* psoundStream)
|
||
{
|
||
// if (pSoundStream)
|
||
// pSoundStream->Release();
|
||
// if (psoundStream)
|
||
// psoundStream->AddRef();
|
||
pSoundStream = psoundStream;
|
||
}
|
||
|
||
#endif // GFC_NO_SOUND
|
||
|
||
//
|
||
// ***** GFxSprite Implementation
|
||
//
|
||
|
||
|
||
GFxSprite::GFxSprite(GFxTimelineDef* pdef, GFxMovieDefImpl* pdefImpl,
|
||
GFxMovieRoot* pr, GFxASCharacter* pparent,
|
||
GFxResourceId id, bool loadedSeparately)
|
||
:
|
||
GFxASCharacter(pdefImpl, pparent, id),
|
||
pDef(pdef),
|
||
pRoot(pr),
|
||
pScale9Grid(0),
|
||
PlayStatePriv(GFxMovie::Playing),
|
||
CurrentFrame(0),
|
||
Level(-1),
|
||
pRootNode(0),
|
||
#ifndef GFC_NO_SOUND
|
||
pActiveSounds(NULL),
|
||
#endif
|
||
pHitAreaHandle(0),
|
||
pHitAreaHolder(0),
|
||
pUserData(0),
|
||
Flags(0),
|
||
MouseStatePriv(UP)
|
||
{
|
||
GASSERT(pDef && pDefImpl);
|
||
GASSERT(pRoot != NULL);
|
||
|
||
pMaskCharacter = NULL;
|
||
#ifndef GFC_NO_SOUND
|
||
pActiveSounds = NULL;
|
||
#endif
|
||
if (pdef->GetResourceType() == GFxResource::RT_SpriteDef)
|
||
{
|
||
GFxSpriteDef* sp = static_cast<GFxSpriteDef*>(pdef);
|
||
SetScale9Grid(sp->GetScale9Grid());
|
||
|
||
Flags |= Flags_SpriteDef;
|
||
}
|
||
|
||
ASEnvironment.SetTargetOnConstruct(this);
|
||
|
||
SetUpdateFrame(true);
|
||
SetHasLoopedPriv(false);
|
||
|
||
// By default LockRoot is false.
|
||
SetLockRoot(false);
|
||
SetLoadedSeparately(loadedSeparately);
|
||
|
||
// Since loadedSeparately flag does not get set for imports (because it only
|
||
// enables _lockroot behavior not related to imports), we need to check for
|
||
// pMovieDefImpl specifically. Imports receive root node so that they can
|
||
// have their own font manager.
|
||
bool importFlag = pparent && !loadedSeparately &&
|
||
(pparent->GetResourceMovieDef() != pdefImpl);
|
||
|
||
if (loadedSeparately || !pparent || importFlag)
|
||
{
|
||
// We share the GFxMovieDefRootNode with other root sprites based on a ref
|
||
// count. Try to find the root node for the same MovieDef and import flag
|
||
// as ours, if not found create one.
|
||
|
||
// An exhaustive search should be ok here since we don't expect too
|
||
// many different MovieDefs and root sprite construction happens rarely;
|
||
// furthermore, a list is cheap to traverse in Advance where updates need
|
||
// to take place. If this becomes a bottleneck we could introduce a
|
||
// hash table in movie root.
|
||
GFxMovieDefRootNode *pnode = pRoot->RootMovieDefNodes.GetFirst();
|
||
|
||
while(!pRoot->RootMovieDefNodes.IsNull(pnode))
|
||
{
|
||
if ((pnode->pDefImpl == pDefImpl) && (pnode->ImportFlag == importFlag))
|
||
{
|
||
// Found node identical to us.
|
||
pnode->SpriteRefCount++;
|
||
pRootNode = pnode;
|
||
break;
|
||
}
|
||
pnode = pnode->pNext;
|
||
}
|
||
|
||
// If compatible root node not found, create one.
|
||
if (!pRootNode)
|
||
{
|
||
GMemoryHeap* pheap = pr->GetMovieHeap();
|
||
|
||
pRootNode = GHEAP_NEW(pheap) GFxMovieDefRootNode(pDefImpl, importFlag);
|
||
// Bytes loaded must be grabbed first due to data race.
|
||
pRootNode->BytesLoaded = pDefImpl->GetBytesLoaded();
|
||
pRootNode->LoadingFrame = importFlag ? 0 : pDefImpl->GetLoadingFrame();
|
||
|
||
// Create a local font manager.
|
||
// Fonts created for sprite will match bindings of our DefImpl.
|
||
pRootNode->pFontManager = *GHEAP_NEW(pheap) GFxFontManager(pDefImpl, pr->pFontManagerStates);
|
||
pRoot->RootMovieDefNodes.PushFront(pRootNode);
|
||
}
|
||
}
|
||
|
||
// Initialize the flags for init action executed.
|
||
UInt frameCount = pDef->GetFrameCount();
|
||
GASSERT(frameCount != 0);
|
||
|
||
InitActionsExecuted.Resize(frameCount);
|
||
memset(&InitActionsExecuted[0], 0, sizeof(InitActionsExecuted[0]) * frameCount);
|
||
|
||
// From now on, the ASMovieClipObj is created on-demand by the GetMovieClipObject method
|
||
//ASMovieClipObj = *GHEAP_NEW(pRoot->GetMovieHeap()) GASMovieClipObject(GetGC(), this);
|
||
// let the base class know what's going on
|
||
//pProto = ASMovieClipObj->Get__proto__();
|
||
|
||
// Though, we still need pProto to set correctly.
|
||
pProto = GetGC()->GetActualPrototype(&ASEnvironment, GASBuiltin_MovieClip);
|
||
|
||
// NOTE: DoInitActions are executed separately in Advance.
|
||
}
|
||
|
||
|
||
GFxSprite::~GFxSprite()
|
||
{
|
||
// DBG
|
||
//printf("~GFxSprite %p %s\n", this, GetName().ToCStr());
|
||
// free/reset mask
|
||
if (GetMask())
|
||
SetMask(NULL);
|
||
|
||
GFxSprite* pmaskOwner = GetMaskOwner();
|
||
if (pmaskOwner)
|
||
pmaskOwner->SetMask(NULL);
|
||
|
||
if (pRootNode)
|
||
{
|
||
pRootNode->SpriteRefCount--;
|
||
if (pRootNode->SpriteRefCount == 0)
|
||
{
|
||
pRoot->RootMovieDefNodes.Remove(pRootNode);
|
||
delete pRootNode;
|
||
}
|
||
}
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
if (pActiveSounds)
|
||
delete pActiveSounds;
|
||
#endif
|
||
ClearDisplayList();
|
||
delete pScale9Grid;
|
||
//Root->DropRef();
|
||
|
||
if (pUserData)
|
||
delete pUserData;
|
||
}
|
||
|
||
void GFxSprite::SetRendererString(GASString str)
|
||
{
|
||
if (!pUserData)
|
||
pUserData = GHEAP_NEW(pRoot->GetMovieHeap()) UserRendererData(ASEnvironment.GetSC());
|
||
pUserData->StringVal = str;
|
||
pUserData->UserData.PropFlags |= GRenderer::UD_HasString;
|
||
pUserData->UserData.pString = pUserData->StringVal.ToCStr();
|
||
}
|
||
|
||
void GFxSprite::SetRendererFloat(float num)
|
||
{
|
||
if (!pUserData)
|
||
pUserData = GHEAP_NEW(pRoot->GetMovieHeap()) UserRendererData(ASEnvironment.GetSC());
|
||
pUserData->FloatVal = num;
|
||
pUserData->UserData.PropFlags |= GRenderer::UD_HasFloat;
|
||
pUserData->UserData.pFloat = &pUserData->FloatVal;
|
||
}
|
||
|
||
void GFxSprite::SetRendererMatrix(float *m, UInt count)
|
||
{
|
||
if (!pUserData)
|
||
pUserData = GHEAP_NEW(pRoot->GetMovieHeap()) UserRendererData(ASEnvironment.GetSC());
|
||
pUserData->UserData.PropFlags |= GRenderer::UD_HasMatrix;
|
||
memcpy(pUserData->MatrixVal, m, count * sizeof(float));
|
||
pUserData->UserData.pMatrix = pUserData->MatrixVal;
|
||
pUserData->UserData.MatrixSize = count;
|
||
}
|
||
|
||
void GFxSprite::SetDirtyFlag()
|
||
{
|
||
pRoot->SetDirtyFlag();
|
||
}
|
||
|
||
void GFxSprite::SetRootNodeLoadingStat(UInt bytesLoaded, UInt loadingFrame)
|
||
{
|
||
if (pRootNode)
|
||
{
|
||
pRootNode->BytesLoaded = bytesLoaded;
|
||
pRootNode->LoadingFrame = pRootNode->ImportFlag ? 0 : loadingFrame;
|
||
}
|
||
}
|
||
#ifndef GFC_NO_IME_SUPPORT
|
||
void GFxSprite::SetIMECandidateListFont(GFxFontResource* pfont)
|
||
{
|
||
GASSERT(Level == GFX_CANDIDATELIST_LEVEL);
|
||
if (Level != GFX_CANDIDATELIST_LEVEL)
|
||
return;
|
||
GASSERT(pRootNode);
|
||
if (!pRootNode)
|
||
return;
|
||
if (!pRootNode->pFontManager)
|
||
return;
|
||
GFxResourceBinding* pbinding = pfont->GetBinding();
|
||
GPtr<GFxFontHandle> pfontHandle;
|
||
if (pfont->GetFont()->IsResolved())
|
||
{
|
||
GFxMovieDefImpl* pdefImpl = pbinding ? pbinding->GetOwnerDefImpl() : NULL;
|
||
pfontHandle = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxFontHandle(NULL, pfont, GFX_CANDIDATELIST_FONTNAME, 0, pdefImpl);
|
||
}
|
||
else
|
||
{
|
||
GPtr<GFxFontHandle> ptmp = *pRootNode->pFontManager->CreateFontHandle(pfont->GetName(), pfont->GetFontFlags(), false);
|
||
if (ptmp)
|
||
pfontHandle = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxFontHandle(NULL, ptmp->GetFont(), GFX_CANDIDATELIST_FONTNAME, 0, ptmp->pSourceMovieDef);
|
||
}
|
||
if (pfontHandle)
|
||
pRootNode->pFontManager->SetIMECandidateFont(pfontHandle);
|
||
}
|
||
#endif //#ifndef GFC_NO_IME_SUPPORT
|
||
|
||
void GFxSprite::ForceShutdown ()
|
||
{
|
||
RemoveFromPlaylist(pRoot);
|
||
|
||
if (ASMovieClipObj)
|
||
ASMovieClipObj->ForceShutdown();
|
||
|
||
}
|
||
|
||
void GFxSprite::ClearDisplayList()
|
||
{
|
||
DisplayList.Clear();
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
// These accessor methods have custom implementation in GFxSprite.
|
||
GFxMovieDefImpl* GFxSprite::GetResourceMovieDef() const
|
||
{
|
||
// Return the definition where binding takes place.
|
||
return pDefImpl.GetPtr();
|
||
}
|
||
|
||
GFxFontManager* GFxSprite::GetFontManager() const
|
||
{
|
||
if (pRootNode)
|
||
return pRootNode->pFontManager;
|
||
return GetParent()->GetFontManager();
|
||
}
|
||
|
||
GFxMovieRoot* GFxSprite::GetMovieRoot() const
|
||
{
|
||
return pRoot;
|
||
}
|
||
GFxASCharacter* GFxSprite::GetLevelMovie(SInt level) const
|
||
{
|
||
return pRoot->GetLevelMovie(level);
|
||
}
|
||
|
||
GFxASCharacter* GFxSprite::GetASRootMovie(bool ignoreLockRoot) const
|
||
{
|
||
// Return _root, considering _lockroot.
|
||
if (!GetParent() ||
|
||
(!ignoreLockRoot && IsLoadedSeparately() && IsLockRoot()) )
|
||
{
|
||
// If no parent, we ARE the root.
|
||
return const_cast<GFxSprite*>(this);
|
||
}
|
||
|
||
//// HACK!
|
||
if (IsUnloaded())
|
||
{
|
||
//!AB: This code seems still useful, since Invoke might use this method
|
||
// and to call it on unloaded characters. This is happening in w****d1.swf
|
||
// when char dies, for example. TODO: investigate.
|
||
//GFC_DEBUG_WARNING(1, "GetASRootMovie called on unloaded sprite");
|
||
return pRoot->GetLevelMovie(0);
|
||
}
|
||
|
||
return GetParent()->GetASRootMovie(ignoreLockRoot);
|
||
}
|
||
|
||
UInt32 GFxSprite::GetBytesLoaded() const
|
||
{
|
||
const GFxASCharacter *pchar = this;
|
||
// Find a load root sprite and grab bytes loaded from it.
|
||
while(pchar)
|
||
{
|
||
const GFxSprite* psprite = pchar->ToSprite();
|
||
if (psprite && psprite->pRootNode)
|
||
return psprite->pRootNode->BytesLoaded;
|
||
pchar = pchar->GetParent();
|
||
}
|
||
// We should never end up here.
|
||
return 0;
|
||
}
|
||
|
||
void GFxSprite::SetLevel(SInt level)
|
||
{
|
||
Level = level;
|
||
|
||
// Assign the default name to level.
|
||
// Note that for levels, Flash allows changing these names later,
|
||
// but such new names can not be used for path lookup.
|
||
char nameBuff[64] = "";
|
||
G_Format(GStringDataPtr(nameBuff, sizeof(nameBuff)), "_level{0}", level);
|
||
SetName(ASEnvironment.CreateString(nameBuff));
|
||
}
|
||
|
||
|
||
// Returns frames loaded, sensitive to background loading.
|
||
UInt GFxSprite::GetLoadingFrame() const
|
||
{
|
||
return (pRootNode && !pRootNode->ImportFlag) ?
|
||
pRootNode->LoadingFrame : GetFrameCount();
|
||
}
|
||
|
||
// Checks that load was called and does so if necessary.
|
||
// Used for root-level sprites.
|
||
void GFxSprite::ExecuteFrame0Events()
|
||
{
|
||
if (!IsOnEventLoadCalled())
|
||
{
|
||
// Must do loading events for root level sprites.
|
||
// For child sprites this is done by the dlist,
|
||
// but root movies don't get added to a dlist, so we do it here.
|
||
// Also, this queues up tags and actions for frame 0.
|
||
SetOnEventLoadCalled();
|
||
// Can't call OnLoadEvent here because implementation of onLoad handler is different
|
||
// for the root: it seems to be called after the first frame as a special case, presumably
|
||
// to allow for load actions to exits. That, however, is NOT true for nested movie clips.
|
||
ExecuteFrameTags(0);
|
||
pRoot->InsertEmptyAction(GFxMovieRoot::AP_Load)->SetAction(this, GFxEventId::Event_Load);
|
||
}
|
||
}
|
||
|
||
void GFxSprite::OnIntervalTimer(void *timer)
|
||
{
|
||
GUNUSED(timer);
|
||
}
|
||
|
||
|
||
// "transform" matrix describes the transform applied to parent and us,
|
||
// including the object's matrix itself. This means that if transform is
|
||
// identity, GetBoundsTransformed will return local bounds and NOT parent bounds.
|
||
GRectF GFxSprite::GetBounds(const Matrix &transform) const
|
||
{
|
||
GFxCharacter* ch;
|
||
UPInt i, n = DisplayList.GetCharacterCount();
|
||
GRectF r(0);
|
||
GRectF tempRect;
|
||
Matrix m;
|
||
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
ch = DisplayList.GetCharacter(i);
|
||
if (ch != NULL)
|
||
{
|
||
// Build transform to target.
|
||
m = transform;
|
||
m.Prepend(ch->GetMatrix());
|
||
// get bounds transformed by matrix
|
||
tempRect = ch->GetBounds(m);
|
||
|
||
if (!tempRect.IsEmpty ())
|
||
{
|
||
if (!r.IsEmpty ())
|
||
r.Union(tempRect);
|
||
else
|
||
r = tempRect;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pDrawingAPI)
|
||
{
|
||
pDrawingAPI->ComputeBound(&tempRect);
|
||
if (!tempRect.IsEmpty())
|
||
{
|
||
tempRect = transform.EncloseTransform(tempRect);
|
||
if (!r.IsEmpty())
|
||
r.Union(tempRect);
|
||
else
|
||
r = tempRect;
|
||
}
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
#ifndef GFC_NO_3D
|
||
// "transform" matrix describes the transform applied to parent and us,
|
||
// including the object's matrix itself. This means that if transform is
|
||
// identity, GetBoundsTransformed will return local bounds and NOT parent bounds.
|
||
GRectF GFxSprite::GetBounds(const GMatrix3D &transform, bool bDivideByW) const
|
||
{
|
||
GFxCharacter* ch;
|
||
UPInt i, n = DisplayList.GetCharacterCount();
|
||
GRectF r(0);
|
||
GRectF tempRect;
|
||
GMatrix3D m;
|
||
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
ch = DisplayList.GetCharacter(i);
|
||
if (ch != NULL)
|
||
{
|
||
// Build transform to target.
|
||
m = transform;
|
||
m.Prepend(ch->GetLocalMatrix3D());
|
||
// get bounds transformed by matrix
|
||
tempRect = ch->GetBounds(m, bDivideByW);
|
||
|
||
if (!tempRect.IsEmpty ())
|
||
{
|
||
if (!r.IsEmpty ())
|
||
r.Union(tempRect);
|
||
else
|
||
r = tempRect;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pDrawingAPI)
|
||
{
|
||
pDrawingAPI->ComputeBound(&tempRect);
|
||
if (!tempRect.IsEmpty())
|
||
{
|
||
tempRect = transform.EncloseTransform(tempRect, bDivideByW);
|
||
if (!r.IsEmpty())
|
||
r.Union(tempRect);
|
||
else
|
||
r = tempRect;
|
||
}
|
||
}
|
||
|
||
return r;
|
||
}
|
||
#endif
|
||
|
||
// "transform" matrix describes the transform applied to parent and us,
|
||
// including the object's matrix itself. This means that if transform is
|
||
// identity, GetBoundsTransformed will return local bounds and NOT parent bounds.
|
||
GRectF GFxSprite::GetRectBounds(const Matrix &transform) const
|
||
{
|
||
GFxCharacter* ch;
|
||
UPInt i, n = DisplayList.GetCharacterCount();
|
||
GRectF r(0);
|
||
GRectF tempRect;
|
||
Matrix m;
|
||
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
ch = DisplayList.GetCharacter(i);
|
||
if (ch != NULL)
|
||
{
|
||
// Build transform to target.
|
||
m = transform;
|
||
m.Prepend(ch->GetMatrix());
|
||
// get bounds transformed by matrix
|
||
tempRect = ch->GetRectBounds(m);
|
||
|
||
if (!tempRect.IsEmpty ())
|
||
{
|
||
if (!r.IsEmpty ())
|
||
r.Union(tempRect);
|
||
else
|
||
r = tempRect;
|
||
}
|
||
}
|
||
}
|
||
return r;
|
||
}
|
||
|
||
GRectF GFxSprite::GetFocusRect() const
|
||
{
|
||
// Here is the difference from Flash: GFx will use 'hitArea's rectangle
|
||
// as a focusRect, if such hitArea is installed for the sprite.
|
||
// The hitArea's rectangle will be used ONLY if the hitArea is installed
|
||
// to 'this' sprite; in the case when hitArea is installed for one of its
|
||
// child it will not be used. It is possible to find such hitAreas, however,
|
||
// it is unclear what to do if more than one child has a hitArea. That is why,
|
||
// at the moment only immediate hitArea is used.
|
||
// The standard Flash Player doesn't use hitArea for focusRect at all.
|
||
GFxSprite* phitArea = GetHitArea();
|
||
Matrix thisToHitAreaTransform;
|
||
// This code searches for the first child's hitArea, but it is commented out
|
||
// because of reason described above.
|
||
//if (!phitArea)
|
||
//{
|
||
// if (pRoot->SpritesWithHitArea.GetSize() > 0)
|
||
// {
|
||
// GFxSprite* pcurHA = NULL;
|
||
// for (UPInt i = 0, n = pRoot->SpritesWithHitArea.GetSize(); i < n; ++i)
|
||
// {
|
||
// pcurHA = pRoot->SpritesWithHitArea[i];
|
||
|
||
// GFxASCharacter* parent = pcurHA;
|
||
// // check if one of the parent of the current hitArea is 'this'.
|
||
// do {
|
||
// parent = parent->GetParent();
|
||
// } while (parent && parent != this);
|
||
// if (parent)
|
||
// {
|
||
// phitArea = pcurHA;
|
||
// break;
|
||
// }
|
||
// }
|
||
|
||
// GFxASCharacter* parent = phitArea;
|
||
// while (parent && parent != this)
|
||
// {
|
||
// thisToHitAreaTransform.Prepend(parent->GetMatrix());
|
||
// parent = parent->GetParent();
|
||
// }
|
||
// }
|
||
//}
|
||
//else
|
||
// thisToHitAreaTransform = phitArea->GetMatrix();
|
||
if (phitArea)
|
||
{
|
||
thisToHitAreaTransform = phitArea->GetMatrix();
|
||
|
||
// now we need to translate hitArea's focus rect to 'this' sprite's
|
||
// coordinate system.
|
||
GRectF haRect = phitArea->GetFocusRect();
|
||
return thisToHitAreaTransform.EncloseTransform(haRect);
|
||
}
|
||
return GFxASCharacter::GetFocusRect();
|
||
}
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_TEXTSNAPSHOT
|
||
void GFxSprite::GetTextSnapshot(GFxStaticTextSnapshotData* pdata) const
|
||
{
|
||
GFxCharacter* ch;
|
||
UPInt i, n = DisplayList.GetCharacterCount();
|
||
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
ch = DisplayList.GetCharacter(i);
|
||
if (ch != NULL)
|
||
{
|
||
// Check if character is a static textfield
|
||
if (ch->GetCharacterDef()->GetResourceType() == GFxResource::RT_TextDef)
|
||
{
|
||
GFxStaticTextCharacter* pstextChar = static_cast<GFxStaticTextCharacter*>(ch);
|
||
pdata->Add(pstextChar);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif // GFC_NO_FXPLAYER_AS_TEXTSNAPSHOT
|
||
|
||
void GFxSprite::UpdateRootFrame()
|
||
{
|
||
#ifdef GFX_AMP_SERVER
|
||
if (pRoot && pRoot->AdvanceStats)
|
||
{
|
||
pRoot->AdvanceStats->SetCurrentFrame(CurrentFrame);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
void GFxSprite::Restart()
|
||
{
|
||
DisplayList.MarkAllEntriesForRemoval(0);
|
||
CurrentFrame = 0;
|
||
UpdateRootFrame();
|
||
RollOverCnt = 0;
|
||
SetUpdateFrame();
|
||
SetHasLoopedPriv(false);
|
||
PlayStatePriv = GFxMovie::Playing;
|
||
|
||
ExecuteFrameTags(CurrentFrame);
|
||
DisplayList.UnloadMarkedObjects();
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
void GFxSprite::CalcDisplayListHitTestMaskArray(GArray<UByte> *phitTest, const GPointF &pt, bool testShape) const
|
||
{
|
||
GUNUSED(testShape);
|
||
|
||
UPInt i, n = DisplayList.GetCharacterCount();
|
||
|
||
// Mask support
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
GFxCharacter* pmaskch = DisplayList.GetCharacter(i);
|
||
|
||
if (pmaskch->GetClipDepth() > 0)
|
||
{
|
||
if (phitTest->GetSize()==0)
|
||
{
|
||
phitTest->Resize(n);
|
||
memset(&(*phitTest)[0], 1, n);
|
||
}
|
||
|
||
GRenderer::Matrix m = pmaskch->GetMatrix();
|
||
GPointF p = m.TransformByInverse(pt);
|
||
|
||
(*phitTest)[i] = pmaskch->PointTestLocal(p, HitTest_TestShape);
|
||
|
||
UPInt k = i+1;
|
||
while (k < n)
|
||
{
|
||
GFxCharacter* pch = DisplayList.GetCharacter(k);
|
||
if (pch && (pch->GetDepth() > pmaskch->GetClipDepth()))
|
||
break;
|
||
(*phitTest)[k] = (*phitTest)[i];
|
||
k++;
|
||
}
|
||
i = k-1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Return the topmost entity that the given point covers. NULL if none.
|
||
// Coords are in parent's frame.
|
||
GFxASCharacter* GFxSprite::GetTopMostMouseEntity(const GPointF &pt, const TopMostParams& params)
|
||
{
|
||
// Invisible sprites/buttons don't receive mouse events (i.e. are disabled), unless it is a hitArea.
|
||
// Masks also shouldn't receive mouse events (!AB)
|
||
if (IsHitTestDisableFlagSet() || (!GetVisible() && !GetHitAreaHolder()) || IsUsedAsMask())
|
||
return 0;
|
||
|
||
if (params.IgnoreMC == this)
|
||
return 0;
|
||
|
||
if (!IsFocusAllowed(params.pRoot, params.ControllerIdx))
|
||
return 0;
|
||
|
||
GRenderer::Matrix m = GetMatrix();
|
||
GPointF localPt;
|
||
|
||
#ifndef GFC_NO_3D
|
||
if (Is3D(true))
|
||
{
|
||
const GMatrix3D *pPersp = GetPerspective3D(true);
|
||
const GMatrix3D *pView = GetView3D(true);
|
||
if (pPersp)
|
||
GetMovieRoot()->ScreenToWorld.SetPerspective(*pPersp);
|
||
if (pView)
|
||
GetMovieRoot()->ScreenToWorld.SetView(*pView);
|
||
GetMovieRoot()->ScreenToWorld.SetWorld(GetWorldMatrix3D());
|
||
GetMovieRoot()->ScreenToWorld.GetWorldPoint(&localPt);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
localPt = m.TransformByInverse(pt);
|
||
}
|
||
|
||
|
||
SPInt i, n = (SPInt)DisplayList.GetCharacterCount();
|
||
|
||
GFxSprite* pmask = GetMask();
|
||
if (pmask)
|
||
{
|
||
if (pmask->IsUsedAsMask() && !pmask->IsUnloaded())
|
||
{
|
||
GPointF pp;
|
||
#ifndef GFC_NO_3D
|
||
if (pmask->Is3D(true))
|
||
{
|
||
const GMatrix3D *pPersp = pmask->GetPerspective3D(true);
|
||
const GMatrix3D *pView = pmask->GetView3D(true);
|
||
if (pPersp)
|
||
GetMovieRoot()->ScreenToWorld.SetPerspective(*pPersp);
|
||
if (pView)
|
||
GetMovieRoot()->ScreenToWorld.SetView(*pView);
|
||
|
||
GetMovieRoot()->ScreenToWorld.SetWorld(pmask->GetWorldMatrix3D());
|
||
GetMovieRoot()->ScreenToWorld.GetWorldPoint(&pp);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
GRenderer::Matrix matrix;
|
||
matrix.SetInverse(pmask->GetWorldMatrix());
|
||
matrix.Prepend(GetWorldMatrix());
|
||
pp = matrix.Transform(localPt);
|
||
}
|
||
if (!pmask->PointTestLocal(pp, HitTest_TestShape))
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
GArray<UByte> hitTest;
|
||
CalcDisplayListHitTestMaskArray(&hitTest, localPt, 1);
|
||
|
||
// Go backwards, to check higher objects first.
|
||
for (i = n - 1; i >= 0; i--)
|
||
{
|
||
GFxCharacter* ch = DisplayList.GetCharacter(i);
|
||
|
||
if (hitTest.GetSize() && (!hitTest[i] || ch->GetClipDepth()>0))
|
||
continue;
|
||
|
||
if (ch->IsTopmostLevelFlagSet()) // do not check children w/topmostLevel
|
||
continue;
|
||
|
||
// MA: This should consider submit masks in the display list,
|
||
// such masks can obscure/clip out buttons for the purpose of
|
||
// hit-testing as well.
|
||
|
||
if (ch != NULL)
|
||
{
|
||
GFxASCharacter* te = ch->GetTopMostMouseEntity(localPt, params);
|
||
|
||
if (te && params.TestAll) // needed for mouse wheel
|
||
return te;
|
||
|
||
// If we found anything and we are in button mode, this refers to us.
|
||
if (ActsAsButton() || (pHitAreaHolder && pHitAreaHolder->ActsAsButton()))
|
||
{
|
||
// It is either child or us; no matter - button mode takes precedence.
|
||
// Note, if both - the hitArea and the holder of hitArea have button handlers
|
||
// then the holder (parent) takes precedence; otherwise, if only the hitArea has handlers
|
||
// it should be returned.
|
||
if (te)
|
||
{
|
||
if (pHitAreaHolder && pHitAreaHolder->ActsAsButton())
|
||
return pHitAreaHolder;
|
||
else
|
||
{
|
||
// Sprites with hit area also shouldn't receive mouse events if this hit area is not the sprite's child
|
||
// We need to check here if a hit area is our child
|
||
GFxSprite* phitArea = GetHitArea();
|
||
if (phitArea)
|
||
{
|
||
GFxASCharacter* parent = phitArea;
|
||
do {
|
||
parent = parent->GetParent();
|
||
} while (parent && parent != this);
|
||
if (!parent)
|
||
// hit area is not our child so we should not receive mouse events
|
||
return 0;
|
||
// delegate the call to the hit area
|
||
return phitArea->GetTopMostMouseEntity(localPt, params);
|
||
}
|
||
else
|
||
return this;
|
||
}
|
||
}
|
||
}
|
||
else if (te && te != this)
|
||
{
|
||
// Found one.
|
||
// @@ TU: GetVisible() needs to be recursive here!
|
||
|
||
// character could be _root, in which case it would have no parent.
|
||
if (te->GetParent() && te->GetParent()->GetVisible())
|
||
//if (te->GetVisible()) // TODO move this check to the base case(s) only
|
||
{
|
||
// @@
|
||
// Vitaly suggests "return ch" here, but it breaks
|
||
// samples/test_button_functions.swf
|
||
//
|
||
// However, it fixes samples/clip_as_button.swf
|
||
//
|
||
// What gives?
|
||
//
|
||
// Answer: a button event must be passed up to parent until
|
||
// somebody handles it.
|
||
//
|
||
return te;
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (pDrawingAPI && ActsAsButton())
|
||
{
|
||
if (pDrawingAPI->DefPointTestLocal(localPt, true, this))
|
||
return this;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
GFxSprite::ActiveSounds::ActiveSounds()
|
||
{
|
||
Volume = 100;
|
||
Pan = 0;
|
||
}
|
||
GFxSprite::ActiveSounds::~ActiveSounds()
|
||
{
|
||
size_t i;
|
||
if (pStreamSound)
|
||
{
|
||
pStreamSound->Stop();
|
||
pStreamSound = NULL;
|
||
}
|
||
for(i = 0; i < ASSounds.GetSize(); ++i)
|
||
{
|
||
ASSounds[i]->ReleaseTarget();
|
||
}
|
||
}
|
||
GFxSprite::ActiveSoundItem::ActiveSoundItem()
|
||
{
|
||
pSoundObject = NULL;
|
||
pResource = NULL;
|
||
}
|
||
|
||
GFxSprite::ActiveSoundItem::~ActiveSoundItem()
|
||
{
|
||
if (pChannel)
|
||
pChannel->Stop();
|
||
if (pResource)
|
||
{
|
||
pResource->DecPlayingCount();
|
||
if (!pResource->IsPlaying())
|
||
{
|
||
pResource->GetSoundInfo()->ReleaseResource();
|
||
}
|
||
pResource->Release();
|
||
}
|
||
}
|
||
|
||
bool GFxSprite::IsSoundPlaying(GASSoundObject* psobj)
|
||
{
|
||
if (pActiveSounds)
|
||
{
|
||
for(size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
if (pActiveSounds->Sounds[i]->pSoundObject == psobj)
|
||
return pActiveSounds->Sounds[i]->pChannel && pActiveSounds->Sounds[i]->pChannel->IsPlaying();
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
// add an active sound
|
||
void GFxSprite::AddActiveSound(GSoundChannel* pchan, GASSoundObject* psobj, GFxSoundResource* pres)
|
||
{
|
||
GASSERT(pchan);
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
GPtr<ActiveSoundItem> psi;
|
||
for(size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
if (pActiveSounds->Sounds[i]->pChannel.GetPtr() == pchan)
|
||
{
|
||
psi = pActiveSounds->Sounds[i];
|
||
break;
|
||
}
|
||
}
|
||
if (!psi)
|
||
{
|
||
psi = *GNEW ActiveSoundItem;
|
||
psi->pChannel = pchan;
|
||
pActiveSounds->Sounds.PushBack(psi);
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
}
|
||
psi->pSoundObject = psobj;
|
||
psi->pResource = pres;
|
||
if (psi->pResource)
|
||
{
|
||
psi->pResource->IncPlayingCount();
|
||
psi->pResource->AddRef();
|
||
}
|
||
}
|
||
|
||
void GFxSprite::SetStreamingSound(GSoundChannel* pchan)
|
||
{
|
||
if (!pchan && !pActiveSounds)
|
||
return;
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
if (pActiveSounds->pStreamSound)
|
||
pActiveSounds->pStreamSound->Stop();
|
||
pActiveSounds->pStreamSound = pchan;
|
||
if (pActiveSounds->pStreamSound)
|
||
{
|
||
pActiveSounds->pStreamSound->SetVolume(GetRealSoundVolume());
|
||
AddActiveSound(pchan, NULL, NULL); // TBD do we need to do this??
|
||
}
|
||
}
|
||
|
||
GSoundChannel* GFxSprite::GetStreamingSound() const
|
||
{
|
||
if (!pActiveSounds)
|
||
return NULL;
|
||
return pActiveSounds->pStreamSound;
|
||
}
|
||
// Detach an AS sound object from this sprite
|
||
void GFxSprite::DetachSoundObject(GASSoundObject* psobj)
|
||
{
|
||
if (pActiveSounds && psobj)
|
||
{
|
||
size_t i;
|
||
for(i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
if (psi->pSoundObject == psobj)
|
||
psi->pSoundObject = NULL;
|
||
}
|
||
for(i = 0; i < pActiveSounds->ASSounds.GetSize(); ++i)
|
||
{
|
||
if (pActiveSounds->ASSounds[i] == psobj)
|
||
{
|
||
pActiveSounds->ASSounds.RemoveAt(i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Float GFxSprite::GetActiveSoundPosition(GASSoundObject* psobj)
|
||
{
|
||
if (pActiveSounds && psobj)
|
||
{
|
||
size_t i;
|
||
for(i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
if (psi->pSoundObject == psobj && psi->pChannel)
|
||
return psi->pChannel->GetPosition();
|
||
}
|
||
}
|
||
return 0.0f;
|
||
}
|
||
// register an AS sound object which is attached to this sprite
|
||
void GFxSprite::AttachSoundObject(GASSoundObject* psobj)
|
||
{
|
||
GASSERT(psobj);
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
pActiveSounds->ASSounds.PushBack(psobj);
|
||
}
|
||
|
||
|
||
// Return a sound volume which is saved inside this object.
|
||
// It is used just for displaying this value
|
||
SInt GFxSprite::GetSoundVolume()
|
||
{
|
||
if (pActiveSounds)
|
||
return pActiveSounds->Volume;
|
||
return 100;
|
||
}
|
||
// Return a sound volume for SubAudio track which is saved inside this object.
|
||
SInt GFxSprite::GetSubSoundVolume()
|
||
{
|
||
if (pActiveSounds)
|
||
return pActiveSounds->SubVolume;
|
||
return 100;
|
||
}
|
||
|
||
// Return a calculated sound volume.
|
||
Float GFxSprite::GetRealSoundVolume()
|
||
{
|
||
Float v = GetSoundVolume()/ 100.0f;
|
||
GFxASCharacter* parent = GetParent();
|
||
while (parent)
|
||
{
|
||
if (parent->IsSprite())
|
||
v *= parent->ToSprite()->GetSoundVolume()/100.0f;
|
||
parent = parent->GetParent();
|
||
}
|
||
return v;
|
||
}
|
||
|
||
// Return a calculated sound volume for SubAudio track.
|
||
Float GFxSprite::GetRealSubSoundVolume()
|
||
{
|
||
Float v = GetSubSoundVolume()/ 100.0f;
|
||
GFxASCharacter* parent = GetParent();
|
||
while (parent)
|
||
{
|
||
if (parent->IsSprite())
|
||
v *= parent->ToSprite()->GetSubSoundVolume()/100.0f;
|
||
parent = parent->GetParent();
|
||
}
|
||
return v;
|
||
}
|
||
|
||
// Save the new sound volume and propagate it to the child movieclip
|
||
void GFxSprite::SetSoundVolume(SInt volume, SInt subvol)
|
||
{
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
pActiveSounds->Volume = volume;
|
||
pActiveSounds->SubVolume = subvol;
|
||
UpdateActiveSoundVolume();
|
||
}
|
||
|
||
// update the volume of all sound started by this sprite
|
||
void GFxSprite::UpdateActiveSoundVolume()
|
||
{
|
||
if (!pActiveSounds) return;
|
||
Float v = GetRealSoundVolume();
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
psi->pChannel->SetVolume(v);
|
||
}
|
||
for (UInt i = 0; i< DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* pasc = ch->CharToASCharacter_Unsafe();
|
||
if (pasc->IsSprite())
|
||
(pasc->ToSprite())->UpdateActiveSoundVolume();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Return a sound pan which is saved inside this object.
|
||
// It is used just for displaying this value
|
||
SInt GFxSprite::GetSoundPan()
|
||
{
|
||
if (pActiveSounds)
|
||
return pActiveSounds->Pan;
|
||
return 0;
|
||
}
|
||
|
||
// Return a calculated sound pan.
|
||
Float GFxSprite::GetRealSoundPan()
|
||
{
|
||
Float v = GetSoundPan()/ 100.0f;
|
||
GFxASCharacter* parent = GetParent();
|
||
while (parent)
|
||
{
|
||
if (parent->IsSprite())
|
||
{
|
||
v *= parent->ToSprite()->GetSoundPan()/100.0f;
|
||
}
|
||
parent = parent->GetParent();
|
||
}
|
||
return v;
|
||
}
|
||
|
||
// Save the new sound volume and propagate it to the child movieclip
|
||
void GFxSprite::SetSoundPan(SInt pan)
|
||
{
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
pActiveSounds->Pan = pan;
|
||
UpdateActiveSoundPan();
|
||
}
|
||
|
||
// update the volume of all sound started by this sprite
|
||
void GFxSprite::UpdateActiveSoundPan()
|
||
{
|
||
if (!pActiveSounds) return;
|
||
Float v = GetRealSoundPan();
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
psi->pChannel->SetPan(v);
|
||
}
|
||
for (UInt i = 0; i< DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* pasc = ch->CharToASCharacter_Unsafe();
|
||
if (pasc->IsSprite())
|
||
pasc->ToSprite()->UpdateActiveSoundPan();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Stop all active sound in this sprite with the given name
|
||
// if the name is empty, stop all sound in this movieclip and all child clips
|
||
void GFxSprite::StopActiveSounds()
|
||
{
|
||
// if the sound name is not passed we need to stop all sound in this movie clip
|
||
// and all child clips
|
||
if (pActiveSounds)
|
||
{
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
psi->pChannel->Stop();
|
||
}
|
||
pActiveSounds->Sounds.Clear();
|
||
}
|
||
for (UInt i = 0; i< DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* pasc = ch->CharToASCharacter_Unsafe();
|
||
if (pasc->IsSprite())
|
||
(pasc->ToSprite())->StopActiveSounds();
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
void GFxSprite::StopActiveSounds(const GString& name)
|
||
{
|
||
if (pActiveSounds)
|
||
{
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); )
|
||
{
|
||
ActiveSoundItem* psi = pActiveSounds->Sounds[i];
|
||
if (psi->pResource)
|
||
{
|
||
const GFxString* resName = GetResourceMovieDef()->GetNameOfExportedResource(psi->pResource->);
|
||
if (resName && *resName == name)
|
||
{
|
||
psi->pChannel->Stop();
|
||
delete psi;
|
||
pActiveSounds->Sounds.RemoveAt(i);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
*/
|
||
void GFxSprite::StopActiveSounds(GFxSoundResource* pres)
|
||
{
|
||
if (pActiveSounds)
|
||
{
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); )
|
||
{
|
||
GPtr<ActiveSoundItem> psi = pActiveSounds->Sounds[i];
|
||
if (psi->pResource == pres)
|
||
{
|
||
psi->pChannel->Stop();
|
||
pActiveSounds->Sounds.RemoveAt(i);
|
||
}
|
||
else
|
||
{
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
for (UInt i = 0; i< DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* pasc = ch->CharToASCharacter_Unsafe();
|
||
if (pasc->IsSprite())
|
||
(pasc->ToSprite())->StopActiveSounds(pres);
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxSprite::CheckActiveSounds()
|
||
{
|
||
if (pActiveSounds)
|
||
{
|
||
// we need to make a copy of active sounds array here because
|
||
// during onSoundComplete call this movie clip can be removed
|
||
GArray<GPtr<ActiveSoundItem> > sounds;
|
||
UPInt i;
|
||
for(i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
sounds.PushBack(pActiveSounds->Sounds[i]);
|
||
|
||
GArray<GPtr<ActiveSoundItem> > completeSounds;
|
||
|
||
for(i = 0; i < sounds.GetSize();)
|
||
{
|
||
GPtr<ActiveSoundItem> psi = sounds[i];
|
||
if (!psi->pChannel->IsPlaying())
|
||
{
|
||
if (psi->pSoundObject)
|
||
psi->pSoundObject->ExecuteOnSoundComplete();
|
||
completeSounds.PushBack(sounds[i]);
|
||
sounds.RemoveAt(i);
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
}
|
||
else
|
||
++i;
|
||
}
|
||
|
||
// remove all completed sounds from active sounds array
|
||
for (i = 0; i < completeSounds.GetSize(); ++i)
|
||
{
|
||
SInt idx = FindActiveSound(completeSounds[i]);
|
||
if (idx != -1)
|
||
pActiveSounds->Sounds.RemoveAt(idx);
|
||
}
|
||
}
|
||
}
|
||
|
||
// release an active sound which is attached to this sprite. It is used to pass
|
||
// active sounds between movieclips with MovieClip.attachAudio method
|
||
GFxSprite::ActiveSoundItem* GFxSprite::ReleaseActiveSound(GSoundChannel* pchan)
|
||
{
|
||
GASSERT(pchan);
|
||
if (!pActiveSounds)
|
||
return NULL;
|
||
for(size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
GPtr<ActiveSoundItem> pas = pActiveSounds->Sounds[i];
|
||
if (pas->pChannel.GetPtr() == pchan)
|
||
{
|
||
pActiveSounds->Sounds.RemoveAt(i);
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
pas->AddRef();
|
||
return pas.GetPtr();
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
// attach an active sound released by ReleaseActiveSound mehtod.
|
||
void GFxSprite::AttachActiveSound(GFxSprite::ActiveSoundItem* psi)
|
||
{
|
||
GASSERT(psi);
|
||
if (!pActiveSounds)
|
||
pActiveSounds = GNEW ActiveSounds;
|
||
pActiveSounds->Sounds.PushBack(GPtr<ActiveSoundItem>(psi));
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
}
|
||
|
||
SInt GFxSprite::FindActiveSound(ActiveSoundItem* item)
|
||
{
|
||
if (pActiveSounds)
|
||
{
|
||
for (UPInt i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
if (pActiveSounds->Sounds[i] == item)
|
||
return (SInt)i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
#endif // GFC_NO_SOUND
|
||
|
||
void GFxSprite::SetPause(bool pause)
|
||
{
|
||
#ifndef GFC_NO_SOUND
|
||
if (pActiveSounds)
|
||
{
|
||
for (size_t i = 0; i < pActiveSounds->Sounds.GetSize(); ++i)
|
||
{
|
||
ActiveSoundItem* psi = pActiveSounds->Sounds[i];
|
||
if (psi->pChannel)
|
||
{
|
||
psi->pChannel->Pause(pause);
|
||
}
|
||
}
|
||
}
|
||
for (UInt i = 0; i< DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* pasc = ch->CharToASCharacter_Unsafe();
|
||
pasc->SetPause(pause);
|
||
}
|
||
}
|
||
#else
|
||
GUNUSED(pause);
|
||
#endif
|
||
}
|
||
|
||
|
||
// Increment CurrentFrame, and take care of looping.
|
||
void GFxSprite::IncrementFrameAndCheckForLoop()
|
||
{
|
||
CurrentFrame++;
|
||
|
||
UInt loadingFrame = GetLoadingFrame();
|
||
UInt frameCount = pDef->GetFrameCount();
|
||
|
||
if ((loadingFrame < frameCount) && (CurrentFrame >= loadingFrame))
|
||
{
|
||
// We can not advance past the loading frame, since those tags are not yet here.
|
||
CurrentFrame = (loadingFrame > 0) ? loadingFrame - 1 : 0;
|
||
}
|
||
else if (CurrentFrame >= frameCount)
|
||
{
|
||
// Loop.
|
||
CurrentFrame = 0;
|
||
SetHasLoopedPriv(true);
|
||
if (frameCount > 1)
|
||
{
|
||
DisplayList.MarkAllEntriesForRemoval();
|
||
SetDirtyFlag();
|
||
}
|
||
else
|
||
{
|
||
// seems like we can stop advancing 1 frame sprite here
|
||
SetPlayState(GFxMovie::Stopped);
|
||
}
|
||
}
|
||
UpdateRootFrame();
|
||
}
|
||
|
||
#ifdef GFC_USE_OLD_ADVANCE
|
||
void GFxSprite::AdvanceFrame(bool nextFrame, Float framePos)
|
||
{
|
||
if (IsAdvanceDisabled() || IsUnloading())
|
||
return;
|
||
|
||
// Keep this (particularly GASEnvironment) alive during execution!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
pRoot->MCAdvanceCnt.AddCount(1);
|
||
|
||
GASSERT(pDef && pRoot != NULL);
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
if (nextFrame && PlayStatePriv != GFxMovie::Stopped)
|
||
{
|
||
CheckActiveSounds();
|
||
GFxSoundStreamDef* psoundDef = pDef->GetSoundStream();
|
||
if (psoundDef)
|
||
{
|
||
if (!psoundDef->ProcessSwfFrame(pRoot, CurrentFrame, this))
|
||
pDef->SetSoundStream(NULL);
|
||
}
|
||
}
|
||
#endif // GFC_NO_SOUND
|
||
|
||
// So, here is the problem. EnterFrame should be processed in order reverse to order of tags processing.
|
||
// That is why we need to dance with saving/restoring action queue positions....
|
||
|
||
// Save insert location for child actions. Child actions have to be
|
||
// queued before frame actions; however, frame tags affect their presence.
|
||
GFxMovieRoot::ActionEntry *pchildInitClipPos = pRoot->ActionQueue.GetInsertEntry(GFxMovieRoot::AP_InitClip);
|
||
GFxMovieRoot::ActionEntry *pchildActionPos = pRoot->ActionQueue.GetInsertEntry(GFxMovieRoot::AP_Frame);
|
||
|
||
// Check for the end of frame
|
||
if (IsJustLoaded())
|
||
{
|
||
SetJustLoaded(false);
|
||
}
|
||
else
|
||
{
|
||
if (nextFrame)
|
||
{
|
||
// remove all unloaded
|
||
|
||
// Update current and next frames.
|
||
if (PlayStatePriv == GFxMovie::Playing)
|
||
{
|
||
UInt CurrentFrame0 = CurrentFrame;
|
||
IncrementFrameAndCheckForLoop();
|
||
|
||
// Execute the current frame's tags.
|
||
if (CurrentFrame != CurrentFrame0)
|
||
{
|
||
|
||
#if 0
|
||
// Shape dump logic for external debugging.
|
||
FILE* fd = fopen("shapes", "at");
|
||
fprintf(fd, "Frame %d\n", CurrentFrame);
|
||
fclose(fd);
|
||
#endif
|
||
|
||
// Init action tags must come before Event_EnterFrame
|
||
ExecuteInitActionFrameTags(CurrentFrame);
|
||
|
||
// Post OnEnterFrame event, so that it arrives before other
|
||
// frame actions generated by frame tags.
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
|
||
ExecuteFrameTags(CurrentFrame);
|
||
}
|
||
else
|
||
{
|
||
// Post OnEnterFrame event, so that it arrives before other
|
||
// frame actions generated by frame tags.
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
}
|
||
|
||
// Clean up display List (remove dead objects). Such objects
|
||
// can only exist if we looped around to frame 0.
|
||
if (CurrentFrame == 0)
|
||
DisplayList.UnloadMarkedObjects();
|
||
|
||
DisplayList.CheckConsistency();
|
||
}
|
||
}
|
||
GFxMovieRoot::ActionEntry *psaveInitClipPos = pRoot->ActionQueue.SetInsertEntry(GFxMovieRoot::AP_InitClip, pchildInitClipPos);
|
||
GFxMovieRoot::ActionEntry *psaveActionPos = pRoot->ActionQueue.SetInsertEntry(GFxMovieRoot::AP_Frame, pchildActionPos);
|
||
|
||
// Advance everything in the display list.
|
||
// This call will automatically skip advancing children that were just loaded.
|
||
DisplayList.AdvanceFrame(nextFrame, framePos);
|
||
|
||
// Restore to tail location, but only if there were some actions generated by
|
||
// the current frame logic at tail after children.
|
||
// If no such actions existed, the new insert location is correct.
|
||
if (psaveInitClipPos != pchildInitClipPos)
|
||
pRoot->ActionQueue.SetInsertEntry(GFxMovieRoot::AP_InitClip, psaveInitClipPos);
|
||
if (psaveActionPos != pchildActionPos)
|
||
pRoot->ActionQueue.SetInsertEntry(GFxMovieRoot::AP_Frame, psaveActionPos);
|
||
|
||
#ifdef GFX_TRACE_DIPLAYLIST_EVERY_FRAME
|
||
if (IsLevelsMovie())
|
||
DumpDisplayList(0, "");
|
||
#endif
|
||
}
|
||
#else
|
||
|
||
void GFxSprite::AdvanceFrame(bool nextFrame, Float framePos)
|
||
{
|
||
GUNUSED(framePos);
|
||
if (IsAdvanceDisabled() || IsUnloading())
|
||
return;
|
||
|
||
// Keep this (particularly GASEnvironment) alive during execution!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
GASSERT(pDef && pRoot != NULL);
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
if (nextFrame)// && PlayStatePriv != GFxMovie::Stopped)
|
||
{
|
||
CheckActiveSounds();
|
||
if (PlayStatePriv != GFxMovie::Stopped)
|
||
{
|
||
GFxSoundStreamDef* psoundDef = pDef->GetSoundStream();
|
||
if (psoundDef)
|
||
{
|
||
if (!psoundDef->ProcessSwfFrame(pRoot, CurrentFrame, this))
|
||
pDef->SetSoundStream(NULL);
|
||
}
|
||
}
|
||
}
|
||
#endif // GFC_NO_SOUND
|
||
|
||
// Adjust x,y of this character if it is being dragged.
|
||
if (pRoot->IsMouseSupportEnabled())
|
||
GFxASCharacter::DoMouseDrag();
|
||
|
||
if (nextFrame)
|
||
{
|
||
// remove all unloaded
|
||
|
||
// Update current and next frames.
|
||
if (PlayStatePriv == GFxMovie::Playing)
|
||
{
|
||
UInt CurrentFrame0 = CurrentFrame;
|
||
IncrementFrameAndCheckForLoop();
|
||
|
||
// Execute the current frame's tags.
|
||
if (CurrentFrame != CurrentFrame0)
|
||
{
|
||
|
||
#if 0
|
||
// Shape dump logic for external debugging.
|
||
FILE* fd = fopen("shapes", "at");
|
||
fprintf(fd, "Frame %d\n", CurrentFrame);
|
||
fclose(fd);
|
||
#endif
|
||
|
||
// Init action tags must come before Event_EnterFrame
|
||
ExecuteInitActionFrameTags(CurrentFrame);
|
||
|
||
// Post OnEnterFrame event, so that it arrives before other
|
||
// frame actions generated by frame tags.
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
|
||
ExecuteFrameTags(CurrentFrame);
|
||
}
|
||
else
|
||
{
|
||
// Post OnEnterFrame event, so that it arrives before other
|
||
// frame actions generated by frame tags.
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
OnEvent(GFxEventId::Event_EnterFrame);
|
||
}
|
||
|
||
// Clean up display List (remove dead objects). Such objects
|
||
// can only exist if we looped around to frame 0.
|
||
if (CurrentFrame == 0)
|
||
DisplayList.UnloadMarkedObjects();
|
||
|
||
DisplayList.CheckConsistency();
|
||
}
|
||
|
||
#ifdef GFX_TRACE_DIPLAYLIST_EVERY_FRAME
|
||
if (IsLevelsMovie())
|
||
DumpDisplayList(0, "");
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
// Adds the tag info to a timeline snapshot.
|
||
void GFxPlaceObjectUnpacked::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt frame)
|
||
{
|
||
Trace("\n");
|
||
GFxTimelineSnapshot::SnapshotElement* pse = psnapshot->FindDepth(Pos.Depth);
|
||
if (pse && pse->Flags & GFxTimelineSnapshot::Flags_DeadOnArrival)
|
||
pse = NULL; // force to add a new SE, if the prev is marked as DOA
|
||
if (!pse)
|
||
{
|
||
pse = psnapshot->Add(Pos.Depth);
|
||
pse->Tags.Assign(this);
|
||
pse->PlaceType = (UInt8)GFxPlaceObject::Place_Add;
|
||
pse->CreateFrame = frame;
|
||
}
|
||
else
|
||
{
|
||
// found something at the depth
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
pse->Flags |= GFxTimelineSnapshot::Flags_NoReplaceAllowed;
|
||
}
|
||
|
||
void GFxPlaceObjectUnpacked::Trace(const char* str)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf ("%s", str);
|
||
printf ("PlaceObject(U): \tAdd\n");
|
||
printf("\tPos.Depth: \t\t%d\n", Pos.Depth);
|
||
printf("\tPos.CharacterId: \t%d\n", Pos.CharacterId);
|
||
#else
|
||
GUNUSED(str);
|
||
#endif
|
||
}
|
||
|
||
|
||
void GFxPlaceObject::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt frame)
|
||
{
|
||
Trace("\n");
|
||
UInt16 depth = GetDepth();
|
||
GFC_DEBUG_ASSERT(depth != 7, "blag");
|
||
GFxTimelineSnapshot::SnapshotElement* pse = psnapshot->FindDepth(depth);
|
||
if (pse && pse->Flags & GFxTimelineSnapshot::Flags_DeadOnArrival)
|
||
pse = NULL; // force to add a new SE, if the prev is marked as DOA
|
||
if (!pse)
|
||
{
|
||
pse = psnapshot->Add(depth);
|
||
pse->PlaceType = (UInt8)GFxPlaceObject::Place_Add;
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
else
|
||
{
|
||
// found something at the depth
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
pse->Flags |= GFxTimelineSnapshot::Flags_NoReplaceAllowed;
|
||
}
|
||
|
||
void GFxPlaceObject::Trace(const char* str)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
UnpackedData data;
|
||
Unpack(data);
|
||
printf ("%s", str);
|
||
printf ("PlaceObject: \tAdd\n");
|
||
printf("\tPos.Depth: \t\t%d\n", data.Pos.Depth);
|
||
printf("\tPos.CharacterId: \t%d\n", data.Pos.CharacterId);
|
||
#else
|
||
GUNUSED(str);
|
||
#endif
|
||
}
|
||
|
||
void GFxPlaceObject2::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt frame)
|
||
{
|
||
Trace("\n");
|
||
UInt16 depth = GetDepth();
|
||
UInt8 placeType = (UInt8)GetPlaceType();
|
||
GFxTimelineSnapshot::SnapshotElement* pse = psnapshot->FindDepth(depth);
|
||
if (pse && pse->Flags & GFxTimelineSnapshot::Flags_DeadOnArrival)
|
||
pse = NULL; // force to add a new SE, if the prev is marked as DOA
|
||
|
||
if (!pse)
|
||
{
|
||
pse = psnapshot->Add(depth);
|
||
pse->PlaceType = placeType;
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
else
|
||
{
|
||
// found something at the depth
|
||
switch(placeType)
|
||
{
|
||
case GFxPlaceObject::Place_Move:
|
||
pse->Tags.Union(this);
|
||
break;
|
||
case GFxPlaceObject::Place_Replace:
|
||
if (pse->PlaceType != GFxTimelineSnapshot::Place_Add)
|
||
pse->PlaceType = GFxPlaceObject::Place_Replace;
|
||
pse->Tags.Union(this);
|
||
pse->CreateFrame = frame;
|
||
break;
|
||
default:
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxPlaceObject2::TraceBase(const char* str, UInt8 version)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
GFxPlaceObject2::UnpackedData data;
|
||
UnpackBase(data, version);
|
||
printf ("%s", str);
|
||
printf ("PlaceObject2: \t%s\n", (data.PlaceType ==Place_Add)?"Add":((data.PlaceType ==Place_Move)?"Move":"Replace"));
|
||
printf ("\tName: \t%s\n", data.Name);
|
||
printf("\tPos.Depth: \t\t%d\n", data.Pos.Depth);
|
||
printf("\tPos.CharacterId: \t%d\n", data.Pos.CharacterId);
|
||
printf("\tPos.ClipDepth: \t\t%d\n", data.Pos.ClipDepth);
|
||
if (data.Pos.HasCxform())
|
||
{
|
||
char buff[2560];
|
||
data.Pos.ColorTransform.Format(buff);
|
||
printf ("\tPos.Cxform:\n");
|
||
printf("%s", buff);
|
||
|
||
}
|
||
if (data.Pos.HasMatrix())
|
||
{
|
||
char buff[2560];
|
||
data.Pos.Matrix_1.Format(buff);
|
||
printf ("\tPos.Matrix:\n");
|
||
printf("%s", buff);
|
||
}
|
||
if (data.Pos.HasBlendMode())
|
||
printf("\tPos.BlendMode: \t%d\n", data.Pos.BlendMode);
|
||
if (data.Pos.TextFilter)
|
||
printf("\tPos.HasFilters\n");
|
||
#else
|
||
GUNUSED2(str, version);
|
||
#endif
|
||
}
|
||
|
||
void GFxPlaceObject3::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt frame)
|
||
{
|
||
Trace("\n");
|
||
UInt16 depth = GetDepth();
|
||
UInt8 placeType = (UInt8)GetPlaceType();
|
||
GFxTimelineSnapshot::SnapshotElement* pse = psnapshot->FindDepth(depth);
|
||
if (pse && pse->Flags & GFxTimelineSnapshot::Flags_DeadOnArrival)
|
||
pse = NULL; // force to add a new SE, if the prev is marked as DOA
|
||
if (!pse)
|
||
{
|
||
pse = psnapshot->Add(depth);
|
||
pse->PlaceType = placeType;
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
else
|
||
{
|
||
// found something at the depth
|
||
switch(placeType)
|
||
{
|
||
case GFxPlaceObject::Place_Move:
|
||
pse->Tags.Union(this);
|
||
break;
|
||
case GFxPlaceObject::Place_Replace:
|
||
if (pse->PlaceType != GFxTimelineSnapshot::Place_Add)
|
||
pse->PlaceType = GFxPlaceObject2::Place_Replace;
|
||
pse->Tags.Union(this);
|
||
pse->CreateFrame = frame;
|
||
break;
|
||
default:
|
||
pse->Tags.Assign(this);
|
||
pse->CreateFrame = frame;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxPlaceObject3::Trace(const char* str)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
GFxPlaceObject2::UnpackedData data;
|
||
Unpack(data);
|
||
printf ("%s", str);
|
||
printf ("PlaceObject3: \t%s\n", (data.PlaceType ==GFxPlaceObject2::Place_Add)?"Add":((data.PlaceType ==GFxPlaceObject2::Place_Move)?"Move":"Replace"));
|
||
printf ("\tName: \t%s\n", data.Name);
|
||
printf("\tPos.Depth: \t\t%d\n", data.Pos.Depth);
|
||
printf("\tPos.CharacterId: \t%d\n", data.Pos.CharacterId);
|
||
printf("\tPos.ClipDepth: \t\t%d\n", data.Pos.ClipDepth);
|
||
if (data.Pos.HasCxform())
|
||
{
|
||
char buff[2560];
|
||
data.Pos.ColorTransform.Format(buff);
|
||
printf ("\tPos.Cxform:\n");
|
||
printf("%s", buff);
|
||
|
||
}
|
||
if (data.Pos.HasMatrix())
|
||
{
|
||
char buff[2560];
|
||
data.Pos.Matrix_1.Format(buff);
|
||
printf ("\tPos.Matrix:\n");
|
||
printf("%s", buff);
|
||
}
|
||
if (data.Pos.HasBlendMode())
|
||
printf("\tPos.BlendMode: \t%d\n", data.Pos.BlendMode);
|
||
if (data.Pos.TextFilter)
|
||
printf("\tPos.HasFilters\n");
|
||
#else
|
||
GUNUSED(str);
|
||
#endif
|
||
}
|
||
|
||
|
||
static void GFxRemoveObjectBase_AddToTimelineSnapshot(GASExecuteTag* ptag, UInt16 depth, GFxTimelineSnapshot* psnapshot)
|
||
{
|
||
ptag->Trace("\n");
|
||
UPInt index;
|
||
GFxTimelineSnapshot::SnapshotElement* pse = psnapshot->FindDepth(depth, &index);
|
||
if (pse && pse->PlaceType != GFxTimelineSnapshot::Place_Add)
|
||
{
|
||
psnapshot->RemoveAtIndex(index);
|
||
pse = NULL;
|
||
}
|
||
if (pse)
|
||
{
|
||
if (pse->Tags.HasMainTag() && psnapshot->Direction == GFxTimelineSnapshot::Direction_Forward)
|
||
{
|
||
// forward direction: check if the event handler array contains onUnload event.
|
||
// If it does, do not remove the element, just set flag Flags_DeadOnArrive.
|
||
// ExecuteSnapshot will create the object, execute onInitialize, onConstruct,
|
||
// onLoad and onUnload events and then kill the object.
|
||
|
||
GFxPlaceObjectBase::EventArrayType* pEventHandlers =
|
||
pse->Tags.pMainTag->UnpackEventHandlers();
|
||
|
||
if (pEventHandlers)
|
||
{
|
||
UPInt sz = pEventHandlers->GetSize();
|
||
for (UPInt i = 0; i < sz; ++i)
|
||
{
|
||
const GFxSwfEvent* pevt = pEventHandlers->At(i);
|
||
if (pevt->Event.Id & GFxEventId::Event_Unload)
|
||
{
|
||
// found!
|
||
pse->Flags |= GFxTimelineSnapshot::Flags_DeadOnArrival;
|
||
pse = NULL; // prevent from deletion
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (pse)
|
||
psnapshot->RemoveAtIndex(index);
|
||
}
|
||
if (!pse)
|
||
{
|
||
if (psnapshot->Direction == GFxTimelineSnapshot::Direction_Forward)
|
||
{
|
||
pse = psnapshot->Add(depth);
|
||
pse->Depth = depth;
|
||
pse->PlaceType = GFxTimelineSnapshot::Place_Remove;
|
||
// need to keep this SE, don't delete it from Timeline Snapshot
|
||
pse->Flags |= GFxTimelineSnapshot::Flags_DeadOnArrival;
|
||
}
|
||
else
|
||
{
|
||
// if we are moving backward, then RemoveObject tag should not exist
|
||
// w/o corresponding Add tag. So, at the moment I consider this situation
|
||
// as an error. (!AB)
|
||
GASSERT(0);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Adds the tag info to a timeline snapshot.
|
||
void GFxRemoveObject::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt)
|
||
{
|
||
GFxRemoveObjectBase_AddToTimelineSnapshot(this, Depth, psnapshot);
|
||
}
|
||
|
||
void GFxRemoveObject2::AddToTimelineSnapshot(GFxTimelineSnapshot* psnapshot, UInt)
|
||
{
|
||
GFxRemoveObjectBase_AddToTimelineSnapshot(this, Depth, psnapshot);
|
||
}
|
||
|
||
void GFxRemoveObject::Trace(const char* str)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf ("%s", str);
|
||
printf ("RemoveObject:\n");
|
||
printf("\tDepth: \t%d\n", Depth);
|
||
printf("\tCharacterId: %d\n", Id);
|
||
#else
|
||
GUNUSED(str);
|
||
#endif
|
||
}
|
||
|
||
void GFxRemoveObject2::Trace(const char* str)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf ("%s", str);
|
||
printf ("RemoveObject2:\n");
|
||
printf("\tDepth: \t%d\n", Depth);
|
||
#else
|
||
GUNUSED(str);
|
||
#endif
|
||
}
|
||
|
||
|
||
void GFxSprite::MakeSnapshot(GFxTimelineSnapshot* psnapshot, UInt startFrame, UInt endFrame)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("+++ id = %d,%s fr = %d to %d, currentframe %d ++++++++\n",
|
||
GetId(), (GetName().ToCStr() ? GetName().ToCStr() : ""),
|
||
startFrame, endFrame, CurrentFrame);
|
||
#endif
|
||
|
||
// Keep this (particularly GASEnvironment) alive during execution!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
for (UInt f = startFrame; f <= endFrame; ++f)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("\n\t--- frame: %d ---\n", f);
|
||
#endif
|
||
const Frame playlist = pDef->GetPlaylist(f);
|
||
for (UInt i = 0; i < playlist.GetTagCount(); i++)
|
||
{
|
||
GASExecuteTag* e = playlist.GetTag(i);
|
||
e->AddToTimelineSnapshot(psnapshot, f);
|
||
}
|
||
}
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("-------------\n");
|
||
#endif
|
||
}
|
||
|
||
void GFxSprite::ExecuteSnapshot(GFxTimelineSnapshot* psnapshot, GFxActionPriority::Priority prio)
|
||
{
|
||
GUNUSED(prio);
|
||
if (!psnapshot->SnapshotList.IsEmpty())
|
||
{
|
||
const GFxTimelineSnapshot::SnapshotElement* pe;
|
||
for(pe = psnapshot->SnapshotList.GetFirst(); ;
|
||
pe = psnapshot->SnapshotList.GetNext(pe))
|
||
{
|
||
const GFxTimelineSnapshot::SnapshotElement& e = *pe;
|
||
|
||
// [PPS] Place_Add/Move/Replace are always PlaceObject types?
|
||
|
||
switch(e.PlaceType)
|
||
{
|
||
case GFxTimelineSnapshot::Place_Add:
|
||
{
|
||
GFxPlaceObjectBase::UnpackedData data;
|
||
e.Tags.Unpack(data);
|
||
|
||
GASEnvironment *penv = GetASEnvironment();
|
||
GASString sname((data.Name == NULL) ? penv->GetBuiltin(GASBuiltin_empty_) : penv->CreateString(data.Name));
|
||
UInt32 flags = GFxDisplayList::Flags_PlaceObject;
|
||
if (e.Flags & GFxTimelineSnapshot::Flags_DeadOnArrival)
|
||
flags |= GFxDisplayList::Flags_DeadOnArrival;
|
||
AddDisplayObject(
|
||
data.Pos, sname, data.pEventHandlers, 0,
|
||
e.CreateFrame, flags);
|
||
}
|
||
break;
|
||
case GFxTimelineSnapshot::Place_Move:
|
||
{
|
||
GFxPlaceObjectBase::UnpackedData data;
|
||
e.Tags.Unpack(data);
|
||
|
||
MoveDisplayObject(data.Pos);
|
||
}
|
||
break;
|
||
case GFxTimelineSnapshot::Place_Replace:
|
||
{
|
||
GFxPlaceObjectBase::UnpackedData data;
|
||
e.Tags.Unpack(data);
|
||
|
||
GASEnvironment *penv = GetASEnvironment();
|
||
GASString sname((data.Name == NULL) ? penv->GetBuiltin(GASBuiltin_empty_) : penv->CreateString(data.Name));
|
||
ReplaceDisplayObject(data.Pos, sname);
|
||
}
|
||
break;
|
||
case GFxTimelineSnapshot::Place_Remove:
|
||
{
|
||
//RemoveDisplayObject(e.Depth, pos.CharacterId);
|
||
RemoveDisplayObject(e.Depth, GFxResourceId()); // [PPS] GFxRemoveObject/2 does not store char id
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (psnapshot->SnapshotList.IsLast(pe))
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Execute the tags associated with the specified frame.
|
||
// frame is 0-based
|
||
void GFxSprite::ExecuteFrameTags(UInt frame)
|
||
{
|
||
// Keep this (particularly GASEnvironment) alive during execution!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
GASSERT(frame != UInt(~0));
|
||
//GASSERT(frame < GetLoadingFrame());
|
||
if (frame >= GetLoadingFrame())
|
||
return;
|
||
|
||
// RAGE - context messages
|
||
#if __BANK
|
||
char frameStr[12];
|
||
sprintf(frameStr, "%d", frame);
|
||
DIAG_CONTEXT_MESSAGE("Executing %s frame %s", GetCharacterHandle()->GetNamePath().ToCStr(), frameStr);
|
||
#endif
|
||
|
||
// Execute this frame's init actions, if necessary.
|
||
ExecuteInitActionFrameTags(frame);
|
||
|
||
const Frame playlist = pDef->GetPlaylist(frame);
|
||
for (UInt i = 0; i < playlist.GetTagCount(); i++)
|
||
{
|
||
GASExecuteTag* e = playlist.GetTag(i);
|
||
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("\nexecution, fr = %d, %s\n", frame, GetCharacterHandle()->GetNamePath().ToCStr());
|
||
e->Trace("");
|
||
#endif
|
||
e->ExecuteWithPriority(this, GFxMovieRoot::AP_Frame);
|
||
}
|
||
}
|
||
|
||
// Executes init action tags only for the frame.
|
||
void GFxSprite::ExecuteInitActionFrameTags(UInt frame)
|
||
{
|
||
// Execute this frame's init actions, if necessary.
|
||
if (InitActionsExecuted[frame] == false)
|
||
{
|
||
// Keep this (particularly GASEnvironment) alive during execution!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
bool markFrameAsVisited = false;
|
||
|
||
/*
|
||
// execute init clip actions for each first time used import movies.
|
||
const GArray<GFxMovieDataDef*>* pimportMoviesSet = pDef->GetImportSourceMoviesForFrame(frame);
|
||
if (pimportMoviesSet && pimportMoviesSet->GetSize() > 0)
|
||
{
|
||
// iterate through all import source movies for the current frame
|
||
for(UInt f = 0, nf = pimportMoviesSet->GetSize(); f < nf; ++f)
|
||
{
|
||
GFxMovieDefSub* psrcImportMovie = (*pimportMoviesSet)[f];
|
||
GASSERT(psrcImportMovie);
|
||
if (psrcImportMovie->HasInitActions())
|
||
{
|
||
ExecuteImportedInitActions(psrcImportMovie);
|
||
}
|
||
}
|
||
markFrameAsVisited = true;
|
||
}
|
||
*/
|
||
|
||
GFxTimelineDef::Frame initActionsFrame;
|
||
|
||
if (pDef->GetInitActions(&initActionsFrame, frame) &&
|
||
initActionsFrame.GetTagCount() > 0)
|
||
{
|
||
// Need to execute these actions.
|
||
for (UInt i= 0; i < initActionsFrame.GetTagCount(); i++)
|
||
{
|
||
GASExecuteTag* e = initActionsFrame.GetTag(i);
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("\ninit action execution, fr = %d, %s\n", frame, GetCharacterHandle()->GetNamePath().ToCStr());
|
||
e->Trace("");
|
||
#endif
|
||
e->Execute(this);
|
||
}
|
||
markFrameAsVisited = true;
|
||
}
|
||
if (markFrameAsVisited)
|
||
{
|
||
// Mark this frame done, so we never execute these init actions again.
|
||
InitActionsExecuted[frame] = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxSprite::ExecuteImportedInitActions(GFxMovieDef* psourceMovie)
|
||
{
|
||
GASSERT(psourceMovie);
|
||
|
||
GFxMovieDefImpl* pdefImpl = static_cast<GFxMovieDefImpl*>(psourceMovie);
|
||
GFxMovieDataDef* pdataDef = pdefImpl->GetDataDef();
|
||
|
||
for(UPInt f = 0, fc = pdataDef->GetInitActionListSize(); f < fc; ++f)
|
||
{
|
||
Frame actionsList;
|
||
if (pdataDef->GetInitActions(&actionsList, (int)f))
|
||
{
|
||
for(UInt i = 0; i < actionsList.GetTagCount(); ++i)
|
||
{
|
||
GASExecuteTag* e = actionsList.GetTag(i);
|
||
// InitImportActions must get the right binding context since
|
||
// their binding scope does not match that of sprite.
|
||
if (e->IsInitImportActionsTag())
|
||
((GFxInitImportActions*)e)->ExecuteInContext(this, pdefImpl);
|
||
else
|
||
e->ExecuteWithPriority(this, GFxMovieRoot::AP_Highest);
|
||
}
|
||
}
|
||
}
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
// Add the given action buffer to the list of action
|
||
// buffers to be processed at the end of the next frame advance in root.
|
||
void GFxSprite::AddActionBuffer(GASActionBuffer* a, GFxActionPriority::Priority prio)
|
||
{
|
||
GFxMovieRoot::ActionEntry* pe = pRoot->InsertEmptyAction(prio);
|
||
if (pe) pe->SetAction(this, a);
|
||
}
|
||
|
||
// Set the sprite state at the specified frame number.
|
||
// 0-based frame numbers!! (in contrast to ActionScript and Flash MX)
|
||
void GFxSprite::GotoFrame(UInt targetFrameNumber)
|
||
{
|
||
if (IsUnloading())
|
||
return;
|
||
// IF_VERBOSE_DEBUG(LogMsg("sprite::GotoFrame(%d)\n", targetFrameNumber));//xxxxx
|
||
|
||
targetFrameNumber = (UInt)G_Clamp<SInt>(targetFrameNumber, 0, (SInt)GetLoadingFrame() - 1);
|
||
|
||
#ifndef GFC_NO_SOUND
|
||
// stop streaming sound
|
||
SetStreamingSound(NULL);
|
||
#endif
|
||
|
||
if (targetFrameNumber < CurrentFrame)
|
||
{
|
||
//GASSERT(!(GetName() == "ss7" && targetFrameNumber == 6 && CurrentFrame == 7));
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("\n #### gotoFrame backward, from %d to %d, this = %s\n",
|
||
CurrentFrame, targetFrameNumber, GetCharacterHandle()->GetNamePath().ToCStr());
|
||
DumpDisplayList(0, "Before");
|
||
#endif
|
||
// Mark all <20>deletable<6C> characters for removal. The <20>deletable<6C> characters include
|
||
// the following characters:
|
||
// the ones, created by the timeline, even if they were touched by ActionScript
|
||
// (AcceptAnimMoves = false), but only if the <20>CreateFrame<6D> property of these
|
||
// characters is greater or equal than TargetFrame (so, they were created ON
|
||
// or AFTER the TargetFrame).
|
||
DisplayList.MarkAllEntriesForRemoval((targetFrameNumber) ? targetFrameNumber-1 : 0);
|
||
//DumpDisplayList(0, "Interm1");
|
||
|
||
if (targetFrameNumber >= 1)
|
||
{
|
||
// We need to make a snapshot from Frame 0 to targetFrame - 1.
|
||
// The TargetFrame is excluded because we would execute all tags for
|
||
// the target frame by the regular way <20> calling ExecuteFrameTags.
|
||
GFxTimelineSnapshot snapshot(GFxTimelineSnapshot::Direction_Backward, pRoot->GetHeap(), this);
|
||
MakeSnapshot(&snapshot, 0, targetFrameNumber-1);
|
||
|
||
// Set the current frame to target one and execute the snapshot
|
||
CurrentFrame = targetFrameNumber;
|
||
ExecuteSnapshot(&snapshot);
|
||
}
|
||
else
|
||
{
|
||
// if targetFrame is 0, no snapshot is necessary
|
||
CurrentFrame = targetFrameNumber;
|
||
}
|
||
//DumpDisplayList(0, "Interm2");
|
||
|
||
// Execute target frame's tags
|
||
ExecuteFrameTags(targetFrameNumber);
|
||
//DumpDisplayList(0, "Interm3");
|
||
|
||
// Unload all remaining mark-for-remove characters. Note, this is not just removing
|
||
// such characters from the display list. <20>Unload<61> means: if the character or one of
|
||
// its children has onUnload handler, then this character is not removed from the
|
||
// display list <20> it is being moved to another depth and the onUnload event is
|
||
// scheduled. If no onUnload event handler is defined (neither the character nor
|
||
// its children have it) then this character is unloaded instantly and removed
|
||
// from the display list.
|
||
DisplayList.UnloadMarkedObjects();
|
||
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
DumpDisplayList(0, "After");
|
||
printf(" ^^^^ end of gotoFrame backward, this = %s\n\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr());
|
||
#endif
|
||
DisplayList.CheckConsistency();
|
||
}
|
||
else if (targetFrameNumber > CurrentFrame)
|
||
{
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
printf("\n #### gotoFrame forward, from %d to %d, this = %s\n",
|
||
CurrentFrame, targetFrameNumber, GetCharacterHandle()->GetNamePath().ToCStr());
|
||
DumpDisplayList(0, "Before");
|
||
#endif
|
||
// Need to gather a snapshot only if targetFrame is greater than 1
|
||
// and if it is greater than next frame (CurrentFrame + 1). For the next
|
||
// frame it is not enough to just execute frame tags by the regular way -
|
||
// calling to ExecuteFrameTags.
|
||
if (targetFrameNumber > 1 && targetFrameNumber > CurrentFrame + 1)
|
||
{
|
||
GFxTimelineSnapshot snapshot(GFxTimelineSnapshot::Direction_Forward, pRoot->GetHeap(), this);
|
||
// Making the snapshot starting from the CurrentFrame + 1 and finishing
|
||
// at targetFrame - 1. We will execute tags for the target frame
|
||
// on regular basis by calling to ExecuteFrameTags.
|
||
MakeSnapshot(&snapshot, CurrentFrame + 1, targetFrameNumber-1);
|
||
|
||
// need to run init actions for every frame being skipped, as well as
|
||
// for target one. But for the target frame InitAction tags will be
|
||
// executed from inside the ExecuteFrameTags.
|
||
for (UInt f = CurrentFrame + 1; f < targetFrameNumber; ++f)
|
||
{
|
||
ExecuteInitActionFrameTags(f);
|
||
}
|
||
|
||
CurrentFrame = targetFrameNumber;
|
||
ExecuteSnapshot(&snapshot);
|
||
}
|
||
else
|
||
CurrentFrame = targetFrameNumber;
|
||
|
||
// Execute target frame's tags
|
||
ExecuteFrameTags(targetFrameNumber);
|
||
|
||
#ifdef GFX_TRACE_TIMELINE_EXECUTION
|
||
DumpDisplayList(0, "After");
|
||
printf(" ^^^^ end of gotoFrame forward, this = %s\n\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr());
|
||
#endif
|
||
|
||
DisplayList.CheckConsistency();
|
||
}
|
||
|
||
// GotoFrame stops by default.
|
||
PlayStatePriv = GFxMovie::Stopped;
|
||
}
|
||
|
||
bool GFxSprite::GotoLabeledFrame(const char* label, SInt offset)
|
||
{
|
||
UInt targetFrame = GFC_MAX_UINT;
|
||
if (pDef->GetLabeledFrame(label, &targetFrame, 0))
|
||
{
|
||
GotoFrame((UInt)((SInt)targetFrame + offset));
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
LogWarning("Error: %s, MovieImpl::GotoLabeledFrame('%s') unknown label\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr(), label);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void GFxSprite::SetPlayState(PlayState s)
|
||
{
|
||
PlayStatePriv = s;
|
||
if (!IsUnloading() && !IsUnloaded())
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
#ifndef GFC_NO_SOUND
|
||
if (PlayStatePriv == GFxMovie::Stopped)
|
||
{
|
||
SetStreamingSound(NULL);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void GFxSprite::Display(GFxDisplayContext &context)
|
||
{
|
||
// We're invisible, so don't display!
|
||
//!AB: but mask should be drawn even if it is invisible!
|
||
if (!IsDisplayable())
|
||
return;
|
||
|
||
// save context transform pointers
|
||
GFxDisplayContextTransforms oldXform;
|
||
context.CopyTransformsTo(&oldXform);
|
||
|
||
// We must apply a new binding table if the character needs to
|
||
// use resource data from the loaded/imported GFxMovieDefImpl.
|
||
GFxResourceBinding *psave = context.pResourceBinding;
|
||
context.pResourceBinding = &pDefImpl->GetResourceBinding();
|
||
|
||
GRenderer::Cxform wcx;
|
||
GMatrix2D matrix;
|
||
#ifndef GFC_NO_3D
|
||
GMatrix3D matrix3D;
|
||
GMatrix3D *pmatrix3D = &matrix3D;
|
||
#else
|
||
GMatrix3D *pmatrix3D = NULL;
|
||
#endif
|
||
GFxSprite* pmask = GetMask();
|
||
bool masked = false;
|
||
if (pmask)
|
||
{
|
||
if (!pmask->IsUsedAsMask() || pmask->IsUnloaded())
|
||
{
|
||
// if mask character was unloaded or unmarked as a mask - remove it from the sprite
|
||
SetMask(NULL);
|
||
}
|
||
else
|
||
{
|
||
// mask can be outside the hierarchy
|
||
context.PreDrawMask(pmask, &matrix, pmatrix3D); // prepare context transform pointers
|
||
context.PushAndDrawMask(pmask);
|
||
masked = true;
|
||
}
|
||
}
|
||
|
||
// prepare context transform pointers for display
|
||
context.PreDisplay(oldXform, this, &matrix, pmatrix3D, &wcx);
|
||
|
||
GFxDisplayContextFilters oldFilters;
|
||
CharFilterDesc* pFilters = GetFilters();
|
||
bool filtered = 0;
|
||
|
||
if (pFilters && pFilters->HasFilters)
|
||
{
|
||
#ifndef GFC_NO_FILTERS_PREPASS
|
||
if (pFilters->PrePassResult)
|
||
{
|
||
context.DisplayFilterPrePass(this, pFilters->MainPassFilters,
|
||
pFilters->PrePassResult, pFilters->FrameRect, pFilters->Width, pFilters->Height);
|
||
goto endDisplay;
|
||
}
|
||
#endif
|
||
filtered = context.BeginFilters(oldFilters, this, pFilters->Filters);
|
||
}
|
||
|
||
if (pDrawingAPI)
|
||
{
|
||
GRenderer::BlendType blend = GetActiveBlendMode();
|
||
pDrawingAPI->Display(context, *context.pParentMatrix, wcx, blend, GetClipDepth() > 0);
|
||
}
|
||
|
||
// Check if we need to push user renderer data
|
||
{
|
||
bool bUserDataPushed = false;
|
||
if (pUserData)
|
||
bUserDataPushed = context.GetRenderer()->PushUserData(&pUserData->UserData);
|
||
|
||
DisplayList.Display(context);
|
||
|
||
// Check if we need to pop user renderer data or 3D data
|
||
if (bUserDataPushed)
|
||
context.GetRenderer()->PopUserData();
|
||
}
|
||
|
||
if (filtered)
|
||
context.EndFilters(oldFilters, this, pFilters->Filters);
|
||
|
||
endDisplay:
|
||
context.pResourceBinding = psave;
|
||
context.PostDisplay(oldXform);
|
||
DoDisplayCallback();
|
||
|
||
if (masked)
|
||
{
|
||
context.PopMask();
|
||
}
|
||
}
|
||
|
||
void GFxSprite::PropagateMouseEvent(const GFxEventId& id)
|
||
{
|
||
GPtr<GFxSprite> thisHolder(this); // PropagateMouseEvent may release "this"; preventing.
|
||
|
||
if (id.Id == GFxEventId::Event_MouseMove)
|
||
{
|
||
// Adjust x,y of this character if it is being dragged.
|
||
if (pRoot->IsMouseSupportEnabled())
|
||
GFxASCharacter::DoMouseDrag();
|
||
}
|
||
|
||
// MA: Hidden clips don't receive mouse events.
|
||
// Tested by assigning _visible property to false,
|
||
// but the object didn't hide in Flash.
|
||
if (GetVisible() == false)
|
||
return;
|
||
|
||
DisplayList.PropagateMouseEvent(id);
|
||
// Notify us after children.
|
||
OnEvent(id);
|
||
}
|
||
|
||
void GFxSprite::PropagateKeyEvent(const GFxEventId& id, int* pkeyMask)
|
||
{
|
||
GPtr<GFxSprite> thisHolder(this); // PropagateKeyEvent may release "this"; preventing.
|
||
|
||
// AB: Hidden clips don't receive key events.
|
||
// Tested by assigning _visible property to false,
|
||
// but the object didn't hide in Flash. nEed to test more:
|
||
// what about hidden by timeline?
|
||
if (!GetVisible())
|
||
return;
|
||
|
||
DisplayList.PropagateKeyEvent (id, pkeyMask);
|
||
// Notify us after children.
|
||
OnKeyEvent(id, pkeyMask);
|
||
}
|
||
|
||
void GFxSprite::CloneInternalData(const GFxASCharacter* src)
|
||
{
|
||
GASSERT(src);
|
||
GFxASCharacter::CloneInternalData(src);
|
||
if (src->IsSprite())
|
||
{
|
||
const GFxSprite* pSrcSprite = src->ToSprite();
|
||
if (pSrcSprite->ActsAsButton())
|
||
SetHasButtonHandlers (true);
|
||
}
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
// ThisPtr - sprite
|
||
// Arg(0) - constructor function of the class
|
||
static void GFx_InitializeClassInstance(const GASFnCall& fn)
|
||
{
|
||
GPtr<GFxSprite> pscriptCh = fn.ThisPtr->ToSprite();
|
||
GASSERT(pscriptCh);
|
||
|
||
GASFunctionRef ctorFunc = fn.Arg(0).ToFunction(fn.Env);
|
||
GASSERT(!ctorFunc.IsNull());
|
||
|
||
pscriptCh->SetProtoToPrototypeOf(ctorFunc.GetObjectPtr());
|
||
}
|
||
|
||
// ThisPtr - sprite
|
||
// Arg(0) - String, name of instance
|
||
// Arg(1) - Boolean, true, if construct event should be fired (!AB: not anymore)
|
||
//static
|
||
void GFx_FindClassAndInitializeClassInstance(const GASFnCall& fn)
|
||
{
|
||
GASSERT(fn.Env);
|
||
|
||
GASGlobalContext* gctxt = fn.Env->GetGC();
|
||
GASSERT(gctxt);
|
||
|
||
GASFunctionRef ctorFunc;
|
||
const GASString symbolName(fn.Arg(0).ToString(fn.Env));
|
||
if (!symbolName.IsEmpty())
|
||
{
|
||
if (gctxt->FindRegisteredClass(fn.Env->GetSC(), symbolName, &ctorFunc))
|
||
{
|
||
GASSERT(!ctorFunc.IsNull());
|
||
|
||
GPtr<GFxSprite> pscriptCh = fn.ThisPtr->ToSprite();
|
||
GASSERT(pscriptCh);
|
||
|
||
pscriptCh->SetProtoToPrototypeOf(ctorFunc.GetObjectPtr());
|
||
|
||
// now, check for 'construct' event and fire it if Arg(1) is set to TRUE
|
||
//if (fn.Arg(1).ToBool(fn.Env)) //!AB, fire onConstruct always, since it is impossible
|
||
{ // to check whether onConstruct method exists or not
|
||
GFxMovieRoot::ActionEntry e(pscriptCh, GFxEventId::Event_Construct);
|
||
e.Execute(pscriptCh->GetMovieRoot());
|
||
}
|
||
|
||
// now, invoke a constructor
|
||
GFxMovieRoot::ActionEntry e(pscriptCh, ctorFunc);
|
||
e.Execute(pscriptCh->GetMovieRoot());
|
||
}
|
||
else
|
||
{
|
||
// a special case, when a component doesn't have a class (it is possible if Linkage->AS 2.0 Class field left empty)
|
||
// but there is a construct event for its instance.
|
||
|
||
// now, check for 'construct' event and fire it if Arg(1) is set to TRUE
|
||
//if (fn.Arg(1).ToBool(fn.Env)) //!AB, fire onConstruct always, since it is impossible
|
||
{ // to check whether onConstruct method exists or not
|
||
GPtr<GFxSprite> pscriptCh = fn.ThisPtr->ToSprite();
|
||
GASSERT(pscriptCh);
|
||
|
||
GFxMovieRoot::ActionEntry e(pscriptCh, GFxEventId::Event_Construct);
|
||
e.Execute(pscriptCh->GetMovieRoot());
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
// Arg(0) - Object, initializer object
|
||
static void GFx_InitObjectMembers(const GASFnCall& fn)
|
||
{
|
||
GASSERT(fn.Env);
|
||
GASSERT(fn.NArgs == 1);
|
||
|
||
GPtr<GFxSprite> pscriptCh = fn.ThisPtr->ToSprite();
|
||
GASSERT(pscriptCh);
|
||
|
||
const GASObjectInterface* pinitSource = fn.Arg(0).ToObjectInterface(fn.Env);
|
||
|
||
struct InitVisitor : public GASObject::MemberVisitor
|
||
{
|
||
GASEnvironment *pEnv;
|
||
GFxASCharacter *pCharacter;
|
||
|
||
InitVisitor(GASEnvironment *penv, GFxASCharacter *pchar)
|
||
: pEnv(penv), pCharacter(pchar)
|
||
{ }
|
||
|
||
virtual void Visit(const GASString& name, const GASValue& val, UByte flags)
|
||
{
|
||
GUNUSED(flags);
|
||
pCharacter->SetMember(pEnv, name, val);
|
||
}
|
||
};
|
||
InitVisitor memberVisitor(fn.Env, pscriptCh);
|
||
pinitSource->VisitMembers(memberVisitor.pEnv->GetSC(), &memberVisitor);
|
||
}
|
||
|
||
// Returns 0 if nothing to do
|
||
// 1 - if need to add to optimized play list
|
||
// -1 - if need to remove from optimized play list
|
||
int GFxSprite::CheckAdvanceStatus(bool playingNow)
|
||
{
|
||
int rv = 0;
|
||
#ifndef GFC_USE_OLD_ADVANCE
|
||
// First of all, check if advance is disabled at all. Advance is disabled
|
||
// * if noAdvance extension property is set;
|
||
// * if sprite is invisible and _global.noInvisibleAdvance extension property is set;
|
||
// * if sprite is unloading or unloaded from timeline.
|
||
bool advanceDisabled = (IsAdvanceDisabled() || !CanAdvanceChar());
|
||
|
||
// Check if movie is playable.
|
||
//bool advancable = (!advanceDisabled && GetPlayState() == GFxMovie::Playing);
|
||
bool advancable = (!advanceDisabled && (GetPlayState() == GFxMovie::Playing ||
|
||
pRoot->IsDraggingCharacter(this)
|
||
#ifndef GFC_NO_SOUND
|
||
|| (pActiveSounds && pActiveSounds->Sounds.GetSize() != 0)
|
||
#endif
|
||
));
|
||
|
||
if (advancable)
|
||
{
|
||
// if it is already playing and advancable - do nothing (return 0).
|
||
// otherwise, return 1 (play).
|
||
rv = !playingNow; //(playingNow) ? 0 : 1
|
||
#ifdef GFC_TRACE_ADVANCE
|
||
if (rv)
|
||
{
|
||
printf("+++ Advance: Starting to play: %s, state %s\n", GetCharacterHandle()->GetNamePath().ToCStr(),
|
||
(GetPlayState() == GFxMovie::Playing) ? "playing" : "stopped");
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
if (playingNow)
|
||
{
|
||
// check, if the onEnterFrame exists
|
||
if (!advanceDisabled)
|
||
{
|
||
if (!HasEventHandler(GFxEventId::Event_EnterFrame))
|
||
{
|
||
rv = -1;
|
||
#ifdef GFC_TRACE_ADVANCE
|
||
printf("+++ Advance: Stopping play (no onEnterFrame): %s, state %s\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr(),
|
||
(GetPlayState() == GFxMovie::Playing) ? "playing" : "stopped");
|
||
#endif
|
||
}
|
||
}
|
||
else
|
||
{
|
||
rv = -1;
|
||
#ifdef GFC_TRACE_ADVANCE
|
||
printf("+++ Advance: Stopping play (adv disabled): %s, state %s\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr(),
|
||
(GetPlayState() == GFxMovie::Playing) ? "playing" : "stopped");
|
||
#endif
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!advanceDisabled)
|
||
{
|
||
if (HasEventHandler(GFxEventId::Event_EnterFrame))
|
||
{
|
||
rv = 1;
|
||
#ifdef GFC_TRACE_ADVANCE
|
||
printf("+++ Advance: Starting to play (onEnterFrame): %s, state %s\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr(),
|
||
(GetPlayState() == GFxMovie::Playing) ? "playing" : "stopped");
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
GUNUSED(playingNow);
|
||
#endif //#ifndef GFC_USE_OLD_ADVANCE
|
||
return rv;
|
||
}
|
||
|
||
// Add an object to the display list.
|
||
GFxCharacter* GFxSprite::AddDisplayObject(
|
||
const GFxCharPosInfo &pos,
|
||
const GASString &name,
|
||
const GArrayLH<GFxSwfEvent*, GFxStatMD_Tags_Mem> *peventHandlers,
|
||
const GASObjectInterface *pinitSource,
|
||
UInt createFrame,
|
||
UInt32 addFlags,
|
||
GFxCharacterCreateInfo* pcharCreateOverride,
|
||
GFxASCharacter* origChar)
|
||
{
|
||
GASSERT(pDef);
|
||
|
||
bool placeObject = (addFlags & GFxDisplayList::Flags_PlaceObject) != 0;
|
||
bool replaceIfDepthIsOccupied = (addFlags & GFxDisplayList::Flags_ReplaceIfDepthIsOccupied) != 0;
|
||
|
||
// TBD:
|
||
// Here we look up a character but do not return it's binding.
|
||
// This means that while we will have the right character created,
|
||
// its pDefImpl will correspond OUR table and not THEIR table.
|
||
// However, imported characters MUST reference their own tables for resources!
|
||
|
||
GFxCharacterCreateInfo ccinfo = pcharCreateOverride ? *pcharCreateOverride :
|
||
pDefImpl->GetCharacterCreateInfo(pos.CharacterId);
|
||
if (!ccinfo.pCharDef)
|
||
{
|
||
LogError("%s, GFxSprite::AddDisplayObject(): unknown cid = %d\n",
|
||
GetCharacterHandle()->GetNamePath().ToCStr(), pos.CharacterId.GetIdIndex());
|
||
return NULL;
|
||
}
|
||
|
||
// RAGE - context messages
|
||
DIAG_CONTEXT_MESSAGE("AS CFn: Adding %s to %s", name.ToCStr(), GetCharacterHandle()->GetNamePath().ToCStr());
|
||
|
||
|
||
// If we already have this object on this plane, then move it instead of replacing it.
|
||
// This can happen when wrapping around from last frame to first one, where
|
||
// the first frame fill bring back to life objects marked for removal after last frame.
|
||
// It can also happen when seeking back.
|
||
bool markedForRemove = false;
|
||
GFxCharacter* pexistingChar = DisplayList.GetCharacterAtDepth(pos.Depth, &markedForRemove);
|
||
|
||
if (placeObject)
|
||
{
|
||
// If the object was moved explicitly in Action script, leave it there.
|
||
bool animatedByTimeline = true;
|
||
if (pexistingChar)
|
||
{
|
||
animatedByTimeline = pexistingChar->GetAcceptAnimMoves();
|
||
if (!animatedByTimeline && pexistingChar->GetContinueAnimationFlag())
|
||
{
|
||
pexistingChar->SetAcceptAnimMoves(true);// reset the flag
|
||
animatedByTimeline = true;
|
||
}
|
||
}
|
||
if (!markedForRemove && pexistingChar && !animatedByTimeline)
|
||
return NULL;
|
||
|
||
if ( pexistingChar && !pexistingChar->IsUnloadQueuedUp() &&
|
||
(pexistingChar->GetId() == pos.CharacterId))
|
||
{
|
||
// We need to use OriginalName here to determine is it the same character
|
||
// or a different one. We can't use the regular name (GetName() method), since
|
||
// it could be changed via ActionScript (_name property) and this shouldn't
|
||
// affect the timeline execution (see test_name_change1.swf)
|
||
const GASString originalName
|
||
((pexistingChar->IsCharASCharacter()) ? pexistingChar->CharToASCharacter_Unsafe()->GetOriginalName() :
|
||
ASEnvironment.GetBuiltin(GASBuiltin_empty_));
|
||
|
||
// The name can be set only for scriptable characters, such as Sprite, Button and TextField.
|
||
// Though, theoretically it is possible that PlaceObject for generic character contains
|
||
// a name. Technically, this is incorrect, but possible (see wizardy.swf).
|
||
// In this case, we need to skip names comparison. We can use pexistingChar->IsASCharacter()
|
||
// criteria, since Ids are equal.
|
||
|
||
if (!pexistingChar->IsCharASCharacter() ||
|
||
(name.IsEmpty() && originalName.IsEmpty()) ||
|
||
(name.IsEmpty() && ((GFxASCharacter*)pexistingChar)->HasInstanceBasedName()) ||
|
||
(!name.IsEmpty() && originalName == name))
|
||
{
|
||
// Move will bring the object back by clearing its MarkForRemove flag
|
||
// in display list. We only do so if we are talking about THE SAME object,
|
||
// i.e. created in the same frame. Flash considers objects created in
|
||
// different frames to be different, even if they share a name.
|
||
if (pexistingChar->GetCreateFrame() == ((createFrame == GFC_MAX_UINT) ? CurrentFrame : createFrame))
|
||
{
|
||
GFxCharPosInfo newPos = pos;
|
||
if (pexistingChar->IsCharASCharacter())
|
||
{
|
||
GFxASCharacter* asCh = pexistingChar->CharToASCharacter_Unsafe();
|
||
if (!pos.HasFilters() && asCh->GetFilters())
|
||
newPos.SetFiltersFlag();
|
||
}
|
||
if (!pos.HasBlendMode() && pexistingChar->GetBlendMode() != GRenderer::Blend_None)
|
||
{
|
||
newPos.SetBlendModeFlag();
|
||
newPos.BlendMode = GRenderer::Blend_None;
|
||
}
|
||
if (!pos.HasCxform() && !pexistingChar->GetCxform().IsIdentity())
|
||
{
|
||
// if the object is moved by AddDisplayObject and no color transform suppose to
|
||
// to be used then we need to reset the color transform for the existing
|
||
// character, since it may have it changed earlier.
|
||
// This is actual when the character has changed Cxform matrix and
|
||
// then time-line goes to the first frame, where this Cxform doesn't
|
||
// exist. (!AB)
|
||
// GFxCharPosInfo newPos = pos;
|
||
newPos.SetCxFormFlag();
|
||
newPos.ColorTransform.SetIdentity();
|
||
}
|
||
|
||
MoveDisplayObject(newPos);
|
||
|
||
// if character was marked for remove the
|
||
return NULL;
|
||
}
|
||
// Otherwise replace object with a new-state fresh instance of it.
|
||
replaceIfDepthIsOccupied = 1;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
replaceIfDepthIsOccupied = 1;
|
||
}
|
||
SetDirtyFlag();
|
||
|
||
GPtr<GFxCharacter> ch = *ccinfo.pCharDef->CreateCharacterInstance(this, pos.CharacterId,
|
||
ccinfo.pBindDefImpl);
|
||
GASSERT(ch);
|
||
GFxASCharacter* pscriptCh = ch->CharToASCharacter();
|
||
GFxSprite* pspriteCh = pscriptCh ? pscriptCh->ToSprite() : 0;
|
||
bool nameSet = 0;
|
||
|
||
ch->SetScale9GridExists(false);
|
||
|
||
const GFxASCharacter* parent = ch->GetParent();
|
||
while (parent)
|
||
{
|
||
if (parent->GetScale9Grid())
|
||
{
|
||
ch->SetScale9GridExists(true);
|
||
ch->PropagateScale9GridExists();
|
||
break;
|
||
}
|
||
parent = parent->GetParent();
|
||
}
|
||
|
||
if (pscriptCh)
|
||
{
|
||
if (!name.IsEmpty())
|
||
{
|
||
ch->SetOriginalName(name);
|
||
nameSet = 1;
|
||
}
|
||
|
||
if (origChar)
|
||
{
|
||
// if we are duplicating char - call CloneInternalData
|
||
pscriptCh->CloneInternalData(origChar);
|
||
}
|
||
|
||
// in the case if AddDisplayObject is not initiated through timeline
|
||
// (Flag_PlaceObject is NOT set) we need to mark the newly created
|
||
// object by SetAcceptAnimMovies(false).
|
||
if (!(addFlags & GFxDisplayList::Flags_PlaceObject))
|
||
pscriptCh->SetAcceptAnimMoves(false);
|
||
else
|
||
pscriptCh->SetTimelineObjectFlag(true);
|
||
}
|
||
if ((addFlags & GFxDisplayList::Flags_PlaceObject))
|
||
ch->SetTimelineObjectFlag(true);
|
||
|
||
ch->SetCreateFrame((createFrame == GFC_MAX_UINT) ? CurrentFrame : createFrame);
|
||
|
||
// Attach event Handlers (if any).
|
||
|
||
bool hasConstructEvent = true; //!AB, fire onConstruct always, since it is impossible
|
||
// to check whether onConstruct method exists or not
|
||
GFxMovieRoot* proot = GetMovieRoot();
|
||
if (pscriptCh && peventHandlers)
|
||
{
|
||
for (UPInt i = 0, n = peventHandlers->GetSize(); i < n; i++)
|
||
{
|
||
(*peventHandlers)[i]->AttachTo(pscriptCh);
|
||
// If we installed a button handler, sprite will work in button mode...
|
||
if (pspriteCh && (*peventHandlers)[i]->Event.IsButtonEvent())
|
||
pspriteCh->SetHasButtonHandlers (true);
|
||
|
||
if (placeObject)
|
||
{
|
||
if ((*peventHandlers)[i]->Event.Id == GFxEventId::Event_Initialize)
|
||
{
|
||
GASActionBufferData* pactionOpData = (*peventHandlers)[i]->pActionOpData;
|
||
if (pactionOpData && !pactionOpData->IsNull())
|
||
{
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Initialize);
|
||
if (pe) pe->SetAction(pscriptCh, GFxEventId::Event_Initialize);
|
||
//{
|
||
// GPtr<GASActionBuffer> pbuff =
|
||
// *GHEAP_NEW(proot->GetMovieHeap())
|
||
// GASActionBuffer(pscriptCh->GetASEnvironment()->GetSC(), pactionOpData);
|
||
// pe->SetAction(pscriptCh, pbuff);
|
||
//}
|
||
}
|
||
}
|
||
//!AB, fire onConstruct always, since it is impossible
|
||
// to check whether onConstruct method exists or not. It is only possible
|
||
// to check whether onClipEvent(construct) exists or not, but this is not enough.
|
||
//if ((*peventHandlers)[i]->Event.Id == GFxEventId::Event_Construct)
|
||
//{
|
||
// hasConstructEvent = true;
|
||
//}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Check for registered classes
|
||
UInt sessionId = 0;
|
||
if (pscriptCh)
|
||
{
|
||
GASGlobalContext* gctxt = this->GetGC();
|
||
GASSERT(gctxt);
|
||
|
||
GASFunctionRef ctorFunc;
|
||
const GString* psymbolName = ch->GetResourceMovieDef()->GetNameOfExportedResource(ccinfo.pCharDef->GetId());
|
||
|
||
UInt oldSessionId;
|
||
sessionId = proot->ActionQueue.StartNewSession(&oldSessionId);
|
||
|
||
bool initSourceUsed = false;
|
||
|
||
if (psymbolName)
|
||
{
|
||
GASString symbolName = GetASEnvironment()->CreateString(*psymbolName);
|
||
|
||
if (gctxt->FindRegisteredClass(GetASEnvironment()->GetSC(), symbolName, &ctorFunc))
|
||
{
|
||
// first - schedule initializer of class instance. It will
|
||
// assign proper prototype to the pscriptCh.
|
||
GASValueArray params;
|
||
GFxMovieRoot::ActionEntry* pe;
|
||
|
||
pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Initialize);
|
||
params.PushBack(ctorFunc);
|
||
if (pe) pe->SetAction(pscriptCh, GFx_InitializeClassInstance, ¶ms);
|
||
|
||
//if (hasConstructEvent) //!AB, fire onConstruct always, since it is impossible
|
||
{ // to check whether onConstruct method exists or not
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, GFxEventId::Event_Construct);
|
||
hasConstructEvent = false;
|
||
}
|
||
|
||
// Queue up copying init properties if they exist (for SWF 6+).
|
||
if (GetVersion() >= 6 && pinitSource && pscriptCh)
|
||
{
|
||
params.Resize(0);
|
||
GASValue v;
|
||
v.SetAsObjectInterface(const_cast<GASObjectInterface*>(pinitSource));
|
||
params.PushBack(v);
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, GFx_InitObjectMembers, ¶ms);
|
||
}
|
||
initSourceUsed = true;
|
||
|
||
// now, schedule constructor invocation
|
||
pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, ctorFunc);
|
||
}
|
||
else if (placeObject)
|
||
{
|
||
GASValueArray params;
|
||
params.PushBack(GASValue(symbolName)); //[0]
|
||
//params.PushBack(GASValue(hasConstructEvent)); //[1]
|
||
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, GFx_FindClassAndInitializeClassInstance, ¶ms);
|
||
hasConstructEvent = false;
|
||
}
|
||
}
|
||
if (placeObject)
|
||
{
|
||
if (hasConstructEvent)
|
||
{
|
||
// if no class is registered, only onClipEvent(construct)
|
||
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, GFxEventId::Event_Construct);
|
||
}
|
||
}
|
||
else if (!initSourceUsed)
|
||
{
|
||
// Not a placeobject (means - attachMovie or duplicateMovieClip) and no registered class:
|
||
// queue up copying init properties if they exist (for SWF 6+).
|
||
if (GetVersion() >= 6 && pinitSource && pscriptCh)
|
||
{
|
||
GASValueArray params;
|
||
GASValue v;
|
||
v.SetAsObjectInterface(const_cast<GASObjectInterface*>(pinitSource));
|
||
params.PushBack(v);
|
||
GFxMovieRoot::ActionEntry* pe = proot->InsertEmptyAction(GFxMovieRoot::AP_Construct);
|
||
if (pe) pe->SetAction(pscriptCh, GFx_InitObjectMembers, ¶ms);
|
||
}
|
||
}
|
||
proot->ActionQueue.RestoreSession(oldSessionId);
|
||
}
|
||
|
||
addFlags &= ~GFxDisplayList::Flags_ReplaceIfDepthIsOccupied;
|
||
if (replaceIfDepthIsOccupied)
|
||
addFlags |= GFxDisplayList::Flags_ReplaceIfDepthIsOccupied;
|
||
|
||
if (pscriptCh)
|
||
{
|
||
pscriptCh->AddToPlayList(proot);
|
||
pscriptCh->ModifyOptimizedPlayList(proot);
|
||
}
|
||
|
||
DisplayList.AddDisplayObject(pos, ch.GetPtr(), addFlags);
|
||
|
||
// Assign sticky variables (only applies if there is an identifiable name).
|
||
if (nameSet)
|
||
proot->ResolveStickyVariables(pscriptCh);
|
||
|
||
GASSERT(!ch || ch->GetRefCount() > 1);
|
||
|
||
// here is a special case for attachMovie. If we have queued up constructors' invocations before
|
||
// DisplayList.AddDisplayObject then we need to execute them right now.
|
||
if (pscriptCh && !placeObject)
|
||
{
|
||
proot->DoActionsForSession(sessionId);
|
||
|
||
// check if onLoad event is allowed. OnEventLoad puts onLoad event into a queue.
|
||
// This happens BEFORE all children are added and BEFORE constructor is executed.
|
||
// But real decision about executing/not executing onLoad event may be done only
|
||
// AFTER all children are added ("DisplayList.AddDisplayObject") and ctor is
|
||
// executed ("DoActionsForSession"), but BEFORE the first frame execution.
|
||
// So, we set a flag "OnLoadReqd", if new sprite really needs "onLoad" event to
|
||
// be executed. This flag is tested in ExecuteEvent and if it is not set for
|
||
// non-timeline sprite, the onLoad event will not be executed, even though
|
||
// it is in the queue. This is necessary for correct execution of onLoad events
|
||
// during "attachMovie" call.
|
||
if (pspriteCh && pspriteCh->HasEventHandler(GFxEventId::Event_Load))
|
||
pspriteCh->SetOnLoadReqd();
|
||
}
|
||
else
|
||
{
|
||
// Allow onLoad events for timeline sprites
|
||
if (pspriteCh)
|
||
pspriteCh->SetOnLoadReqd();
|
||
}
|
||
|
||
if (pscriptCh && nameSet)
|
||
{
|
||
// check if the name is "hitArea" and assign the hitArea if it is so.
|
||
if (name == ASEnvironment.GetBuiltin(GASBuiltin_hitArea))
|
||
SetHitArea((GFxSprite*) pscriptCh);
|
||
}
|
||
return ch.GetPtr();
|
||
}
|
||
|
||
|
||
void GFxSprite::MoveDisplayObject(const GFxCharPosInfo &pos)
|
||
{
|
||
DisplayList.MoveDisplayObject(pos);
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
void GFxSprite::ReplaceDisplayObject(const GFxCharPosInfo &pos, const GASString& name)
|
||
{
|
||
GASSERT(pDef);
|
||
|
||
GFxCharacterCreateInfo ccinfo = pDefImpl->GetCharacterCreateInfo(pos.CharacterId);
|
||
if (ccinfo.pCharDef == NULL)
|
||
{
|
||
LogError("%s, Sprite::ReplaceDisplayObject(): unknown cid = %d\n", GetCharacterHandle()->GetNamePath().ToCStr(), pos.CharacterId.GetIdIndex());
|
||
return;
|
||
}
|
||
GASSERT(ccinfo.pCharDef && ccinfo.pBindDefImpl);
|
||
|
||
GPtr<GFxCharacter> ch = *ccinfo.pCharDef->CreateCharacterInstance(this, pos.CharacterId,
|
||
ccinfo.pBindDefImpl);
|
||
GASSERT(ch);
|
||
//GASSERT(!ch->IsCharASCharacter()); //!AB: technically, AS chars shouldn't be used here
|
||
|
||
ReplaceDisplayObject(pos, ch, name);
|
||
|
||
// we need to set correct create frame to the character
|
||
ch->SetCreateFrame(CurrentFrame);
|
||
}
|
||
|
||
|
||
void GFxSprite::ReplaceDisplayObject(const GFxCharPosInfo &pos, GFxCharacter* ch, const GASString& name)
|
||
{
|
||
GASSERT(ch != NULL);
|
||
if (!name.IsEmpty())
|
||
ch->SetName(name);
|
||
|
||
const GFxASCharacter* parent = ch->GetParent();
|
||
while (parent)
|
||
{
|
||
if (parent->GetScale9Grid())
|
||
{
|
||
ch->SetScale9GridExists(true);
|
||
ch->PropagateScale9GridExists();
|
||
break;
|
||
}
|
||
parent = parent->GetParent();
|
||
}
|
||
|
||
//!AB: ReplaceDisplayObject shouldn't be used for "active" objects, such as sprites,
|
||
// buttons or dynamic textfields. For such "active" object the Remove tag should be
|
||
// executed first. However, Flash CS5 sometimes uses EditTextChar instead of
|
||
// StaticText and applies "replace" to it w/o Remove tag. Not sure if we need
|
||
// to add such objects in playlist or not since they were not intended to be "active".
|
||
//!AB: make sure the replacee char is already in play list (if it is AS char)
|
||
//GASSERT(!ch->IsCharASCharacter() ||
|
||
// ch->CharToASCharacter_Unsafe()->pPlayPrev != NULL ||
|
||
// pRoot->pPlayListHead == ch->CharToASCharacter_Unsafe());
|
||
|
||
DisplayList.ReplaceDisplayObject(pos, ch);
|
||
|
||
if (!name.IsEmpty() && ch->IsCharASCharacter())
|
||
GetMovieRoot()->ResolveStickyVariables(ch->CharToASCharacter_Unsafe());
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
void GFxSprite::RemoveDisplayObject(SInt depth, GFxResourceId id)
|
||
{
|
||
// RAGE - context messages
|
||
#if __BANK
|
||
char depthStr[12];
|
||
sprintf(depthStr, "%d", depth);
|
||
DIAG_CONTEXT_MESSAGE("AS CFn: Removing %s @depth %s", GetName().ToCStr(), depthStr);
|
||
#endif
|
||
|
||
DisplayList.RemoveDisplayObject(depth, id);
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
// For debugging -- return the id of the GFxCharacter at the specified depth.
|
||
// Return -1 if nobody's home.
|
||
GFxResourceId GFxSprite::GetIdAtDepth(int depth)
|
||
{
|
||
GFxCharacter* pch = GetCharacterAtDepth(depth);
|
||
return pch ? pch->GetId() : GFxResourceId();
|
||
}
|
||
|
||
GFxCharacter* GFxSprite::GetCharacterAtDepth(int depth)
|
||
{
|
||
UPInt index = DisplayList.GetDisplayIndex(depth);
|
||
if (index == GFC_MAX_UPINT)
|
||
return 0;
|
||
return DisplayList.GetDisplayObject(index).GetCharacter();
|
||
}
|
||
|
||
|
||
// Movie Loading support
|
||
bool GFxSprite::ReplaceChildCharacter(GFxASCharacter *poldChar, GFxASCharacter *pnewChar)
|
||
{
|
||
GASSERT(poldChar != 0);
|
||
GASSERT(pnewChar != 0);
|
||
GASSERT(poldChar->GetParent() == this);
|
||
|
||
// Get the display entry.
|
||
UPInt index = DisplayList.GetDisplayIndex(poldChar->GetDepth());
|
||
if (index == GFC_MAX_UPINT)
|
||
return 0;
|
||
|
||
// Copy physical properties & re-link all ActionScript references.
|
||
pnewChar->CopyPhysicalProperties(poldChar);
|
||
// For Sprites, we also copy _lockroot.
|
||
if (pnewChar->IsSprite() && poldChar->IsSprite())
|
||
{
|
||
pnewChar->ToSprite()->SetLockRoot(poldChar->ToSprite()->IsLockRoot());
|
||
|
||
// Filters
|
||
GFxASCharacter::CharFilterDesc* poldfilters = poldChar->ToSprite()->GetFilters();
|
||
if (poldfilters)
|
||
{
|
||
if (poldfilters->LastIsTemp)
|
||
{
|
||
GArray<GFxFilterDesc> newfilters (poldfilters->Filters);
|
||
newfilters.PopBack();
|
||
pnewChar->SetFilters(newfilters);
|
||
}
|
||
else
|
||
pnewChar->SetFilters(poldfilters->Filters);
|
||
}
|
||
}
|
||
|
||
// Inform old character of unloading.
|
||
// Use OnUnloading instead of OnEventUnload to fire onUnload events.
|
||
poldChar->OnUnloading();
|
||
poldChar->SetUnloading(true);
|
||
GetMovieRoot()->DoActions();
|
||
|
||
pnewChar->MoveNameHandle(poldChar);
|
||
|
||
// Alter the display list.
|
||
// Need to get the index again since it might be changed because of DoActions.
|
||
index = DisplayList.GetDisplayIndex(poldChar->GetDepth());
|
||
if (index == GFC_MAX_UPINT)
|
||
return 0;
|
||
DisplayList.ReplaceDisplayObjectAtIndex(index, pnewChar);
|
||
SetDirtyFlag();
|
||
return true;
|
||
}
|
||
|
||
bool GFxSprite::ReplaceChildCharacterOnLoad(GFxASCharacter *poldChar, GFxASCharacter *pnewChar)
|
||
{
|
||
if (!ReplaceChildCharacter(poldChar, pnewChar))
|
||
return false;
|
||
|
||
// And call load.
|
||
pnewChar->OnEventLoad();
|
||
GetMovieRoot()->DoActions();
|
||
SetDirtyFlag();
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// *** Sprite ActionScript support
|
||
//
|
||
|
||
// GFxASCharacter override to indicate which standard members are handled for us.
|
||
UInt32 GFxSprite::GetStandardMemberBitMask() const
|
||
{
|
||
// MovieClip lets base handle all members it supports.
|
||
return UInt32(
|
||
M_BitMask_PhysicalMembers |
|
||
M_BitMask_CommonMembers |
|
||
(1 << M_target) |
|
||
(1 << M_droptarget) |
|
||
(1 << M_url) |
|
||
(1 << M_blendMode) |
|
||
(1 << M_cacheAsBitmap) |
|
||
(1 << M_filters) |
|
||
(1 << M_focusrect) |
|
||
(1 << M_enabled) |
|
||
(1 << M_trackAsMenu) |
|
||
(1 << M_lockroot) |
|
||
(1 << M_tabEnabled) |
|
||
(1 << M_tabIndex) |
|
||
(1 << M_useHandCursor) |
|
||
(1 << M_quality) |
|
||
(1 << M_highquality) |
|
||
(1 << M_soundbuftime) |
|
||
(1 << M_xmouse) |
|
||
(1 << M_ymouse) |
|
||
|
||
// GFxASCharacter will report these as read-only for us,
|
||
// but we still need to handle them in GetMember.
|
||
(1 << M_currentframe) |
|
||
(1 << M_totalframes) |
|
||
(1 << M_framesloaded)
|
||
);
|
||
}
|
||
|
||
bool GFxSprite::GetStandardMember(StandardMember member, GASValue* pval, bool opcodeFlag) const
|
||
{
|
||
if (GFxASCharacter::GetStandardMember (member, pval, opcodeFlag))
|
||
return true;
|
||
|
||
// Handle MovieClip specific "standard" members.
|
||
switch(member)
|
||
{
|
||
case M_currentframe:
|
||
pval->SetInt(CurrentFrame + 1);
|
||
return true;
|
||
|
||
case M_totalframes:
|
||
pval->SetInt(pDef->GetFrameCount());
|
||
return true;
|
||
|
||
case M_framesloaded:
|
||
pval->SetInt(GetLoadingFrame());
|
||
return true;
|
||
|
||
case M_lockroot:
|
||
pval->SetBool(IsLockRoot());
|
||
return true;
|
||
|
||
case M_focusEnabled:
|
||
{
|
||
if (FocusEnabled.IsDefined())
|
||
pval->SetBool(FocusEnabled.IsTrue());
|
||
else
|
||
pval->SetUndefined();
|
||
return 1;
|
||
}
|
||
|
||
case M_tabChildren:
|
||
{
|
||
// Is a yellow rectangle visible around a focused GFxASCharacter Clip (?)
|
||
if (TabChildren.IsDefined())
|
||
pval->SetBool(TabChildren.IsTrue());
|
||
else
|
||
pval->SetUndefined();
|
||
return 1;
|
||
}
|
||
|
||
case M_hitArea:
|
||
if (pHitAreaHandle)
|
||
{
|
||
pval->SetAsCharacterHandle(pHitAreaHandle);
|
||
return 1;
|
||
}
|
||
else
|
||
pval->SetUndefined();
|
||
break;
|
||
|
||
case M_scale9Grid:
|
||
if (GetASEnvironment()->GetVersion() >= 8)
|
||
{
|
||
if (pScale9Grid)
|
||
{
|
||
GASEnvironment* penv = const_cast<GASEnvironment*>(GetASEnvironment());
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_RECTANGLE
|
||
GPtr<GASRectangleObject> rectObj = *GHEAP_NEW(penv->GetHeap()) GASRectangleObject(penv);
|
||
GASRect gr(TwipsToPixels(pScale9Grid->x),
|
||
TwipsToPixels(pScale9Grid->y),
|
||
TwipsToPixels(pScale9Grid->x+pScale9Grid->w),
|
||
TwipsToPixels(pScale9Grid->y+pScale9Grid->h));
|
||
rectObj->SetProperties(penv, gr);
|
||
#else
|
||
GPtr<GASObject> rectObj = *GHEAP_NEW(penv->GetHeap()) GASObject(penv);
|
||
GASStringContext *psc = penv->GetSC();
|
||
rectObj->SetConstMemberRaw(psc, "x", TwipsToPixels(pScale9Grid->x));
|
||
rectObj->SetConstMemberRaw(psc, "y", TwipsToPixels(pScale9Grid->y));
|
||
rectObj->SetConstMemberRaw(psc, "width", TwipsToPixels(pScale9Grid->x+pScale9Grid->w));
|
||
rectObj->SetConstMemberRaw(psc, "height", TwipsToPixels(pScale9Grid->y+pScale9Grid->h));
|
||
#endif
|
||
pval->SetAsObject(rectObj);
|
||
}
|
||
else
|
||
{
|
||
pval->SetUndefined();
|
||
}
|
||
return true;
|
||
}
|
||
break;
|
||
|
||
// extension
|
||
case M_hitTestDisable:
|
||
if (GetASEnvironment()->CheckExtensions())
|
||
{
|
||
pval->SetBool(IsHitTestDisableFlagSet());
|
||
return 1;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool GFxSprite::SetStandardMember(StandardMember member, const GASValue& origVal, bool opcodeFlag)
|
||
{
|
||
GASValue val(origVal);
|
||
GASEnvironment* penv = GetASEnvironment();
|
||
if (member > M_BuiltInProperty_End)
|
||
{
|
||
// Check, if there are watch points set. Invoke, if any.
|
||
if (penv && ASMovieClipObj && ASMovieClipObj->HasWatchpoint()) // have set any watchpoints?
|
||
{
|
||
GASValue newVal;
|
||
if (ASMovieClipObj->InvokeWatchpoint(penv,
|
||
penv->CreateConstString(GFxASCharacter::MemberTable[member].pName), val, &newVal))
|
||
{
|
||
val = newVal;
|
||
}
|
||
}
|
||
}
|
||
if (GFxASCharacter::SetStandardMember (member, val, opcodeFlag))
|
||
return true;
|
||
|
||
// Handle MovieClip specific "standard" members.
|
||
switch(member)
|
||
{
|
||
case M_currentframe:
|
||
case M_totalframes:
|
||
case M_framesloaded:
|
||
// Already handled as Read-Only in base,
|
||
// so we won't get here.
|
||
return true;
|
||
|
||
case M_lockroot:
|
||
SetLockRoot(val.ToBool(GetASEnvironment()));
|
||
return true;
|
||
|
||
case M_focusEnabled:
|
||
if (!val.IsUndefined())
|
||
FocusEnabled = val.ToBool(GetASEnvironment());
|
||
else
|
||
FocusEnabled = Bool3W();
|
||
return 1;
|
||
|
||
case M_tabChildren:
|
||
if (!val.IsUndefined())
|
||
TabChildren = val.ToBool(GetASEnvironment());
|
||
else
|
||
TabChildren = Bool3W();
|
||
return 1;
|
||
|
||
// extension
|
||
case M_hitTestDisable:
|
||
if (GetASEnvironment()->CheckExtensions())
|
||
{
|
||
SetHitTestDisableFlag(val.ToBool(GetASEnvironment()));
|
||
return 1;
|
||
}
|
||
break;
|
||
|
||
case M_hitArea:
|
||
{
|
||
// If hit area is not a MovieClip then it should be handled as regular member
|
||
GFxASCharacter* pcharacter = val.ToASCharacter(GetASEnvironment());
|
||
if (pcharacter && pcharacter->IsSprite())
|
||
{
|
||
SetHitArea((GFxSprite*) pcharacter);
|
||
return 1;
|
||
}
|
||
SetHitArea(0);
|
||
}
|
||
break;
|
||
|
||
case M_scale9Grid:
|
||
if (GetASEnvironment()->GetVersion() >= 8)
|
||
{
|
||
GASEnvironment* penv = GetASEnvironment();
|
||
GASObject* pobj = val.ToObject(penv);
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_RECTANGLE
|
||
if (pobj && pobj->GetObjectType() == Object_Rectangle)
|
||
{
|
||
GASRectangleObject* prect = (GASRectangleObject*)pobj;
|
||
GASRect gr;
|
||
prect->GetProperties(penv, gr);
|
||
GFxScale9Grid sg;
|
||
sg.x = PixelsToTwips(Float(gr.Left));
|
||
sg.y = PixelsToTwips(Float(gr.Top));
|
||
sg.w = PixelsToTwips(Float(gr.Width()));
|
||
sg.h = PixelsToTwips(Float(gr.Height()));
|
||
SetScale9Grid(&sg);
|
||
}
|
||
#else
|
||
if (pobj)
|
||
{
|
||
GASStringContext *psc = penv->GetSC();
|
||
GASValue params[4];
|
||
pobj->GetConstMemberRaw(psc, "x", ¶ms[0]);
|
||
pobj->GetConstMemberRaw(psc, "y", ¶ms[1]);
|
||
pobj->GetConstMemberRaw(psc, "width", ¶ms[2]);
|
||
pobj->GetConstMemberRaw(psc, "height", ¶ms[3]);
|
||
GFxScale9Grid sg;
|
||
sg.x = PixelsToTwips(Float(params[0].ToNumber(penv)));
|
||
sg.y = PixelsToTwips(Float(params[1].ToNumber(penv)));
|
||
sg.w = PixelsToTwips(Float(params[2].ToNumber(penv)));
|
||
sg.h = PixelsToTwips(Float(params[3].ToNumber(penv)));
|
||
SetScale9Grid(&sg);
|
||
}
|
||
#endif
|
||
else
|
||
SetScale9Grid(0);
|
||
return true;
|
||
}
|
||
break;
|
||
|
||
// No other custom properties to set for now.
|
||
default:
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Set the named member to the value. Return true if we have
|
||
// that member; false otherwise.
|
||
bool GFxSprite::SetMemberRaw(GASStringContext *psc, const GASString& name, const GASValue& val, const GASPropFlags& flags)
|
||
{
|
||
if (IsStandardMember(name))
|
||
{
|
||
StandardMember member = GetStandardMemberConstant(name);
|
||
if (SetStandardMember(member, val, 0))
|
||
return true;
|
||
}
|
||
|
||
if (ASMovieClipObj || GetMovieClipObject())
|
||
{
|
||
// Note that MovieClipObject will also track setting of button
|
||
// handlers, i.e. 'onPress', etc.
|
||
return ASMovieClipObj->SetMemberRaw(psc, name, val, flags);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// internal method, a common implementation for both GetMember/GetMemberRaw.
|
||
// Only either penv or psc should be not NULL. If penv is not null, then this method
|
||
// works like GetMember, if psc is not NULL - it is like GetMemberRaw
|
||
bool GFxSprite::GetMember(GASEnvironment* penv, GASStringContext *psc, const GASString& name, GASValue* pval)
|
||
{
|
||
if (IsStandardMember(name))
|
||
{
|
||
StandardMember member = GetStandardMemberConstant(name);
|
||
if (GetStandardMember(member, pval, 0))
|
||
return true;
|
||
|
||
switch(member)
|
||
{
|
||
case M_transform:
|
||
{
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_TRANSFORM
|
||
// create a GASTransformObject
|
||
GPtr<GASTransformObject> transfObj =
|
||
*GHEAP_NEW(GetASEnvironment()->GetHeap()) GASTransformObject(GetASEnvironment(), this);
|
||
pval->SetAsObject(transfObj);
|
||
return true;
|
||
#else
|
||
GFC_DEBUG_WARNING(1, "Transform ActionScript class was not included in this GFx build.");
|
||
pval->SetUndefined();
|
||
return true;
|
||
#endif
|
||
}
|
||
case M__version:
|
||
if (IsLevelsMovie())
|
||
{
|
||
pval->SetString(GetASEnvironment()->CreateConstString(GFX_FLASH_VERSION));
|
||
return true;
|
||
}
|
||
break;
|
||
|
||
#ifndef GFC_NO_3D
|
||
case M_matrix3d:
|
||
{
|
||
const GMatrix3D* pm3D = GetMatrix3D();
|
||
if (!pm3D)
|
||
pm3D = &GMatrix3D::Identity;
|
||
|
||
// create a GASArrayObject
|
||
GPtr<GASArrayObject> arrayObj = *GHEAP_NEW(penv->GetHeap()) GASArrayObject(GetASEnvironment());
|
||
arrayObj->Resize(16);
|
||
for(int i=0;i<16;i++)
|
||
arrayObj->SetElement(i, GASValue(pm3D->GetAsFloatArray()[i]));
|
||
|
||
pval->SetAsObject(arrayObj);
|
||
return true;
|
||
}
|
||
|
||
case M_perspfov:
|
||
{
|
||
GeomDataType geomData;
|
||
pval->SetNumber(GetPerspectiveFOV());
|
||
return true;
|
||
}
|
||
case M_z:
|
||
{
|
||
GeomDataType geomData;
|
||
pval->SetNumber(GetGeomData(geomData).Z);
|
||
return true;
|
||
}
|
||
|
||
case M_zscale:
|
||
{
|
||
GeomDataType geomData;
|
||
pval->SetNumber(GetGeomData(geomData).ZScale);
|
||
return true;
|
||
}
|
||
|
||
case M_xrotation:
|
||
{
|
||
GeomDataType geomData;
|
||
pval->SetNumber(GetGeomData(geomData).XRotation);
|
||
return true;
|
||
}
|
||
|
||
case M_yrotation:
|
||
{
|
||
GeomDataType geomData;
|
||
pval->SetNumber(GetGeomData(geomData).YRotation);
|
||
return true;
|
||
}
|
||
#endif
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Handle the __proto__ property here, since we are going to
|
||
// zero out it temporarily (see comments below).
|
||
if ((penv && name == penv->GetBuiltin(GASBuiltin___proto__)) ||
|
||
(psc && name == psc->GetBuiltin(GASBuiltin___proto__)))
|
||
{
|
||
GASObject* proto = Get__proto__();
|
||
pval->SetAsObject(proto);
|
||
return true;
|
||
}
|
||
// The sprite's variables have higher priority than display list, BUT
|
||
// the display list has HIGHER priority than prototype's properties.
|
||
// That is why we would need to do a trick: set __proto__ of the ASMovieClipObj
|
||
// to NULL in order to exclude the prototype and then put it back.
|
||
if (ASMovieClipObj)
|
||
{
|
||
// HACK: zero the __proto__ of ASMovieClipObj to let DisplayList to be
|
||
// checked before the prototype. TODO: redesign.
|
||
GPtr<GASObject> savedProto = ASMovieClipObj->Exchange__proto__(NULL);
|
||
GASSERT(savedProto == Get__proto__());
|
||
bool rv = false;
|
||
if (penv && ASMovieClipObj->GetMember(penv, name, pval))
|
||
{
|
||
rv = true;
|
||
}
|
||
else if (psc && ASMovieClipObj->GetMemberRaw(psc, name, pval))
|
||
{
|
||
rv = true;
|
||
}
|
||
// restore the __proto__
|
||
ASMovieClipObj->Exchange__proto__(savedProto);
|
||
if (rv)
|
||
return true;
|
||
}
|
||
|
||
// Not a built-in property. Check items on our display list.
|
||
GFxASCharacter* pch = DisplayList.GetCharacterByName(ASEnvironment.GetSC(), name);
|
||
if (pch)
|
||
{
|
||
// Found object.
|
||
pval->SetAsCharacter(pch);
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// Now we can search in the __proto__
|
||
GASObject* proto = Get__proto__();
|
||
if (proto)
|
||
{
|
||
// ASMovieClipObj is not created yet; use __proto__ instead
|
||
if (penv && proto->GetMember(penv, name, pval))
|
||
{
|
||
return true;
|
||
}
|
||
else if (psc && proto->GetMemberRaw(psc, name, pval))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (name.GetLength() > 0 && name[0] == '_')
|
||
{
|
||
GASEnvironment::GetVarParams params(name, pval);
|
||
Bool3W rv = ASEnvironment.CheckGlobalAndLevels(params);
|
||
if (rv.IsDefined())
|
||
return rv.IsTrue();
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Find the GFxASCharacter which is one degree removed from us,
|
||
// given the relative pathname.
|
||
//
|
||
// If the pathname is "..", then return our parent.
|
||
// If the pathname is ".", then return ourself. If
|
||
// the pathname is "_level0" or "_root", then return
|
||
// the root pMovie.
|
||
//
|
||
// Otherwise, the name should refer to one our our
|
||
// named characters, so we return it.
|
||
//
|
||
// NOTE: In ActionScript 2.0, top level Names (like
|
||
// "_root" and "_level0") are CASE SENSITIVE.
|
||
// GFxCharacter names in a display list are CASE
|
||
// SENSITIVE. Member names are CASE INSENSITIVE. Gah.
|
||
//
|
||
// In ActionScript 1.0, everything seems to be CASE
|
||
// INSENSITIVE.
|
||
GFxASCharacter* GFxSprite::GetRelativeTarget(const GASString& name, bool first_call)
|
||
{
|
||
bool caseSensitive = IsCaseSensitive();
|
||
GASEnvironment &e = ASEnvironment;
|
||
|
||
if (name.IsBuiltin())
|
||
{
|
||
// Special branches for case-sensitivity for efficiency.
|
||
if (caseSensitive)
|
||
{
|
||
if (e.GetBuiltin(GASBuiltin_dot_) == name || // "."
|
||
e.GetBuiltin(GASBuiltin_this) == name)
|
||
{
|
||
return this;
|
||
}
|
||
else if (e.GetBuiltin(GASBuiltin_dotdot_) == name || // ".."
|
||
e.GetBuiltin(GASBuiltin__parent) == name)
|
||
{
|
||
return GetParent();
|
||
}
|
||
else if (e.GetBuiltin(GASBuiltin__root) == name)
|
||
{
|
||
return GetASRootMovie();
|
||
}
|
||
}
|
||
else
|
||
{ // Case-insensitive version.
|
||
name.ResolveLowercase();
|
||
if (e.GetBuiltin(GASBuiltin_dot_) == name ||
|
||
e.GetBuiltin(GASBuiltin_this).CompareBuiltIn_CaseInsensitive_Unchecked(name))
|
||
{
|
||
return this;
|
||
}
|
||
else if (e.GetBuiltin(GASBuiltin_dotdot_) == name || // ".."
|
||
e.GetBuiltin(GASBuiltin__parent).CompareBuiltIn_CaseInsensitive_Unchecked(name))
|
||
{
|
||
return GetParent();
|
||
}
|
||
else if (e.GetBuiltin(GASBuiltin__root).CompareBuiltIn_CaseInsensitive_Unchecked(name))
|
||
{
|
||
return GetASRootMovie();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (name[0] == '_' && first_call)
|
||
{
|
||
// Parse level index.
|
||
const char* ptail = 0;
|
||
SInt levelIndex = pRoot->ParseLevelName(name.ToCStr(), &ptail,
|
||
caseSensitive);
|
||
if ((levelIndex != -1) && !*ptail)
|
||
return pRoot->GetLevelMovie(levelIndex);
|
||
}
|
||
|
||
// See if we have a match on the display list.
|
||
return DisplayList.GetCharacterByName(e.GetSC(), name);
|
||
}
|
||
|
||
|
||
|
||
// Execute the actions for the specified frame. The
|
||
// FrameSpec could be an integer or a string.
|
||
void GFxSprite::CallFrameActions(UInt frameNumber)
|
||
{
|
||
if (frameNumber == UInt(~0) || frameNumber >= GetLoadingFrame())
|
||
{
|
||
// No dice.
|
||
LogError("Error: %s, CallFrame('%d') - unknown frame\n", GetCharacterHandle()->GetNamePath().ToCStr(), frameNumber);
|
||
return;
|
||
}
|
||
|
||
// These actions will be executed immediately. Such out-of-order execution
|
||
// only happens due to a CallFrame instruction. We implement this by
|
||
// simply assigning a different session id to actions being queued during
|
||
// the execution.
|
||
|
||
UInt aqOldSession, aqCurSession = pRoot->ActionQueue.StartNewSession(&aqOldSession);
|
||
|
||
// Execute the actions.
|
||
const Frame playlist = pDef->GetPlaylist(frameNumber);
|
||
for (UInt i = 0; i < playlist.GetTagCount(); i++)
|
||
{
|
||
GASExecuteTag* e = playlist.GetTag(i);
|
||
if (e->IsActionTag())
|
||
{
|
||
e->Execute(this);
|
||
}
|
||
}
|
||
pRoot->ActionQueue.RestoreSession(aqOldSession);
|
||
|
||
// Execute any new actions triggered by the tag.
|
||
pRoot->DoActionsForSession(aqCurSession);
|
||
|
||
// If some other actions were queued during the DoAction (like gotoAnd<>)- execute them
|
||
// as usual, at the next Advance. (AB)
|
||
}
|
||
|
||
|
||
|
||
// Handle a button event.
|
||
bool GFxSprite::OnButtonEvent(const GFxEventId& id)
|
||
{
|
||
if (!IsEnabledFlagSet())
|
||
return false;
|
||
if (IsLevelsMovie()) // level movies should not handle button events
|
||
return false;
|
||
|
||
// Handle it ourself?
|
||
bool handled = OnEvent(id);
|
||
GFxASCharacter* pparent;
|
||
|
||
if (!handled && ((pparent=GetParent()) != 0))
|
||
{
|
||
// We couldn't handle it ourself, so
|
||
// pass it up to our parent.
|
||
return pparent->OnButtonEvent(id);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void GFxSprite::DetachChild(GFxASCharacter* pchild)
|
||
{
|
||
DisplayList.RemoveCharacter(pchild);
|
||
}
|
||
|
||
// Special event handler; ensures that unload is called on child items in timely manner.
|
||
void GFxSprite::OnEventUnload()
|
||
{
|
||
DIAG_CONTEXT_MESSAGE("OnEventUnload: %s", GetCharacterHandle()->GetNamePath().ToCStr());
|
||
|
||
SetUnloading();
|
||
#ifndef GFC_NO_SOUND
|
||
if (pActiveSounds)
|
||
delete pActiveSounds;
|
||
pActiveSounds = NULL;
|
||
#endif
|
||
|
||
if (pHitAreaHandle)
|
||
SetHitArea(0);
|
||
|
||
SetDirtyFlag();
|
||
|
||
// Cause all children to be unloaded.
|
||
DisplayList.Clear();
|
||
GFxASCharacter::OnEventUnload();
|
||
|
||
// now, wipe out the character, remove the name, release all local variables
|
||
// from environment...
|
||
// This is necessary, if there are still pointers on this character somewhere.
|
||
// For example, if the AS code was not terminated (see GASEnv::NeedTermination).
|
||
// Actually, GFx does not release all local variables, and it doesn't wipe out
|
||
// all members at the moment. TODO(?).
|
||
|
||
//!AB: actually, we can't reset name here, because if another sprite with the same name
|
||
// will appear after on then it may reuse the name handle and the name should be correct.
|
||
//if (pNameHandle)
|
||
// pNameHandle->ResetName(ASEnvironment.GetSC());
|
||
Set__proto__(ASEnvironment.GetSC(), NULL);
|
||
}
|
||
|
||
bool GFxSprite::OnUnloading()
|
||
{
|
||
RemoveFromPlaylist(pRoot);
|
||
|
||
int haIndex = GetHitAreaIndex(); // Returns -1 if not found
|
||
if (haIndex > -1)
|
||
pRoot->SpritesWithHitArea.RemoveAt(haIndex);
|
||
|
||
SetDirtyFlag();
|
||
|
||
DIAG_CONTEXT_MESSAGE("AS CFn: Unloading %s", GetName().ToCStr());
|
||
|
||
// Cause all children to be unloaded.
|
||
bool mayRemove = DisplayList.UnloadAll();
|
||
// check, if we can unload the sprite instantly or if we need just
|
||
// to mark it as "unloading".
|
||
if (mayRemove && HasEventHandler(GFxEventId::Event_Unload))
|
||
mayRemove = false;
|
||
if (!mayRemove)
|
||
{
|
||
// we can't instantly remove the character, because either it has one of the
|
||
// children with Unload event or itself has the Unload handler.
|
||
|
||
// Thus, we need to mark it as "unloading" and queue up the Unload event
|
||
// (no matter, does this character have the unload handler or not; we would
|
||
// need to re-mark it as "unloaded" after the event is executed, see
|
||
// ExecuteEvent).
|
||
|
||
// Here is a special case: if movie is loaded by attachMovie and its
|
||
// onLoad is not called yet, we need to simulate onLoad call before onUnload.
|
||
// Otherwise, we will see only onUnload, since onLoad will be attempted to execute
|
||
// AFTER onLoad. See test_onload_onunload_attachmovie.swf
|
||
if (IsJustLoaded() && !IsAcceptAnimMovesFlagSet())
|
||
{
|
||
GFxMovieRoot::ActionEntry* pe = GetMovieRoot()->InsertEmptyAction();
|
||
if (pe) pe->SetAction(this, GFxEventId::Event_Load);
|
||
}
|
||
GFxMovieRoot::ActionEntry* pe = GetMovieRoot()->InsertEmptyAction();
|
||
if (pe) pe->SetAction(this, GFxEventId::Event_Unload);
|
||
}
|
||
return mayRemove;
|
||
}
|
||
|
||
bool GFxSprite::OnKeyEvent(const GFxEventId& id, int* pkeyMask)
|
||
{
|
||
bool rv = false;
|
||
if (id.Id == GFxEventId::Event_KeyDown)
|
||
{
|
||
// run onKeyDown first.
|
||
rv = OnEvent(id);
|
||
|
||
// also simmulate on(keyPress)
|
||
// covert Event_KeyDown to Event_KeyPress
|
||
GASSERT(pkeyMask != 0);
|
||
|
||
// check if keyPress already was handled then do not handle it again
|
||
if (!((*pkeyMask) & GFxCharacter::KeyMask_KeyPress))
|
||
{
|
||
int kc = id.ConvertToButtonKeyCode();
|
||
if (kc && (rv = OnEvent(GFxEventId(GFxEventId::Event_KeyPress, short(kc), 0)))!=0)
|
||
{
|
||
*pkeyMask |= GFxCharacter::KeyMask_KeyPress;
|
||
}
|
||
}
|
||
|
||
GFxMovieRoot* proot = GetMovieRoot();
|
||
if (proot->IsKeyboardFocused(this, id.KeyboardIndex) && (id.KeyCode == GFxKey::Return || id.KeyCode == GFxKey::Space))
|
||
{
|
||
if (IsFocusRectEnabled() || proot->IsAlwaysEnableKeyboardPress())
|
||
{
|
||
// if focused and enter - simulate on(press)/onPress and on(release)/onRelease
|
||
OnEvent (GFxEventId(GFxEventId::Event_Press, GFxKey::Return, 0, 0, id.KeyboardIndex));
|
||
OnEvent (GFxEventId(GFxEventId::Event_Release, GFxKey::Return, 0, 0, id.KeyboardIndex));
|
||
}
|
||
}
|
||
return rv;
|
||
}
|
||
else
|
||
return OnEvent(id);
|
||
}
|
||
|
||
// Dispatch event Handler(s), if any.
|
||
bool GFxSprite::OnEvent(const GFxEventId& id)
|
||
{
|
||
/*
|
||
if(GetName().ToCStr() && GetName().ToCStr()[0])
|
||
{
|
||
// IF unload executed, clear pparent
|
||
if (id.Id == GFxEventId::Event_Unload)
|
||
GFC_DEBUG_MESSAGE1(1, "Event_Unload event - %s", GetName().ToCStr());
|
||
if (id.Id == GFxEventId::Event_Load)
|
||
GFC_DEBUG_MESSAGE1(1, "Event_Load event - %s", GetName().ToCStr());
|
||
} */
|
||
|
||
// Check if the sprite has special frames, such as _up, _down, _over.
|
||
// Go to these frames if event is Release, Press or RollOver.
|
||
GFxSpriteDef* sp;
|
||
if ((sp = GetSpriteDef()) != NULL)
|
||
{
|
||
if (sp->HasSpecialFrames())
|
||
{
|
||
switch(id.Id)
|
||
{
|
||
case GFxEventId::Event_DragOut:
|
||
case GFxEventId::Event_Release:
|
||
case GFxEventId::Event_RollOver:
|
||
if (sp->HasFrame_over())
|
||
GotoLabeledFrame("_over");
|
||
break;
|
||
case GFxEventId::Event_Press:
|
||
if (sp->HasFrame_down())
|
||
GotoLabeledFrame("_down");
|
||
break;
|
||
case GFxEventId::Event_RollOut:
|
||
case GFxEventId::Event_ReleaseOutside:
|
||
if (sp->HasFrame_up())
|
||
GotoLabeledFrame("_up");
|
||
break;
|
||
default:;
|
||
}
|
||
}
|
||
}
|
||
|
||
do {
|
||
|
||
// First, check for built-in event onClipEvent() handler.
|
||
if (HasClipEventHandler(id))
|
||
{
|
||
// Dispatch.
|
||
continue;
|
||
}
|
||
|
||
// Check for member function.
|
||
// In ActionScript 2.0, event method names are CASE SENSITIVE.
|
||
// In ActionScript 1.0, event method names are CASE INSENSITIVE.
|
||
GASStringContext *psc = GetASEnvironment()->GetSC();
|
||
GASString methodName(id.GetFunctionName(psc));
|
||
if (!methodName.IsEmpty())
|
||
{
|
||
// Event methods can only be in MovieClipObject or Environment.
|
||
// It's important to not call GFx::GetMemberRaw here to save on overhead
|
||
// of checking display list and member constants, which will cost
|
||
// us a lot when this is called in Advance.
|
||
bool hasMethod = false;
|
||
GASValue method;
|
||
|
||
// Resolving of event handlers should NOT involve __resolve and properties handles. Thus,
|
||
// we need to use GetMemberRaw instead of GetMember. (AB)
|
||
GASObject* asObj = (ASMovieClipObj) ? ASMovieClipObj : Get__proto__();
|
||
if (asObj && asObj->GetMemberRaw(ASEnvironment.GetSC(), methodName, &method))
|
||
hasMethod = true;
|
||
//!AB, now we put named functions as a member of target, thus they should be found by the
|
||
// statement above.
|
||
|
||
if (id.Id == GFxEventId::Event_KeyDown || id.Id == GFxEventId::Event_KeyUp)
|
||
{
|
||
// onKeyDown/onKeyUp are available only in Flash 6 and later
|
||
// (don't mess with onClipEvent (keyDown/keyUp)!)
|
||
if (ASEnvironment.GetVersion() >= 6)
|
||
{
|
||
// also, onKeyDown/onKeyUp should be invoked only if focus
|
||
// is enabled and set to this movieclip
|
||
if (!GetMovieRoot()->IsKeyboardFocused(this, id.KeyboardIndex))
|
||
hasMethod = false;
|
||
}
|
||
else
|
||
{
|
||
// Flash 5 and below doesn't have onKeyDown/Up function handlers.
|
||
hasMethod = false;
|
||
}
|
||
}
|
||
|
||
if (hasMethod)
|
||
continue;
|
||
}
|
||
|
||
return false;
|
||
} while (0);
|
||
|
||
// do actual dispatch
|
||
GFxMovieRoot::ActionEntry* pe = GetMovieRoot()->InsertEmptyAction();
|
||
if (pe) pe->SetAction(this, id);
|
||
return true;
|
||
}
|
||
|
||
bool GFxSprite::ExecuteBuffer(GASActionBuffer* pactionbuffer)
|
||
{
|
||
if (!IsUnloaded())
|
||
{
|
||
pactionbuffer->Execute(GetASEnvironment());
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// This happens due to untimely Event_Load/Event_Unload during seek
|
||
//GFC_DEBUG_WARNING1(1, "Action execute after unload: %s", pSprite->GetName().ToCStr());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Execute this even immediately (called for processing queued event actions).
|
||
bool GFxSprite::ExecuteEvent(const GFxEventId& id)
|
||
{
|
||
if (IsUnloaded())
|
||
return false; // do not execute events for unloaded-ready-to-die characters
|
||
|
||
// Keep GASEnvironment alive during any method calls!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
if (id == GFxEventId::Event_Load)
|
||
{
|
||
SetJustLoaded(false);
|
||
if (!IsAcceptAnimMovesFlagSet() && !IsOnLoadReqd())
|
||
return false;
|
||
}
|
||
|
||
bool rv = GFxASCharacter::ExecuteEvent (id);
|
||
// Handler done, mark us as unloaded.
|
||
if (id == GFxEventId::Event_Unload)
|
||
{
|
||
// The character continues its existence after onUnload is invoked;
|
||
// It is removed by the next Advance.
|
||
SetUnloaded(true);
|
||
|
||
#ifndef GFC_USE_OLD_ADVANCE
|
||
// Add to unloaded list
|
||
GASSERT(!IsOptAdvancedListFlagSet());
|
||
pNextUnloaded = pRoot->pUnloadListHead;
|
||
pRoot->pUnloadListHead = this;
|
||
this->AddRef(); // need to addref it since it might be not held by anyone else
|
||
#endif
|
||
}
|
||
return rv;
|
||
}
|
||
|
||
bool GFxSprite::HasEventHandler(const GFxEventId& id) const
|
||
{
|
||
if (HasClipEventHandler(id))
|
||
return true;
|
||
|
||
// Check for member function.
|
||
// In ActionScript 2.0, event method names are CASE SENSITIVE.
|
||
// In ActionScript 1.0, event method names are CASE INSENSITIVE.
|
||
GASStringContext *psc = GetASEnvironment()->GetSC();
|
||
GASString methodName(id.GetFunctionName(psc));
|
||
if (!methodName.IsEmpty())
|
||
{
|
||
// Event methods can only be in MovieClipObject or Environment.
|
||
// It's important to not call GFx::GetMemberRaw here to save on overhead
|
||
// of checking display list and member constants, which will cost
|
||
// us a lot when this is called in Advance.
|
||
GASValue method;
|
||
|
||
// Resolving of event handlers should NOT involve __resolve and properties handles. Thus,
|
||
// we need to use GetMemberRaw instead of GetMember. (AB)
|
||
GASObject* asObj = const_cast<GASObject*> ((ASMovieClipObj) ? ASMovieClipObj : Get__proto__());
|
||
if (asObj && asObj->GetMemberRaw(ASEnvironment.GetSC(), methodName, &method))
|
||
return true;
|
||
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
// Do the events That (appear to) happen as the GFxASCharacter
|
||
// loads. The OnLoad event is generated first. Then,
|
||
// "constructor" frame1 tags and actions are Executed (even
|
||
// before Advance() is called).
|
||
void GFxSprite::OnEventLoad()
|
||
{
|
||
// check if the sprite is timeline animated or was created by attachMovie.
|
||
// In the latter case we need to execute "onLoad" in frame priority to match up
|
||
// with potentially following "onUnload" (if it gets unloaded right after attachMovie).
|
||
if (HasClipEventHandler(GFxEventId::Event_Load))
|
||
{
|
||
pRoot->InsertEmptyAction(GFxActionPriority::AP_Frame)->SetAction(this, GFxEventId::Event_Load);
|
||
SetJustLoaded(true);
|
||
}
|
||
else
|
||
{
|
||
pRoot->InsertEmptyAction(GFxActionPriority::AP_Load)->SetAction(this, GFxEventId::Event_Load);
|
||
SetJustLoaded(true);
|
||
}
|
||
|
||
ExecuteFrameTags(0);
|
||
|
||
// Check if this sprite is a HitArea, and set HitAreaHolder
|
||
UPInt spriteArraySize = pRoot->SpritesWithHitArea.GetSize();
|
||
for (UInt i = 0; i < spriteArraySize; i++)
|
||
if (this == pRoot->SpritesWithHitArea[i]->GetHitArea())
|
||
pRoot->SpritesWithHitArea[i]->SetHitArea(this); // Reset hitArea for efficient resolving
|
||
|
||
}
|
||
|
||
// Do the events that happen when there is XML data waiting
|
||
// on the XML socket connection.
|
||
void GFxSprite::OnEventXmlsocketOnxml()
|
||
{
|
||
GFC_DEBUG_WARNING(1, "OnEventXmlsocketOnxml: unimplemented");
|
||
OnEvent(GFxEventId::Event_SockXML);
|
||
}
|
||
|
||
// Do the events That (appear to) happen on a specified interval.
|
||
void GFxSprite::OnEventIntervalTimer()
|
||
{
|
||
//GFC_DEBUG_WARNING(1, "OnEventIntervalTimer: unimplemented");
|
||
//OnEvent(GFxEventId::TIMER);
|
||
}
|
||
|
||
// Do the events that happen as a MovieClip (swf 7 only) loads.
|
||
void GFxSprite::OnEventLoadProgress()
|
||
{
|
||
GFC_DEBUG_WARNING(1, "OnEventLoadProgress: unimplemented");
|
||
OnEvent(GFxEventId::Event_LoadProgress);
|
||
}
|
||
|
||
bool GFxSprite::Invoke(const char* methodName, GASValue* presult, UInt numArgs)
|
||
{
|
||
// Keep GASEnvironment alive during any method calls!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
return GAS_Invoke(methodName, presult, this, &ASEnvironment, numArgs, ASEnvironment.GetTopIndex());
|
||
}
|
||
|
||
bool GFxSprite::InvokeArgs(const char* methodName, GASValue* presult, const char* methodArgFmt, va_list args)
|
||
{
|
||
// Keep GASEnvironment alive during any method calls!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
return GAS_InvokeParsed(methodName, presult, this, &ASEnvironment, methodArgFmt, args);
|
||
}
|
||
|
||
/*const char* GFxSprite::InvokeArgs(const char* methodName, const char* methodArgFmt,
|
||
const void* const* methodArgs, int numArgs)
|
||
{
|
||
// Keep GASEnvironment alive during any method calls!
|
||
GPtr<GFxSprite> thisPtr(this);
|
||
|
||
return GAS_InvokeParsed(&ASEnvironment, this, methodName, methodArgFmt, methodArgs, numArgs);
|
||
}*/
|
||
|
||
|
||
bool GFxSprite::ActsAsButton() const
|
||
{
|
||
if (IsLevelsMovie() || !IsEnabledFlagSet())
|
||
return false; // _levelN should NOT act as a button no matter what.
|
||
const GASMovieClipObject* asObj = (ASMovieClipObj) ? ASMovieClipObj.GetPtr() :
|
||
static_cast<const GASMovieClipObject*>(Get__proto__());
|
||
if (asObj)
|
||
return asObj->ActsAsButton();
|
||
return false;
|
||
}
|
||
|
||
void GFxSprite::SetHasButtonHandlers(bool has)
|
||
{
|
||
GASMovieClipObject* asObj = GetMovieClipObject();
|
||
if (asObj)
|
||
asObj->SetHasButtonHandlers(has);
|
||
}
|
||
|
||
bool GFxSprite::PointTestLocal(const GPointF &pt, UInt8 hitTestMask) const
|
||
{
|
||
if (IsHitTestDisableFlagSet())
|
||
return false;
|
||
|
||
if (!DoesScale9GridExist()
|
||
#ifndef GFC_NO_3D
|
||
&& !Has3D()
|
||
#endif
|
||
&& !GetBounds(GRenderer::Matrix()).Contains(pt))
|
||
return false;
|
||
|
||
if ((hitTestMask & HitTest_IgnoreInvisible) && !GetVisible())
|
||
return false;
|
||
|
||
SPInt i, n = (SPInt)DisplayList.GetCharacterCount();
|
||
|
||
GFxSprite* pmask = GetMask();
|
||
if (pmask)
|
||
{
|
||
if (pmask->IsUsedAsMask() && !pmask->IsUnloaded())
|
||
{
|
||
GRenderer::Matrix matrix;
|
||
matrix.SetInverse(pmask->GetWorldMatrix());
|
||
matrix.Prepend(GetWorldMatrix());
|
||
GPointF p = matrix.Transform(pt);
|
||
|
||
if (!pmask->PointTestLocal(p, hitTestMask))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
GArray<UByte> hitTest;
|
||
CalcDisplayListHitTestMaskArray(&hitTest, pt, hitTestMask & HitTest_TestShape);
|
||
|
||
GRenderer::Matrix m;
|
||
GPointF p = pt;
|
||
|
||
// Go backwards, to check higher objects first.
|
||
for (i = n - 1; i >= 0; i--)
|
||
{
|
||
GFxCharacter* pch = DisplayList.GetCharacter(i);
|
||
|
||
if ((hitTestMask & HitTest_IgnoreInvisible) && !pch->GetVisible())
|
||
continue;
|
||
|
||
if (hitTest.GetSize() && (!hitTest[i] || pch->GetClipDepth()>0))
|
||
continue;
|
||
#ifndef GFC_NO_3D
|
||
if (pch->Is3D(true))
|
||
{
|
||
const GMatrix3D *pPersp = pch->GetPerspective3D();
|
||
const GMatrix3D *pView = pch->GetView3D();
|
||
if (pPersp)
|
||
GetMovieRoot()->ScreenToWorld.SetPerspective(*pPersp);
|
||
if (pView)
|
||
GetMovieRoot()->ScreenToWorld.SetView(*pView);
|
||
GetMovieRoot()->ScreenToWorld.SetWorld(pch->GetWorldMatrix3D());
|
||
GetMovieRoot()->ScreenToWorld.GetWorldPoint(&p);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
m = pch->GetMatrix();
|
||
p = m.TransformByInverse(pt);
|
||
}
|
||
|
||
if (pch->PointTestLocal(p, hitTestMask))
|
||
return true;
|
||
}
|
||
|
||
if (pDrawingAPI)
|
||
{
|
||
if (pDrawingAPI->DefPointTestLocal(pt, hitTestMask & HitTest_TestShape, this))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void GFxSprite::VisitMembers(GASStringContext *psc,
|
||
MemberVisitor *pvisitor,
|
||
UInt visitFlags,
|
||
const GASObjectInterface*) const
|
||
{
|
||
if (visitFlags & VisitMember_ChildClips)
|
||
DisplayList.VisitMembers(pvisitor, visitFlags);
|
||
GFxASCharacter::VisitMembers(psc, pvisitor, visitFlags, this);
|
||
}
|
||
|
||
|
||
// Delete a member field, if not read-only. Return true if deleted.
|
||
bool GFxSprite::DeleteMember(GASStringContext *psc, const GASString& name)
|
||
{
|
||
if (GFxASCharacter::DeleteMember(psc, name))
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
GASMovieClipObject* GFxSprite::GetMovieClipObject()
|
||
{
|
||
if (!ASMovieClipObj)
|
||
ASMovieClipObj = *GHEAP_NEW(pRoot->GetMovieHeap()) GASMovieClipObject(GetGC(), this);
|
||
return ASMovieClipObj;
|
||
}
|
||
|
||
GASObject* GFxSprite::GetASObject ()
|
||
{
|
||
return GetMovieClipObject();
|
||
}
|
||
|
||
GASObject* GFxSprite::GetASObject () const
|
||
{
|
||
return const_cast<GFxSprite*>(this)->GetMovieClipObject();
|
||
}
|
||
|
||
bool GFxSprite::IsTabable() const
|
||
{
|
||
if (!GetVisible()) return false;
|
||
if (!IsTabEnabledFlagDefined())
|
||
{
|
||
GASObject* pproto = Get__proto__();
|
||
if (pproto)
|
||
{
|
||
// check prototype for tabEnabled
|
||
GASValue val;
|
||
if (pproto->GetMemberRaw(ASEnvironment.GetSC(), ASEnvironment.CreateConstString("tabEnabled"), &val))
|
||
{
|
||
if (!val.IsUndefined())
|
||
return val.ToBool(&ASEnvironment);
|
||
}
|
||
}
|
||
return (ActsAsButton() || GetTabIndex() > 0);
|
||
}
|
||
else
|
||
return IsTabEnabledFlagTrue();
|
||
}
|
||
|
||
bool GFxSprite::IsFocusEnabled() const
|
||
{
|
||
if (!FocusEnabled.IsDefined())
|
||
{
|
||
GASObject* pproto = Get__proto__();
|
||
if (pproto)
|
||
{
|
||
// check prototype for focusEnabled
|
||
GASValue val;
|
||
if (pproto->GetMemberRaw(ASEnvironment.GetSC(), ASEnvironment.CreateConstString("focusEnabled"), &val))
|
||
{
|
||
if (!val.IsUndefined())
|
||
return val.ToBool(&ASEnvironment);
|
||
}
|
||
}
|
||
return (ActsAsButton());
|
||
}
|
||
else if (FocusEnabled.IsFalse())
|
||
return (ActsAsButton());
|
||
else
|
||
return FocusEnabled.IsTrue();
|
||
}
|
||
|
||
void GFxSprite::FillTabableArray(FillTabableParams* params)
|
||
{
|
||
GASSERT(params);
|
||
|
||
// if (!(GetFocusGroupMask() & (1 << params->ControllerIdx)))
|
||
// return;
|
||
|
||
UPInt n = DisplayList.GetCharacterCount();
|
||
if (n == 0)
|
||
return;
|
||
|
||
if (!TabChildren.IsDefined())
|
||
{
|
||
if (!params->TabChildrenInProto.IsDefined())
|
||
{
|
||
GASObject* pproto = Get__proto__();
|
||
if (pproto)
|
||
{
|
||
// check prototype for tabChildren
|
||
GASValue val;
|
||
if (pproto->GetMemberRaw(ASEnvironment.GetSC(), ASEnvironment.CreateConstString("tabChildren"), &val))
|
||
{
|
||
if (!val.IsUndefined())
|
||
params->TabChildrenInProto = val.ToBool(&ASEnvironment);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (TabChildren.IsFalse() || params->TabChildrenInProto.IsFalse()) return;
|
||
|
||
GFxCharacter* ch;
|
||
|
||
for (UPInt i = 0; i < n; i++)
|
||
{
|
||
ch = DisplayList.GetCharacter(i);
|
||
if (ch != NULL && ch->IsCharASCharacter() && ch->GetVisible())
|
||
{
|
||
GPtr<GFxASCharacter> asch = ch->CharToASCharacter_Unsafe();
|
||
if (asch->IsTabIndexed() && !params->TabIndexed)
|
||
{
|
||
// the first char with tabIndex: release the current array and
|
||
// start to fill it again according to tabIndex.
|
||
params->Array->Clear();
|
||
params->TabIndexed = true;
|
||
}
|
||
// Append for now; if tabIndexed - later it will be sorted by tabIndex
|
||
// Note, we might add focusEnabled characters as well; we are doing this only for
|
||
// 'moveFocus' extension: for regular focus handling these characters will
|
||
// be ignored, if IsTabable returns false.
|
||
if ((asch->IsTabable() || (params->InclFocusEnabled && asch->IsFocusEnabled())) &&
|
||
(!params->TabIndexed || asch->IsTabIndexed()))
|
||
params->Array->PushBack(asch);
|
||
asch->FillTabableArray(params);
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxSprite::OnGettingKeyboardFocus(UInt)
|
||
{
|
||
if (ActsAsButton() && !GetMovieRoot()->IsDisableFocusRolloverEvent())
|
||
OnButtonEvent(GFxEventId::Event_RollOver);
|
||
}
|
||
|
||
// invoked when focused item is about to lose keyboard focus input (mouse moved, for example)
|
||
bool GFxSprite::OnLosingKeyboardFocus(GFxASCharacter*, UInt controllerIdx, GFxFocusMovedType)
|
||
{
|
||
if (ActsAsButton() && GetMovieRoot()->IsFocusRectShown(controllerIdx) &&
|
||
!GetMovieRoot()->IsDisableFocusRolloverEvent())
|
||
{
|
||
OnButtonEvent(GFxEventId::Event_RollOut);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
void GFxSprite::OnInsertionAsLevel(int level)
|
||
{
|
||
if (level == 0)
|
||
SetFocusRectFlag(); // _focusrect in _root is true by default
|
||
else if (level > 0)
|
||
{
|
||
// looks like levels above 0 inherits _focusrect from _level0. Probably
|
||
// another properties are also inherited, need investigate.
|
||
GFxASCharacter* _level0 = GetLevelMovie(0);
|
||
if (_level0)
|
||
{
|
||
SetFocusRectFlag(_level0->IsFocusRectEnabled());
|
||
}
|
||
}
|
||
AddToPlayList(pRoot);
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
FocusGroupMask = 0;
|
||
--FocusGroupMask; // Set to 0xffff
|
||
}
|
||
|
||
UInt GFxSprite::GetCursorType() const
|
||
{
|
||
if (ActsAsButton())
|
||
{
|
||
const GASEnvironment* penv = GetASEnvironment();
|
||
GASValue val;
|
||
|
||
if (const_cast<GFxSprite*>(this)->GetMemberRaw(penv->GetSC(), penv->GetBuiltin(GASBuiltin_useHandCursor), &val))
|
||
{
|
||
if (val.IsUndefined() || !val.ToBool(penv))
|
||
return GFxASCharacter::GetCursorType();
|
||
}
|
||
return GFxMouseCursorEvent::HAND;
|
||
}
|
||
return GFxASCharacter::GetCursorType();
|
||
}
|
||
|
||
void GFxSprite::SetMask(GFxSprite* pmaskSprite)
|
||
{
|
||
GFxSprite* poldMask = GetMask();
|
||
if (poldMask)
|
||
{
|
||
// remove this movie clip from the mask holder for the old mask
|
||
poldMask->SetMaskOwner(NULL);
|
||
}
|
||
|
||
// the sprite being masked cannot be a mask for another sprite
|
||
GFxSprite* poldOwner = GetMaskOwner();
|
||
if (poldOwner)
|
||
{
|
||
poldOwner->SetMask(NULL);
|
||
}
|
||
|
||
if (pMaskCharacter && !IsUsedAsMask())
|
||
pMaskCharacter->Release();
|
||
pMaskCharacter = pmaskSprite;
|
||
SetUsedAsMask(false);
|
||
// avoiding load-hit-store
|
||
if (pmaskSprite)
|
||
pmaskSprite->AddRef();
|
||
|
||
if (pmaskSprite)
|
||
{
|
||
// setMask method override layers' masking (by zeroing ClipDepth).
|
||
// (Do we need to do same for the mask sprite too? !AB)
|
||
SetClipDepth(0);
|
||
|
||
GFxSprite* poldOwner = pmaskSprite->GetMaskOwner();
|
||
if (poldOwner)
|
||
{
|
||
poldOwner->SetMask(NULL);
|
||
}
|
||
|
||
pmaskSprite->SetMaskOwner(this);
|
||
}
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
GFxSprite* GFxSprite::GetMask() const
|
||
{
|
||
if (pMaskCharacter && !IsUsedAsMask())
|
||
{
|
||
GASSERT(pMaskCharacter->IsUsedAsMask() && pMaskCharacter->pMaskOwner == this);
|
||
return pMaskCharacter;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
void GFxSprite::SetMaskOwner(GFxSprite* pmaskOwner)
|
||
{
|
||
if (GetMask())
|
||
SetMask(NULL);
|
||
|
||
SetUsedAsMask((pmaskOwner != NULL)?true:false);
|
||
pMaskOwner = pmaskOwner;
|
||
}
|
||
|
||
GFxSprite* GFxSprite::GetMaskOwner() const
|
||
{
|
||
if (pMaskOwner && IsUsedAsMask())
|
||
{
|
||
GASSERT(!pMaskOwner->IsUsedAsMask() && pMaskOwner->pMaskCharacter == this);
|
||
return pMaskOwner;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
void GFxSprite::SetScale9Grid(const GFxScale9Grid* gr)
|
||
{
|
||
bool propagate = (gr != 0) != (pScale9Grid != 0);
|
||
if (gr == 0)
|
||
{
|
||
delete pScale9Grid;
|
||
pScale9Grid = 0;
|
||
SetScale9GridExists(false);
|
||
}
|
||
else
|
||
{
|
||
if (pScale9Grid == 0)
|
||
pScale9Grid = GHEAP_NEW(pRoot->GetMovieHeap()) GFxScale9Grid;
|
||
*pScale9Grid = *gr;
|
||
SetScale9GridExists(true);
|
||
}
|
||
if (propagate)
|
||
PropagateScale9GridExists();
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
void GFxSprite::PropagateScale9GridExists()
|
||
{
|
||
bool actualGrid = GetScale9Grid() != 0;
|
||
// Stop cleaning up scale9Grid if actual one exists in the node
|
||
if (!DoesScale9GridExist() && actualGrid)
|
||
return;
|
||
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxCharacter* ch = DisplayList.GetCharacter(i);
|
||
ch->SetScale9GridExists(DoesScale9GridExist() || actualGrid);
|
||
ch->PropagateScale9GridExists();
|
||
}
|
||
}
|
||
|
||
void GFxSprite::PropagateNoAdvanceGlobalFlag()
|
||
{
|
||
bool actualValue = IsNoAdvanceGlobalFlagSet();
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxASCharacter* ch = DisplayList.GetCharacter(i)->CharToASCharacter();
|
||
if (ch)
|
||
{
|
||
ch->SetNoAdvanceGlobalFlag(IsNoAdvanceGlobalFlagSet() || actualValue);
|
||
ch->PropagateNoAdvanceGlobalFlag();
|
||
ch->ModifyOptimizedPlayList(pRoot);
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxSprite::PropagateNoAdvanceLocalFlag()
|
||
{
|
||
bool actualValue = IsNoAdvanceLocalFlagSet();
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxASCharacter* ch = DisplayList.GetCharacter(i)->CharToASCharacter();
|
||
if (ch)
|
||
{
|
||
ch->SetNoAdvanceLocalFlag(IsNoAdvanceLocalFlagSet() || actualValue);
|
||
ch->PropagateNoAdvanceLocalFlag();
|
||
ch->ModifyOptimizedPlayList(pRoot);
|
||
}
|
||
}
|
||
}
|
||
|
||
void GFxSprite::PropagateFocusGroupMask(UInt mask)
|
||
{
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxASCharacter* ch = DisplayList.GetCharacter(i)->CharToASCharacter();
|
||
if (ch)
|
||
{
|
||
ch->FocusGroupMask = (UInt16)mask;
|
||
ch->PropagateFocusGroupMask(mask);
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifndef GFC_NO_3D
|
||
void GFxSprite::UpdateViewAndPerspective()
|
||
{
|
||
GFxCharacter::UpdateViewAndPerspective();
|
||
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxCharacter* ch = DisplayList.GetCharacter(i);
|
||
if (ch)
|
||
{
|
||
ch->UpdateViewAndPerspective();
|
||
}
|
||
}
|
||
}
|
||
|
||
bool GFxSprite::Has3D() const
|
||
{
|
||
if (GFxCharacter::Has3D())
|
||
return true;
|
||
|
||
for (UPInt i = 0, n = DisplayList.GetCharacterCount(); i < n; ++i)
|
||
{
|
||
GFxCharacter* ch = DisplayList.GetCharacter(i);
|
||
if (ch && ch->Has3D())
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
#endif
|
||
|
||
void GFxSprite::SetVisible(bool visible)
|
||
{
|
||
SetVisibleFlag(visible);
|
||
#ifndef GFC_USE_OLD_ADVANCE
|
||
bool noAdvGlob = !visible && pRoot->IsNoInvisibleAdvanceFlagSet();
|
||
if (noAdvGlob != IsNoAdvanceGlobalFlagSet())
|
||
{
|
||
SetNoAdvanceGlobalFlag(noAdvGlob);
|
||
ModifyOptimizedPlayListLocal<GFxSprite>(pRoot);
|
||
GFxASCharacter* pparent = GetParent();
|
||
if (pparent && !pparent->IsNoAdvanceGlobalFlagSet())
|
||
PropagateNoAdvanceGlobalFlag();
|
||
}
|
||
#else
|
||
SetNoAdvanceGlobalFlag(!visible && pRoot->IsNoInvisibleAdvanceFlagSet());
|
||
#endif
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
|
||
// GFx_SpriteGetTarget can return NULL in the case when
|
||
// sprite-related method (such as 'attachMovie', etc) is being
|
||
// called for MovieClip instance without attached sprite, for
|
||
// example:
|
||
// var m = new MovieClip;
|
||
// m.attachMovie(...);
|
||
// Here GFx_SpriteGetTarget wiil return NULL.
|
||
static GFxSprite* GFx_SpriteGetTarget(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = NULL;
|
||
if (fn.ThisPtr == NULL)
|
||
{
|
||
psprite = (GFxSprite*) fn.Env->GetTarget();
|
||
}
|
||
else
|
||
{
|
||
if (fn.ThisPtr->IsSprite())
|
||
psprite = (GFxSprite*) fn.ThisPtr;
|
||
}
|
||
//GASSERT(psprite);
|
||
return psprite;
|
||
}
|
||
|
||
static GFxASCharacter* GFx_CharacterGetTarget(const GASFnCall& fn)
|
||
{
|
||
GFxASCharacter* psprite = NULL;
|
||
if (fn.ThisPtr == NULL)
|
||
{
|
||
psprite = (GFxASCharacter*) fn.Env->GetTarget();
|
||
}
|
||
else
|
||
{
|
||
if (fn.ThisPtr->IsASCharacter())
|
||
psprite = (GFxASCharacter*) fn.ThisPtr;
|
||
}
|
||
//GASSERT(psprite);
|
||
return psprite;
|
||
}
|
||
|
||
//
|
||
// *** Sprite Built-In ActionScript methods
|
||
//
|
||
|
||
static void GFx_SpritePlay(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
psprite->SetPlayState(GFxMovie::Playing);
|
||
}
|
||
|
||
static void GFx_SpriteStop(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
psprite->SetPlayState(GFxMovie::Stopped);
|
||
}
|
||
|
||
static void GFx_SpriteGotoAndPlay(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs < 1)
|
||
{
|
||
psprite->LogScriptError("Error: %s, GFx_SpriteGotoAndPlay needs one arg\n", psprite->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
return;
|
||
}
|
||
|
||
const GASValue& arg = fn.Arg(0);
|
||
UInt targetFrame = GFC_MAX_UINT;
|
||
if (arg.GetType() == GASValue::STRING)
|
||
{ // Frame label or string frame number
|
||
GASString sa0(arg.ToString(fn.Env));
|
||
if (!psprite->GetLabeledFrame(sa0.ToCStr(), &targetFrame))
|
||
return;
|
||
}
|
||
else
|
||
{ // Convert to 0-based
|
||
targetFrame = arg.ToUInt32(fn.Env) - 1;
|
||
}
|
||
|
||
psprite->GotoFrame(targetFrame);
|
||
psprite->SetPlayState(GFxMovie::Playing);
|
||
}
|
||
|
||
static void GFx_SpriteGotoAndStop(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs < 1)
|
||
{
|
||
psprite->LogScriptError("Error: %s, GFx_SpriteGotoAndPlay needs one arg\n", psprite->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
return;
|
||
}
|
||
|
||
const GASValue& arg = fn.Arg(0);
|
||
UInt targetFrame = GFC_MAX_UINT;
|
||
if (arg.GetType() == GASValue::STRING)
|
||
{ // Frame label or string frame number
|
||
GASString sa0(arg.ToString(fn.Env));
|
||
if (!psprite->GetLabeledFrame(sa0.ToCStr(), &targetFrame))
|
||
return;
|
||
}
|
||
else
|
||
{ // Convert to 0-based
|
||
targetFrame = arg.ToUInt32(fn.Env) - 1;
|
||
}
|
||
|
||
psprite->GotoFrame(targetFrame);
|
||
psprite->SetPlayState(GFxMovie::Stopped);
|
||
}
|
||
|
||
static void GFx_SpriteNextFrame(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
int availableFrameCount = psprite->GetLoadingFrame();
|
||
int currentFrame = psprite->GetCurrentFrame();
|
||
if (currentFrame < availableFrameCount)
|
||
psprite->GotoFrame(currentFrame + 1);
|
||
psprite->SetPlayState(GFxMovie::Stopped);
|
||
}
|
||
|
||
static void GFx_SpritePrevFrame(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
int currentFrame = psprite->GetCurrentFrame();
|
||
if (currentFrame > 0)
|
||
psprite->GotoFrame(currentFrame - 1);
|
||
psprite->SetPlayState(GFxMovie::Stopped);
|
||
}
|
||
|
||
|
||
static void GFx_SpriteGetBytesLoaded(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Value of getBytesLoaded must always match the frames loaded,
|
||
// because many Flash files rely on it. In particular, if this value
|
||
// is equal to getTotalBytes, they assume that all frames must be
|
||
// available. If that does not hold, many pre-loader algorithms
|
||
// will end up in infinite AS loops.
|
||
|
||
fn.Result->SetInt(psprite->GetBytesLoaded());
|
||
}
|
||
|
||
static void GFx_SpriteGetBytesTotal(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
fn.Result->SetInt(psprite->GetResourceMovieDef()->GetFileBytes());
|
||
}
|
||
|
||
|
||
// Duplicate / remove clip.
|
||
static void GFx_SpriteDuplicateMovieClip(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 2) return;
|
||
|
||
GPtr<GFxASCharacter> newCh = psprite->CloneDisplayObject(
|
||
fn.Arg(0).ToString(fn.Env), ((SInt)fn.Arg(1).ToNumber(fn.Env)) + 16384,
|
||
(fn.NArgs == 3) ? fn.Arg(2).ToObjectInterface(fn.Env) : 0);
|
||
|
||
//!AB: duplicateMovieClip in Flash 6 and above should return newly created clip
|
||
if (psprite->GetVersion() >= 6)
|
||
{
|
||
fn.Result->SetAsCharacter(newCh.GetPtr());
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteAttachMovie(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 3) return;
|
||
|
||
// Get resource id, looked up locally based on
|
||
GASString sa0(fn.Arg(0).ToString(fn.Env));
|
||
|
||
DIAG_CONTEXT_MESSAGE("AS CFn: %s.attachMovie(%s)", psprite->GetCharacterHandle()->GetNamePath().ToCStr(), sa0.ToCStr());
|
||
|
||
GFxResourceBindData resBindData;
|
||
if (!psprite->GetMovieRoot()->FindExportedResource(psprite->GetResourceMovieDef(), &resBindData, sa0.ToCStr()))
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachMovie() failed - export name \"%s\" is not found.\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr(), sa0.ToCStr());
|
||
return;
|
||
}
|
||
|
||
GASSERT(resBindData.pResource.GetPtr() != 0); // MA TBD: Could this be true?
|
||
if (!(resBindData.pResource->GetResourceType() & GFxResource::RT_CharacterDef_Bit))
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachMovie() failed - \"%s\" is not a movieclip.\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr(), sa0.ToCStr());
|
||
return;
|
||
}
|
||
|
||
GFxCharacterCreateInfo ccinfo;
|
||
ccinfo.pCharDef = (GFxCharacterDef*) resBindData.pResource.GetPtr();
|
||
ccinfo.pBindDefImpl = resBindData.pBinding->GetOwnerDefImpl();
|
||
|
||
// Create a new object and add it.
|
||
GFxCharPosInfo pos( ccinfo.pCharDef->GetId(), ((SInt)fn.Arg(2).ToNumber(fn.Env)) + 16384,
|
||
1, GRenderer::Cxform::Identity, 1, GRenderer::Matrix::Identity);
|
||
// Bounds check depth.
|
||
if ((pos.Depth < 0) || (pos.Depth > (2130690045 + 16384)))
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachMovie(\"%s\") failed - depth (%d) must be >= 0\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr(), sa0.ToCStr(), pos.Depth);
|
||
return;
|
||
}
|
||
|
||
// We pass pchDef to make sure that symbols from nested imports use
|
||
// the right definition scope, which might be different from that of psprite.
|
||
GPtr<GFxCharacter> newCh = psprite->AddDisplayObject(
|
||
pos, fn.Arg(1).ToString(fn.Env), 0,
|
||
(fn.NArgs == 4) ? fn.Arg(3).ToObjectInterface(fn.Env) : 0, GFC_MAX_UINT,
|
||
GFxDisplayList::Flags_ReplaceIfDepthIsOccupied, &ccinfo);
|
||
if (newCh)
|
||
{
|
||
newCh->SetAcceptAnimMoves(false);
|
||
|
||
//!AB: attachMovie in Flash 6 and above should return newly created clip
|
||
if (psprite->GetVersion() >= 6)
|
||
{
|
||
GFxASCharacter* pspriteCh = newCh->CharToASCharacter();
|
||
GASSERT (pspriteCh != 0);
|
||
fn.Result->SetAsCharacter(pspriteCh);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if !defined(GFC_NO_SOUND) && !defined(GFC_NO_VIDEO)
|
||
|
||
static void GFx_SpriteAttachAudio(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (!psprite)
|
||
return;
|
||
|
||
if (fn.NArgs < 1)
|
||
{
|
||
fn.Env->LogScriptError("Error: %s.attachAudio() needs one Argument\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
return;
|
||
}
|
||
GASObject* o = fn.Arg(0).ToObject(fn.Env);
|
||
if (o)
|
||
{
|
||
if (o->GetObjectType() == GASObject::Object_NetStream)
|
||
{
|
||
GFxVideoBase* pvideoState = fn.Env->GetMovieRoot()->GetVideo();
|
||
if (pvideoState)
|
||
pvideoState->AttachAudio(o, psprite);
|
||
}
|
||
/*
|
||
else if (o->GetObjectType() == GASObject::Object_Microphone)
|
||
{
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
#endif // GFC_NO_VIDEO && GFX_NO_SOUND
|
||
|
||
static void GFx_SpriteAttachBitmap(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
#ifndef GFC_NO_FXPLAYER_AS_BITMAPDATA
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 2)
|
||
return;
|
||
if (psprite->GetVersion() < 8) // supported only in Flash 8
|
||
return;
|
||
|
||
// Arg(0) - BitmapData
|
||
// Arg(1) - depth
|
||
// Arg(2) - String, pixelSnapping, optional (auto, always, never)
|
||
// Arg(3) - Boolean, smoothing, optional
|
||
|
||
GPtr<GASObject> pobj = fn.Arg(0).ToObject(fn.Env);
|
||
if (!pobj || pobj->GetObjectType() != GASObject::Object_BitmapData)
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachBitmap() failed - the argument is not a BitmapData.\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
return;
|
||
}
|
||
|
||
GASBitmapData* pbmpData = static_cast<GASBitmapData*>(pobj.GetPtr());
|
||
GFxImageResource* pimageRes = pbmpData->GetImage();
|
||
if (!pimageRes)
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachBitmap() failed - no image set in BitmapData.\n",
|
||
psprite->GetName().ToCStr());
|
||
return;
|
||
}
|
||
|
||
// Create position info
|
||
GFxCharPosInfo pos = GFxCharPosInfo(GFxResourceId(GFxCharacterDef::CharId_ImageMovieDef_ShapeDef),
|
||
((SInt)fn.Arg(1).ToNumber(fn.Env)) + 16384,
|
||
1, GRenderer::Cxform::Identity, 1, GRenderer::Matrix::Identity);
|
||
// Bounds check depth.
|
||
if ((pos.Depth < 0) || (pos.Depth > (2130690045 + 16384)))
|
||
{
|
||
psprite->LogScriptWarning("Error: %s.attachBitmap() failed - depth (%d) must be >= 0\n",
|
||
psprite->GetCharacterHandle()->GetNamePath().ToCStr(), pos.Depth);
|
||
return;
|
||
}
|
||
|
||
|
||
bool smoothing = (fn.NArgs >= 4) ? fn.Arg(3).ToBool(fn.Env) : false;
|
||
GFxMovieRoot* pmroot = fn.Env->GetMovieRoot();
|
||
GPtr<GFxMovieDefImpl> pimageMovieDef = *pmroot->CreateImageMovieDef(pimageRes, smoothing, "");
|
||
|
||
if (pimageMovieDef)
|
||
{
|
||
// Empty sprite based on new Def. New Def gives correct Version, URL, and symbol lookup behavior.
|
||
GPtr<GFxSprite> pchildSprite = *GHEAP_NEW(pmroot->GetMovieHeap())
|
||
GFxSprite(pimageMovieDef->GetDataDef(), pimageMovieDef, pmroot, psprite,
|
||
GFxResourceId(GFxCharacterDef::CharId_EmptyMovieClip), true);
|
||
|
||
if (pchildSprite)
|
||
{
|
||
|
||
// Verified: Flash assigns depth -16383 to image shapes (depth value = 1 in our list).
|
||
SInt depth = 1;
|
||
GFxCharPosInfo locpos = GFxCharPosInfo(GFxResourceId(GFxCharacterDef::CharId_ImageMovieDef_ShapeDef),
|
||
depth, 0, GRenderer::Cxform(), 1, GRenderer::Matrix());
|
||
|
||
GASString emptyName(fn.Env->GetBuiltin(GASBuiltin_empty_));
|
||
|
||
pchildSprite->AddToPlayList(pmroot);
|
||
pchildSprite->ModifyOptimizedPlayList(pmroot);
|
||
|
||
pchildSprite->AddDisplayObject(locpos, emptyName, NULL, NULL, 1);
|
||
psprite->ReplaceDisplayObject(pos, pchildSprite, emptyName);
|
||
|
||
psprite->SetAcceptAnimMoves(false);
|
||
}
|
||
}
|
||
#else
|
||
GFC_DEBUG_WARNING(1, "Error: attachBitmap is failed since GFC_NO_FXPLAYER_AS_BITMAPDATA is defined.");
|
||
#endif
|
||
}
|
||
|
||
static void GFx_SpriteCreateEmptyMovieClip(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 2) return;
|
||
|
||
// Create a new object and add it.
|
||
GFxCharPosInfo pos = GFxCharPosInfo( GFxResourceId(GFxCharacterDef::CharId_EmptyMovieClip),
|
||
((SInt)fn.Arg(1).ToNumber(fn.Env)) + 16384,
|
||
1, GRenderer::Cxform::Identity, 1, GRenderer::Matrix::Identity);
|
||
// Bounds check depth.
|
||
if ((pos.Depth < 0) || (pos.Depth > (2130690045 + 16384)))
|
||
return;
|
||
|
||
GPtr<GFxCharacter> newCh = psprite->AddDisplayObject(
|
||
pos, fn.Arg(0).ToString(fn.Env), NULL, NULL, GFC_MAX_UINT,
|
||
GFxDisplayList::Flags_ReplaceIfDepthIsOccupied);
|
||
if (newCh)
|
||
{
|
||
newCh->SetAcceptAnimMoves(false);
|
||
|
||
// Return newly created clip.
|
||
GFxASCharacter* pspriteCh = newCh->CharToASCharacter();
|
||
GASSERT (pspriteCh != 0);
|
||
fn.Result->SetAsCharacter(pspriteCh);
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteCreateTextField(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 6) return; //?AB, could it work with less than 6 params specified?
|
||
|
||
// Create a new object and add it.
|
||
GFxCharPosInfo pos = GFxCharPosInfo( GFxResourceId(GFxCharacterDef::CharId_EmptyTextField),
|
||
((SInt)fn.Arg(1).ToNumber(fn.Env)) + 16384,
|
||
1, GRenderer::Cxform::Identity, 1, GRenderer::Matrix::Identity);
|
||
// Bounds check depth.
|
||
if ((pos.Depth < 0) || (pos.Depth > (2130690045 + 16384)))
|
||
return;
|
||
|
||
GPtr<GFxCharacter> newCh = psprite->AddDisplayObject(
|
||
pos, fn.Arg(0).ToString(fn.Env), NULL, NULL, GFC_MAX_UINT,
|
||
GFxDisplayList::Flags_ReplaceIfDepthIsOccupied);
|
||
if (newCh)
|
||
{
|
||
newCh->SetAcceptAnimMoves(false);
|
||
|
||
// Return newly created clip.
|
||
GFxASCharacter* ptextCh = newCh->CharToASCharacter();
|
||
GASSERT (ptextCh != 0);
|
||
ptextCh->SetStandardMember(GFxSprite::M_x, fn.Arg(2), false);
|
||
ptextCh->SetStandardMember(GFxSprite::M_y, fn.Arg(3), false);
|
||
ptextCh->SetStandardMember(GFxSprite::M_width, fn.Arg(4), false);
|
||
ptextCh->SetStandardMember(GFxSprite::M_height, fn.Arg(5), false);
|
||
fn.Result->SetAsCharacter(ptextCh);
|
||
}
|
||
}
|
||
|
||
|
||
static void GFx_SpriteRemoveMovieClip(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (psprite->GetDepth() < 16384)
|
||
{
|
||
psprite->LogScriptWarning("%s.removeMovieClip() failed - depth must be >= 0\n",
|
||
psprite->GetName().ToCStr());
|
||
return;
|
||
}
|
||
psprite->RemoveDisplayObject();
|
||
}
|
||
|
||
// Implementation of MovieClip.setMask method.
|
||
static void GFx_SpriteSetMask(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs >= 1)
|
||
{
|
||
if (fn.Arg(0).IsNull())
|
||
{
|
||
// remove mask
|
||
psprite->SetMask(NULL);
|
||
}
|
||
else
|
||
{
|
||
GFxASCharacter* pobj = fn.Arg(0).ToASCharacter(fn.Env);
|
||
if (pobj)
|
||
{
|
||
GFxSprite* pmaskSpr = pobj->ToSprite();
|
||
psprite->SetMask(pmaskSpr);
|
||
}
|
||
else
|
||
psprite->SetMask(NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteGetBounds(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Get target sprite.
|
||
GFxASCharacter* pobj = (fn.NArgs > 0) ? fn.Arg(0).ToASCharacter(fn.Env) : psprite;
|
||
GFxSprite* ptarget = pobj ? pobj->ToSprite() : 0;
|
||
|
||
GRectF b(0);
|
||
GRenderer::Matrix matrix;
|
||
|
||
if (ptarget)
|
||
{
|
||
// Transform first by sprite's matrix, then by inverse of target.
|
||
if (ptarget != psprite) // Optimize identity case.
|
||
{
|
||
matrix.SetInverse(ptarget->GetWorldMatrix());
|
||
matrix.Prepend(psprite->GetWorldMatrix());
|
||
}
|
||
|
||
// A "perfect" implementation would be { b = psprite->GetBounds(matrix); },
|
||
// however, Flash always gets the local bounding box before a transform,
|
||
// as follows.
|
||
matrix.EncloseTransform(&b, psprite->GetBounds(GRenderer::Matrix()));
|
||
}
|
||
|
||
// Store into result object.
|
||
GPtr<GASObject> presult = *GHEAP_NEW(fn.Env->GetHeap()) GASObject(fn.Env);
|
||
GASStringContext* psc = fn.Env->GetSC();
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_xMin), TwipsToPixels(Double(b.Left)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_xMax), TwipsToPixels(Double(b.Right)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_yMin), TwipsToPixels(Double(b.Top)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_yMax), TwipsToPixels(Double(b.Bottom)));
|
||
|
||
fn.Result->SetAsObject(presult.GetPtr());
|
||
}
|
||
|
||
|
||
//
|
||
// MovieClip.getRect()
|
||
//
|
||
// Returns properties that are the minimum and maximum x and y
|
||
// coordinate values of the movie clip, based on the bounds parameter,
|
||
// excluding any strokes on shapes. The values that getRect() returns
|
||
// are the same or smaller than those returned by MovieClip.getBounds().
|
||
//
|
||
static void GFx_SpriteGetRect(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Get target sprite.
|
||
GFxASCharacter* pobj = (fn.NArgs > 0) ? fn.Arg(0).ToASCharacter(fn.Env) : psprite;
|
||
GFxSprite* ptarget = pobj ? pobj->ToSprite() : 0;
|
||
|
||
GRectF b(0);
|
||
GRenderer::Matrix matrix;
|
||
|
||
|
||
|
||
if (ptarget)
|
||
{
|
||
// Transform first by sprite's matrix, then by inverse of target.
|
||
if (ptarget != psprite) // Optimize identity case.
|
||
{
|
||
matrix.SetInverse(ptarget->GetWorldMatrix());
|
||
matrix.Prepend(psprite->GetWorldMatrix());
|
||
}
|
||
|
||
// A "perfect" implementation would be { b = psprite->GetBounds(matrix); },
|
||
// however, Flash always gets the local bounding box before a transform,
|
||
// as follows.
|
||
matrix.EncloseTransform(&b, psprite->GetRectBounds(GRenderer::Matrix()));
|
||
}
|
||
|
||
// Store into result object.
|
||
GPtr<GASObject> presult = *GHEAP_NEW(fn.Env->GetHeap()) GASObject(fn.Env);
|
||
GASStringContext* psc = fn.Env->GetSC();
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_xMin), TwipsToPixels(Double(b.Left)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_xMax), TwipsToPixels(Double(b.Right)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_yMin), TwipsToPixels(Double(b.Top)));
|
||
presult->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_yMax), TwipsToPixels(Double(b.Bottom)));
|
||
|
||
fn.Result->SetAsObject(presult.GetPtr());
|
||
}
|
||
|
||
|
||
static void GFx_SpriteLocalToGlobal(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 1) return;
|
||
|
||
// Get target object.
|
||
GASStringContext* psc = fn.Env->GetSC();
|
||
GASObjectInterface* pobj = fn.Arg(0).ToObjectInterface(fn.Env);
|
||
if (!pobj) return;
|
||
|
||
// Object must have x & y members, which are numbers; otherwise
|
||
// the function does nothing. This is the expected behavior.
|
||
GASValue xval, yval;
|
||
pobj->GetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_x), &xval);
|
||
pobj->GetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_y), &yval);
|
||
if (!xval.IsNumber() || !yval.IsNumber())
|
||
return;
|
||
|
||
// Compute transformation and set members.
|
||
GPointF pt((Float)GFC_PIXELS_TO_TWIPS(xval.ToNumber(fn.Env)), (Float)GFC_PIXELS_TO_TWIPS(yval.ToNumber(fn.Env)));
|
||
pt = psprite->GetWorldMatrix().Transform(pt);
|
||
pobj->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_x), TwipsToPixels(Double(pt.x)));
|
||
pobj->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_y), TwipsToPixels(Double(pt.y)));
|
||
}
|
||
|
||
static void GFx_SpriteGlobalToLocal(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL || fn.NArgs < 1) return;
|
||
|
||
// Get target object.
|
||
GASStringContext* psc = fn.Env->GetSC();
|
||
GASObjectInterface* pobj = fn.Arg(0).ToObjectInterface(fn.Env);
|
||
if (!pobj) return;
|
||
|
||
// Object must have x & y members, which are numbers; otherwise
|
||
// the function does nothing. This is the expected behavior.
|
||
GASValue xval, yval;
|
||
pobj->GetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_x), &xval);
|
||
pobj->GetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_y), &yval);
|
||
if (!xval.IsNumber() || !yval.IsNumber())
|
||
return;
|
||
|
||
// Compute transformation and set members.
|
||
GPointF pt((Float)GFC_PIXELS_TO_TWIPS(xval.ToNumber(fn.Env)), (Float)GFC_PIXELS_TO_TWIPS(yval.ToNumber(fn.Env)));
|
||
pt = psprite->GetWorldMatrix().TransformByInverse(pt);
|
||
pobj->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_x), TwipsToPixels(Double(pt.x)));
|
||
pobj->SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_y), TwipsToPixels(Double(pt.y)));
|
||
}
|
||
|
||
|
||
// Hit-test implementation.
|
||
static void GFx_SpriteHitTest(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
fn.Result->SetBool(0);
|
||
|
||
GRectF spriteLocalBounds = psprite->GetBounds(GRenderer::Matrix());
|
||
if (spriteLocalBounds.IsNull())
|
||
return;
|
||
|
||
// Hit-test has either 1, 2 or 3 arguments.
|
||
if (fn.NArgs >= 2)
|
||
{
|
||
UInt8 hitTestMask = 0;
|
||
// x, y, shapeFlag version of hitTest.
|
||
GPointF pt( (Float)GFC_PIXELS_TO_TWIPS(fn.Arg(0).ToNumber(fn.Env)),
|
||
(Float)GFC_PIXELS_TO_TWIPS(fn.Arg(1).ToNumber(fn.Env)) );
|
||
if (fn.NArgs >= 3) // optional parameter shapeFlag
|
||
hitTestMask |= (fn.Arg(2).ToBool(fn.Env)) ? GFxCharacter::HitTest_TestShape : 0;
|
||
if (fn.NArgs >= 4) // optional parameter shapeFlag
|
||
hitTestMask |= (fn.Arg(3).ToBool(fn.Env)) ? GFxCharacter::HitTest_IgnoreInvisible : 0;
|
||
|
||
// check for 3D case
|
||
GFxMovieRoot *proot = psprite->GetMovieRoot();
|
||
#ifndef GFC_NO_3D
|
||
if (psprite->Is3D(true) && proot)
|
||
{
|
||
float w = (proot->VisibleFrameRect.Right - proot->VisibleFrameRect.Left);
|
||
float h = (proot->VisibleFrameRect.Bottom - proot->VisibleFrameRect.Top);
|
||
|
||
// note pt.x and pt.y already have ViewScaleXY taken into account (but not ViewOffsetXY)
|
||
float nsx = 2.f * ((pt.x - PixelsToTwips(proot->ViewOffsetX)) / w - 0.5f);
|
||
float nsy = 2.f * ((pt.y - PixelsToTwips(proot->ViewOffsetY)) / h - 0.5f);
|
||
|
||
proot->ScreenToWorld.SetNormalizedScreenCoords(nsx, nsy); // -1 to 1
|
||
// proot->GetLog()->LogMessage("nsx=%.2f, nsy=%.2f\n", nsx, nsy);
|
||
|
||
const GMatrix3D *pPersp = psprite->GetPerspective3D(true);
|
||
const GMatrix3D *pView = psprite->GetView3D(true);
|
||
if (pPersp)
|
||
proot->ScreenToWorld.SetPerspective(*pPersp);
|
||
if (pView)
|
||
proot->ScreenToWorld.SetView(*pView);
|
||
proot->ScreenToWorld.SetWorld(psprite->GetWorldMatrix3D());
|
||
GPointF pt2;
|
||
proot->ScreenToWorld.GetWorldPoint(&pt2);
|
||
|
||
fn.Result->SetBool(psprite->PointTestLocal (pt2, hitTestMask));
|
||
return;
|
||
}
|
||
#endif
|
||
GPointF ptSpr = psprite->GetLevelMatrix().TransformByInverse(pt);
|
||
// pt is already in root's coordinates!
|
||
|
||
if (psprite->DoesScale9GridExist())
|
||
{
|
||
fn.Result->SetBool(psprite->PointTestLocal (ptSpr, hitTestMask));
|
||
}
|
||
else
|
||
{
|
||
if (spriteLocalBounds.Contains(ptSpr))
|
||
{
|
||
if (!(hitTestMask & GFxCharacter::HitTest_TestShape))
|
||
fn.Result->SetBool(true);
|
||
else
|
||
fn.Result->SetBool(psprite->PointTestLocal (ptSpr, hitTestMask));
|
||
}
|
||
else
|
||
fn.Result->SetBool(false);
|
||
}
|
||
}
|
||
else if (fn.NArgs == 1)
|
||
{
|
||
// Target sprite version of hit-test.
|
||
const GASValue& arg = fn.Arg(0);
|
||
GFxASCharacter* ptarget = NULL;
|
||
if (arg.IsCharacter())
|
||
{
|
||
ptarget = arg.ToASCharacter(fn.Env);
|
||
}
|
||
else
|
||
{
|
||
// if argument is not a character, then convert it to string
|
||
// and try to resolve as variable (AB)
|
||
GASString varName = arg.ToString(fn.Env);
|
||
GASValue result;
|
||
if (fn.Env->GetVariable(varName, &result))
|
||
{
|
||
ptarget = result.ToASCharacter(fn.Env);
|
||
}
|
||
}
|
||
if (!ptarget)
|
||
return;
|
||
|
||
GRectF targetLocalRect = ptarget->GetBounds(GRenderer::Matrix());
|
||
if (targetLocalRect.IsNull())
|
||
return;
|
||
// Rect rectangles in same coordinate space & check intersection.
|
||
|
||
// note - this only checks axis aligned rects, which may not be so good for 3D
|
||
GRectF spriteWorldRect, targetWorldRect;
|
||
#ifndef GFC_NO_3D
|
||
if (psprite->Is3D(true))
|
||
spriteWorldRect = psprite->GetWorldMatrix3D().EncloseTransform(spriteLocalBounds);
|
||
else
|
||
#endif
|
||
spriteWorldRect = psprite->GetWorldMatrix().EncloseTransform(spriteLocalBounds);
|
||
#ifndef GFC_NO_3D
|
||
if (ptarget->Is3D(true))
|
||
targetWorldRect = ptarget->GetWorldMatrix3D().EncloseTransform(targetLocalRect);
|
||
else
|
||
#endif
|
||
targetWorldRect = ptarget->GetWorldMatrix().EncloseTransform(targetLocalRect);
|
||
|
||
fn.Result->SetBool(spriteWorldRect.Intersects(targetWorldRect));
|
||
}
|
||
}
|
||
|
||
// Implemented as member function so that it can access Sprite's private methods.
|
||
void GFxSprite::SpriteSwapDepths(const GASFnCall& fn)
|
||
{
|
||
GFxASCharacter* pchar = GFx_CharacterGetTarget(fn);
|
||
if (pchar == NULL || fn.NArgs < 1) return;
|
||
|
||
GFxSprite* pparent = (GFxSprite*)pchar->GetParent();
|
||
const GASValue& arg = fn.Arg(0);
|
||
int depth2 = 0;
|
||
GFxASCharacter* ptarget = 0;
|
||
GFxSprite* psprite = (pchar->IsSprite()) ? (GFxSprite*)pchar : 0;
|
||
|
||
// Depth can be a number at which the character is inserted.
|
||
if (arg.IsNumber())
|
||
{
|
||
depth2 = ((int)arg.ToNumber(fn.Env)) + 16384;
|
||
// Verified: Flash will discard smaller values.
|
||
if (depth2 < 0)
|
||
return;
|
||
// Documented depth range is -16384 to 1048575,
|
||
// but Flash does not seem to enforce that upper constraint. Instead
|
||
// manually determined upper constraint is 2130690045.
|
||
if (depth2 > (2130690045 + 16384))
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// Need to search for target in our scope (environment scope might be different).
|
||
if (psprite)
|
||
{
|
||
GFxASCharacter* poldTarget = fn.Env->GetTarget();
|
||
fn.Env->SetTarget(psprite);
|
||
ptarget = fn.Env->FindTargetByValue(arg);
|
||
fn.Env->SetTarget(poldTarget);
|
||
}
|
||
else
|
||
ptarget = fn.Env->FindTargetByValue(arg); //???
|
||
|
||
if (!ptarget || ptarget == pchar)
|
||
return;
|
||
// Must have same parent.
|
||
if (pparent != ptarget->GetParent())
|
||
return;
|
||
|
||
// Can we swap with text fields, etc? Yes.
|
||
depth2 = ptarget->GetDepth();
|
||
}
|
||
|
||
// Flash doesn't allow to swap depths for "semi-dead" objects (i.e
|
||
// depth_in_flash < -16384, depth_in_gfx < -1).
|
||
if (pchar->GetDepth() < 0)
|
||
return;
|
||
|
||
pchar->SetAcceptAnimMoves(0);
|
||
// Do swap or depth change.
|
||
if (pparent && pparent->DisplayList.SwapDepths(pchar->GetDepth(), depth2, pparent->GetCurrentFrame()))
|
||
{
|
||
pparent->SetDirtyFlag();
|
||
if (ptarget)
|
||
ptarget->SetAcceptAnimMoves(0);
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteGetNextHighestDepth(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Depths is always at least 0. No upper bound constraint is done
|
||
// (i.e. 2130690046 will be returned in Flash, although invalid).
|
||
SInt depth = G_Max<SInt>(0, psprite->GetLargestDepthInUse() - 16384 + 1);
|
||
fn.Result->SetInt(depth);
|
||
}
|
||
|
||
static void GFx_SpriteGetInstanceAtDepth(const GASFnCall& fn)
|
||
{
|
||
fn.Result->SetUndefined();
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Note: this can also report buttons, etc.
|
||
if (fn.NArgs >= 1)
|
||
{
|
||
GFxCharacter* pchar = psprite->GetCharacterAtDepth(((int)fn.Arg(0).ToNumber(fn.Env)) + 16384);
|
||
if (pchar)
|
||
fn.Result->SetAsCharacter(pchar->CharToASCharacter());
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Returns a TextSnapshot object that contains the text in all the
|
||
// static text fields in the specified movie clip; text in child movie
|
||
// clips is not included. This method always returns a TextSnapshot
|
||
// object.
|
||
//
|
||
static void GFx_SpriteGetTextSnapshot(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_TEXTSNAPSHOT
|
||
GPtr<GASTextSnapshotObject> ptextSnapshot = *GHEAP_NEW(fn.Env->GetHeap()) GASTextSnapshotObject(fn.Env);
|
||
// Collect all static text chars
|
||
ptextSnapshot->Process(psprite);
|
||
// Return a TextSnapshot AS Object
|
||
fn.Result->SetAsObject(ptextSnapshot);
|
||
#endif
|
||
}
|
||
|
||
|
||
static void GFx_SpriteGetSWFVersion(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
fn.Result->SetInt(psprite->GetVersion());
|
||
}
|
||
|
||
static void GFx_SpriteStartDrag(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
GFxMovieRoot::DragState st;
|
||
bool lockCenter = false;
|
||
|
||
// If there are arguments, init appropriate values.
|
||
if (fn.NArgs > 0)
|
||
{
|
||
lockCenter = fn.Arg(0).ToBool(fn.Env);
|
||
if (fn.NArgs > 4)
|
||
{
|
||
st.Bound = true;
|
||
GRectF bounds;
|
||
bounds.Left = GFC_PIXELS_TO_TWIPS((Float) fn.Arg(1).ToNumber(fn.Env));
|
||
bounds.Top = GFC_PIXELS_TO_TWIPS((Float) fn.Arg(2).ToNumber(fn.Env));
|
||
bounds.Right = GFC_PIXELS_TO_TWIPS((Float) fn.Arg(3).ToNumber(fn.Env));
|
||
bounds.Bottom = GFC_PIXELS_TO_TWIPS((Float) fn.Arg(4).ToNumber(fn.Env));
|
||
bounds.Normalize();
|
||
st.BoundLT = bounds.TopLeft();
|
||
st.BoundRB = bounds.BottomRight();
|
||
}
|
||
}
|
||
|
||
st.pCharacter = psprite;
|
||
// Init mouse offsets based on LockCenter flag.
|
||
st.InitCenterDelta(lockCenter);
|
||
|
||
// Begin dragging.
|
||
GFxMovieRoot* proot = psprite->GetMovieRoot();
|
||
proot->SetDragState(st);
|
||
psprite->ModifyOptimizedPlayListLocal<GFxSprite>(proot);
|
||
}
|
||
|
||
|
||
static void GFx_SpriteStopDrag(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Stop dragging.
|
||
GFxMovieRoot* proot = psprite->GetMovieRoot();
|
||
proot->StopDrag();
|
||
psprite->ModifyOptimizedPlayListLocal<GFxSprite>(proot);
|
||
}
|
||
|
||
static void GFx_SpriteLoadMovie(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs > 0)
|
||
{
|
||
GFxLoadQueueEntry::LoadMethod lm = GFxLoadQueueEntry::LM_None;
|
||
|
||
// Decode load method argument.
|
||
if (fn.NArgs > 1)
|
||
{
|
||
GASString str(fn.Arg(1).ToString(fn.Env).ToLower());
|
||
if (str == "get")
|
||
lm = GFxLoadQueueEntry::LM_Get;
|
||
else if (str == "post")
|
||
lm = GFxLoadQueueEntry::LM_Post;
|
||
}
|
||
|
||
// Post loadMovie into queue.
|
||
GASString urlStr(fn.Arg(0).ToString(fn.Env));
|
||
psprite->GetMovieRoot()->AddLoadQueueEntry(psprite, urlStr.ToCStr(), lm);
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteUnloadMovie(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
// Post unloadMovie into queue (empty url == unload).
|
||
psprite->GetMovieRoot()->AddLoadQueueEntry(psprite, "");
|
||
}
|
||
|
||
static void GFx_SpriteLoadVariables(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs > 0)
|
||
{
|
||
GFxLoadQueueEntry::LoadMethod lm = GFxLoadQueueEntry::LM_None;
|
||
|
||
// Decode load method argument.
|
||
if (fn.NArgs > 1)
|
||
{
|
||
GASString str(fn.Arg(1).ToString(fn.Env).ToLower());
|
||
if (str == "get")
|
||
lm = GFxLoadQueueEntry::LM_Get;
|
||
else if (str == "post")
|
||
lm = GFxLoadQueueEntry::LM_Post;
|
||
}
|
||
|
||
// Post loadMovie into queue.
|
||
GASString urlStr(fn.Arg(0).ToString(fn.Env));
|
||
psprite->GetMovieRoot()->AddVarLoadQueueEntry(psprite, urlStr.ToCStr(), lm);
|
||
}
|
||
}
|
||
|
||
// Drawing API support
|
||
//=============================================================================
|
||
void GFxSprite::Clear()
|
||
{
|
||
if(pDrawingAPI)
|
||
{
|
||
pDrawingAPI->Clear();
|
||
}
|
||
InvalidateHitResult();
|
||
SetDirtyFlag();
|
||
}
|
||
|
||
void GFxSprite::SetNoLine()
|
||
{
|
||
if (!pDrawingAPI)
|
||
pDrawingAPI = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxDrawingContext;
|
||
|
||
if (!pDrawingAPI->NoLine())
|
||
{
|
||
AcquirePath(false);
|
||
pDrawingAPI->SetNoLine();
|
||
}
|
||
}
|
||
|
||
void GFxSprite::SetNoFill()
|
||
{
|
||
AcquirePath(true);
|
||
pDrawingAPI->SetNoFill();
|
||
}
|
||
|
||
void GFxSprite::SetLineStyle(Float lineWidth,
|
||
UInt rgba,
|
||
bool hinting,
|
||
GFxLineStyle::LineStyle scaling,
|
||
GFxLineStyle::LineStyle caps,
|
||
GFxLineStyle::LineStyle joins,
|
||
Float miterLimit)
|
||
{
|
||
if (!pDrawingAPI)
|
||
pDrawingAPI = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxDrawingContext;
|
||
|
||
if((rgba & 0xFF000000) != 0)
|
||
{
|
||
// Check the line width and set it to the possible minimum (hairline)
|
||
// if negative or zero.
|
||
if (lineWidth <= 0.0f)
|
||
lineWidth = TwipsToPixels(1.0f);
|
||
|
||
if (!pDrawingAPI->SameLineStyle(PixelsToTwips(lineWidth), rgba, hinting, scaling, caps, joins, miterLimit))
|
||
{
|
||
AcquirePath(false);
|
||
pDrawingAPI->SetLineStyle(PixelsToTwips(lineWidth), rgba, hinting, scaling, caps, joins, miterLimit);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!pDrawingAPI->NoLine())
|
||
{
|
||
AcquirePath(false);
|
||
pDrawingAPI->SetNoLine();
|
||
}
|
||
}
|
||
}
|
||
|
||
// The function begins new fill with an "empty" style.
|
||
// It returns the pointer to just created fill style, so the caller
|
||
// can set any values. The function is used in Action Script, beginGradientFill
|
||
GFxFillStyle* GFxSprite::BeginFill()
|
||
{
|
||
AcquirePath(true);
|
||
return pDrawingAPI->SetNewFill();
|
||
}
|
||
|
||
GFxFillStyle* GFxSprite::CreateLineComplexFill()
|
||
{
|
||
AcquirePath(true);
|
||
return pDrawingAPI->CreateLineComplexFill();
|
||
}
|
||
|
||
void GFxSprite::BeginFill(UInt rgba)
|
||
{
|
||
AcquirePath(true);
|
||
pDrawingAPI->SetFill(rgba);
|
||
}
|
||
|
||
|
||
void GFxSprite::BeginBitmapFill(GFxFillType fillType,
|
||
GFxImageResource* pimageRes,
|
||
const Matrix& mtx)
|
||
{
|
||
AcquirePath(true);
|
||
pDrawingAPI->SetBitmapFill(fillType, pimageRes, mtx);
|
||
}
|
||
|
||
void GFxSprite::EndFill()
|
||
{
|
||
AcquirePath(true);
|
||
pDrawingAPI->ResetFill();
|
||
}
|
||
|
||
void GFxSprite::MoveTo(Float x, Float y)
|
||
{
|
||
AcquirePath(false);
|
||
pDrawingAPI->MoveTo(PixelsToTwips(x),
|
||
PixelsToTwips(y));
|
||
InvalidateHitResult();
|
||
//FILE* fd = fopen("coord", "at");
|
||
//fprintf(fd, "mc.moveTo(%.3f, %.3f);\n", x, y);
|
||
//fclose(fd);
|
||
}
|
||
|
||
void GFxSprite::LineTo(Float x, Float y)
|
||
{
|
||
if (!pDrawingAPI)
|
||
pDrawingAPI = *GHEAP_NEW(pRoot->GetHeap()) GFxDrawingContext;
|
||
pDrawingAPI->LineTo(PixelsToTwips(x),
|
||
PixelsToTwips(y));
|
||
InvalidateHitResult();
|
||
//FILE* fd = fopen("coord", "at");
|
||
//fprintf(fd, "mc.lineTo(%.3f, %.3f);\n", x, y);
|
||
//fclose(fd);
|
||
}
|
||
|
||
void GFxSprite::CurveTo(Float cx, Float cy, Float ax, Float ay)
|
||
{
|
||
if (!pDrawingAPI)
|
||
pDrawingAPI = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxDrawingContext;
|
||
pDrawingAPI->CurveTo(PixelsToTwips(cx),
|
||
PixelsToTwips(cy),
|
||
PixelsToTwips(ax),
|
||
PixelsToTwips(ay));
|
||
InvalidateHitResult();
|
||
}
|
||
|
||
bool GFxSprite::AcquirePath(bool newShapeFlag)
|
||
{
|
||
if (!pDrawingAPI)
|
||
pDrawingAPI = *GHEAP_NEW(pRoot->GetMovieHeap()) GFxDrawingContext;
|
||
SetDirtyFlag();
|
||
InvalidateHitResult();
|
||
return pDrawingAPI->AcquirePath(newShapeFlag);
|
||
}
|
||
|
||
void GFxSprite::SetStateChangeFlags(UInt8 flags)
|
||
{
|
||
GFxASCharacter::SetStateChangeFlags(flags);
|
||
UPInt size = DisplayList.GetCharacterCount();
|
||
for (UPInt i = 0; i < size; ++i)
|
||
{
|
||
GFxCharacter* ch = DisplayList.GetCharacter(i);
|
||
ch->SetStateChangeFlags(flags);
|
||
}
|
||
}
|
||
|
||
void GFxSprite::SetHitArea( GFxSprite* phitArea )
|
||
{
|
||
GFxSprite* phaSprite = GetHitArea();
|
||
if (phaSprite)
|
||
phaSprite->SetHitAreaHolder(0);
|
||
|
||
int haIndex = GetHitAreaIndex(); // Returns -1 if not found
|
||
if (phitArea)
|
||
{
|
||
pHitAreaHandle = phitArea->GetCharacterHandle();
|
||
if (haIndex == -1)
|
||
pRoot->SpritesWithHitArea.PushBack(this);
|
||
phitArea->SetHitAreaHolder(this);
|
||
}
|
||
else
|
||
{
|
||
pHitAreaHandle = 0;
|
||
if (haIndex > -1)
|
||
pRoot->SpritesWithHitArea.RemoveAt(haIndex);
|
||
}
|
||
}
|
||
|
||
GFxSprite* GFxSprite::GetHitArea() const
|
||
{
|
||
if (pHitAreaHandle)
|
||
{
|
||
GFxASCharacter* pasChararcter = pHitAreaHandle->ResolveCharacter(pRoot);
|
||
if (pasChararcter)
|
||
return pasChararcter->ToSprite();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int GFxSprite::GetHitAreaIndex()
|
||
{
|
||
UPInt spriteArraySize = pRoot->SpritesWithHitArea.GetSize();
|
||
if (pHitAreaHandle) // Should not be in array, so don't search
|
||
{
|
||
for (UInt i = 0; i < spriteArraySize; i++)
|
||
if (pRoot->SpritesWithHitArea[i] == this)
|
||
return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
void GFxSprite::SetName(const GASString& name)
|
||
{
|
||
GFxASCharacter::SetName(name);
|
||
// need to reset cached character if name is changed.
|
||
DisplayList.ResetCachedCharacter();
|
||
}
|
||
|
||
// Drawing API
|
||
//=============================================================================
|
||
static void GFx_SpriteClear(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
psprite->Clear();
|
||
}
|
||
|
||
static void GFx_SpriteBeginFill(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if(fn.NArgs > 0) // rgb:Number
|
||
{
|
||
UInt rgba = UInt(fn.Arg(0).ToUInt32(fn.Env) | 0xFF000000);
|
||
if(fn.NArgs > 1) // alpha:Number
|
||
{
|
||
// Alpha is clamped to 0 to 100 range.
|
||
Float alpha = ((Float)fn.Arg(1).ToNumber(fn.Env)) * 255.0f / 100.0f;
|
||
rgba &= 0xFFFFFF;
|
||
rgba |= UInt(G_Clamp(alpha, 0.0f, 255.0f)) << 24;
|
||
}
|
||
psprite->BeginFill(rgba);
|
||
}
|
||
else
|
||
{
|
||
psprite->SetNoFill();
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteCreateGradient(const GASFnCall& fn, GFxFillStyle* fillStyle)
|
||
{
|
||
// beginGradientFill(fillType:String,
|
||
// colors:Array,
|
||
// alphas:Array,
|
||
// ratios:Array,
|
||
// matrix:Object,
|
||
// [spreadMethod:String],
|
||
// [interpolationMethod:String],
|
||
// [focalPointRatio:Number]) : Void
|
||
|
||
GASObject* arg = 0;
|
||
if (fn.NArgs > 0)
|
||
{
|
||
GASString fillTypeStr(fn.Arg(0).ToString(fn.Env));
|
||
|
||
if (fn.NArgs > 1 && (arg = fn.Arg(1).ToObject(fn.Env)) != NULL &&
|
||
arg->GetObjectType() == GASObjectInterface::Object_Array)
|
||
{
|
||
const GASArrayObject* colors = (const GASArrayObject*)arg;
|
||
if (fn.NArgs > 2 && (arg = fn.Arg(2).ToObject(fn.Env)) != NULL &&
|
||
arg->GetObjectType() == GASObjectInterface::Object_Array)
|
||
{
|
||
const GASArrayObject* alphas = (const GASArrayObject*)arg;
|
||
if (fn.NArgs > 3 && (arg = fn.Arg(3).ToObject(fn.Env)) != NULL &&
|
||
arg->GetObjectType() == GASObjectInterface::Object_Array)
|
||
{
|
||
const GASArrayObject* ratios = (const GASArrayObject*)arg;
|
||
if (fn.NArgs > 4 &&
|
||
colors->GetSize() > 0 &&
|
||
colors->GetSize() == alphas->GetSize() &&
|
||
colors->GetSize() == ratios->GetSize())
|
||
{
|
||
GMatrix2D matrix;
|
||
GASValue v;
|
||
GASStringContext *psc = fn.Env->GetSC();
|
||
|
||
arg = fn.Arg(4).ToObject(fn.Env);
|
||
|
||
#ifndef GFC_NO_FXPLAYER_AS_MATRIX
|
||
if(arg->GetObjectType() == GASObjectInterface::Object_Matrix)
|
||
{
|
||
matrix = ((GASMatrixObject*)arg)->GetMatrix(fn.Env);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
if (arg->GetConstMemberRaw(psc, "matrixType", &v) &&
|
||
v.ToString(fn.Env) == "box")
|
||
{
|
||
Float x = 0;
|
||
Float y = 0;
|
||
Float w = 100;
|
||
Float h = 100;
|
||
Float r = 0;
|
||
if(arg->GetConstMemberRaw(psc, "x", &v)) x = (Float)v.ToNumber(fn.Env);
|
||
if(arg->GetConstMemberRaw(psc, "y", &v)) y = (Float)v.ToNumber(fn.Env);
|
||
if(arg->GetConstMemberRaw(psc, "w", &v)) w = (Float)v.ToNumber(fn.Env);
|
||
if(arg->GetConstMemberRaw(psc, "h", &v)) h = (Float)v.ToNumber(fn.Env);
|
||
if(arg->GetConstMemberRaw(psc, "r", &v)) r = (Float)v.ToNumber(fn.Env);
|
||
|
||
x += w / 2;
|
||
y += h / 2;
|
||
w *= GASGradientBoxMagicNumber;
|
||
h *= GASGradientBoxMagicNumber;
|
||
|
||
matrix.AppendRotation(r);
|
||
matrix.AppendScaling(w, h);
|
||
matrix.AppendTranslation(x, y);
|
||
}
|
||
else
|
||
{
|
||
// The matrix is transposed:
|
||
// a(0,0) d(0,1) g(0,2)
|
||
// b(1,0) e(1,1) h(1,2)
|
||
// c(2,0) f(2,1) i(2,2)
|
||
// Elements c,f,i are not used
|
||
if(arg->GetConstMemberRaw(psc, "a", &v)) matrix.M_[0][0] = (Float)v.ToNumber(fn.Env) * GASGradientBoxMagicNumber;
|
||
if(arg->GetConstMemberRaw(psc, "d", &v)) matrix.M_[0][1] = (Float)v.ToNumber(fn.Env) * GASGradientBoxMagicNumber;
|
||
if(arg->GetConstMemberRaw(psc, "g", &v)) matrix.M_[0][2] = (Float)v.ToNumber(fn.Env);
|
||
if(arg->GetConstMemberRaw(psc, "b", &v)) matrix.M_[1][0] = (Float)v.ToNumber(fn.Env) * GASGradientBoxMagicNumber;
|
||
if(arg->GetConstMemberRaw(psc, "e", &v)) matrix.M_[1][1] = (Float)v.ToNumber(fn.Env) * GASGradientBoxMagicNumber;
|
||
if(arg->GetConstMemberRaw(psc, "h", &v)) matrix.M_[1][2] = (Float)v.ToNumber(fn.Env);
|
||
}
|
||
}
|
||
|
||
int spreadMethod = 0; // pad
|
||
bool linearRGB = false;
|
||
Float focalPointRatio = 0;
|
||
if (fn.NArgs > 5)
|
||
{
|
||
GASString str(fn.Arg(5).ToString(fn.Env));
|
||
|
||
if(str == "reflect") spreadMethod = 1;
|
||
else if(str == "repeat") spreadMethod = 2;
|
||
|
||
if (fn.NArgs > 6)
|
||
{
|
||
linearRGB = fn.Arg(6).ToString(fn.Env) == "linearRGB";
|
||
if (fn.NArgs > 7)
|
||
{
|
||
focalPointRatio = (Float)(fn.Arg(7).ToNumber(fn.Env));
|
||
if (GASNumberUtil::IsNaN(focalPointRatio))
|
||
focalPointRatio = 0;
|
||
else if (focalPointRatio < -1) focalPointRatio = -1;
|
||
else if (focalPointRatio > 1) focalPointRatio = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Create Gradient itself
|
||
GFxFillType fillType = GFxFill_LinearGradient;
|
||
if (fillTypeStr == "radial")
|
||
{
|
||
fillType = GFxFill_RadialGradient;
|
||
if (focalPointRatio != 0)
|
||
fillType = GFxFill_FocalPointGradient;
|
||
}
|
||
|
||
// Gradient data must live in a global heap since it is used as a key in ResourceLib.
|
||
GPtr<GFxGradientData> pgradientData =
|
||
*GNEW GFxGradientData(fillType, (UInt16)colors->GetSize(), linearRGB);
|
||
|
||
if (pgradientData)
|
||
{
|
||
pgradientData->SetFocalRatio(focalPointRatio);
|
||
|
||
for (int i = 0; i < colors->GetSize(); i++)
|
||
{
|
||
UInt rgba = UInt(colors->GetElementPtr(i)->ToUInt32(fn.Env) | 0xFF000000);
|
||
|
||
// Alpha is clamped to 0 to 100 range.
|
||
Float alpha = ((Float)alphas->GetElementPtr(i)->ToNumber(fn.Env)) * 255.0f / 100.0f;
|
||
rgba &= 0xFFFFFF;
|
||
rgba |= UInt(G_Clamp(alpha, 0.0f, 255.0f)) << 24;
|
||
|
||
Float ratio = (Float)ratios->GetElementPtr(i)->ToNumber(fn.Env);
|
||
ratio = G_Clamp(ratio, 0.0f, 255.0f);
|
||
|
||
GFxGradientRecord& record = (*pgradientData)[i];
|
||
record.Ratio = (UByte)ratio;
|
||
record.Color = rgba;
|
||
}
|
||
|
||
fillStyle->SetGradientFill(fillType, pgradientData, matrix);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteBeginGradientFill(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
GFxFillStyle* fillStyle = psprite->BeginFill();
|
||
if (fillStyle)
|
||
GFx_SpriteCreateGradient(fn, fillStyle);
|
||
}
|
||
|
||
static void GFx_SpriteLineGradientStyle(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
GFxFillStyle* fillStyle = psprite->CreateLineComplexFill();
|
||
if (fillStyle)
|
||
GFx_SpriteCreateGradient(fn, fillStyle);
|
||
}
|
||
|
||
static void GFx_SpriteBeginBitmapFill(const GASFnCall& fn)
|
||
{
|
||
GUNUSED(fn);
|
||
#ifndef GFC_NO_FXPLAYER_AS_BITMAPDATA
|
||
//beginBitmapFill(bmp:BitmapData,
|
||
// [matrix:Matrix],
|
||
// [repeat:Boolean],
|
||
// [smoothing:Boolean]) : Void
|
||
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs > 0) // bmp:BitmapData,
|
||
{
|
||
GPtr<GASObject> pobj = fn.Arg(0).ToObject(fn.Env);
|
||
if (!pobj || pobj->GetObjectType() != GASObject::Object_BitmapData)
|
||
return;
|
||
|
||
GASBitmapData* pbmpData = static_cast<GASBitmapData*>(pobj.GetPtr());
|
||
GFxImageResource* pimageRes = pbmpData->GetImage();
|
||
if (!pimageRes)
|
||
return;
|
||
|
||
GMatrix2D matrix;
|
||
bool repeat = true;
|
||
bool smoothing = false;
|
||
|
||
if (fn.NArgs > 1) // [matrix:Matrix]
|
||
{
|
||
#ifndef GFC_NO_FXPLAYER_AS_MATRIX
|
||
GASObject* arg = fn.Arg(1).ToObject(fn.Env);
|
||
if (arg && arg->GetObjectType() == GASObjectInterface::Object_Matrix)
|
||
{
|
||
matrix = ((GASMatrixObject*)arg)->GetMatrix(fn.Env);
|
||
}
|
||
#endif
|
||
|
||
if (fn.NArgs > 2) // [repeat:Boolean]
|
||
{
|
||
repeat = fn.Arg(2).ToBool(fn.Env);
|
||
if (fn.NArgs > 3) // [smoothing:Boolean]
|
||
{
|
||
smoothing = fn.Arg(3).ToBool(fn.Env);
|
||
}
|
||
}
|
||
}
|
||
|
||
GFxFillType fillType;
|
||
|
||
if (smoothing)
|
||
{
|
||
if (repeat) fillType = GFxFill_TiledSmoothImage;
|
||
else fillType = GFxFill_ClippedSmoothImage;
|
||
}
|
||
else
|
||
{
|
||
if (repeat) fillType = GFxFill_TiledImage;
|
||
else fillType = GFxFill_ClippedImage;
|
||
}
|
||
|
||
psprite->BeginBitmapFill(fillType, pimageRes, matrix);
|
||
}
|
||
#else
|
||
GFC_DEBUG_WARNING(1, "Error: beginBitmapFill is failed since GFC_NO_FXPLAYER_AS_BITMAPDATA is defined.");
|
||
#endif
|
||
}
|
||
|
||
static void GFx_SpriteEndFill(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
psprite->EndFill();
|
||
}
|
||
|
||
static void GFx_SpriteLineStyle(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs > 0) // thickness:Number
|
||
{
|
||
Float lineWidth = (Float)(fn.Arg(0).ToNumber(fn.Env));
|
||
UInt rgba = 0xFF000000;
|
||
bool hinting = false;
|
||
GFxLineStyle::LineStyle scaling = GFxLineStyle::LineScaling_Normal;
|
||
GFxLineStyle::LineStyle caps = GFxLineStyle::LineCap_Round;
|
||
GFxLineStyle::LineStyle joins = GFxLineStyle::LineJoin_Round;
|
||
Float miterLimit = 3.0f;
|
||
|
||
if(fn.NArgs > 1) // rgb:Number
|
||
{
|
||
rgba = fn.Arg(1).ToUInt32(fn.Env) | 0xFF000000;
|
||
|
||
if(fn.NArgs > 2) // alpha:Number
|
||
{
|
||
Float alpha = ((Float)fn.Arg(2).ToNumber(fn.Env)) * 255.0f / 100.0f;
|
||
|
||
rgba &= 0xFFFFFF;
|
||
rgba |= UInt(G_Clamp(alpha, 0.0f, 255.0f)) << 24;
|
||
|
||
if(fn.NArgs > 3) // pixelHinting:Boolean
|
||
{
|
||
hinting = fn.Arg(3).ToBool(fn.Env);
|
||
|
||
if(fn.NArgs > 4) // noScale:String
|
||
{
|
||
GASString str(fn.Arg(4).ToString(fn.Env));
|
||
if(str == "none") scaling = GFxLineStyle::LineScaling_None;
|
||
else if(str == "vertical") scaling = GFxLineStyle::LineScaling_Vertical;
|
||
else if(str == "horizontal") scaling = GFxLineStyle::LineScaling_Horizontal;
|
||
|
||
if(fn.NArgs > 5) // capsStyle:String
|
||
{
|
||
str = fn.Arg(5).ToString(fn.Env);
|
||
if(str == "none") caps = GFxLineStyle::LineCap_None;
|
||
else if(str == "square") caps = GFxLineStyle::LineCap_Square;
|
||
|
||
if(fn.NArgs > 6) // jointStyle:String
|
||
{
|
||
str = fn.Arg(6).ToString(fn.Env);
|
||
if(str == "miter") joins = GFxLineStyle::LineJoin_Miter;
|
||
else if(str == "bevel") joins = GFxLineStyle::LineJoin_Bevel;
|
||
|
||
if(fn.NArgs > 7) // miterLimit:Number
|
||
{
|
||
miterLimit = (Float)(fn.Arg(7).ToNumber(fn.Env));
|
||
if(miterLimit < 1.0f) miterLimit = 1.0f;
|
||
if(miterLimit > 255.0f) miterLimit = 255.0f;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
psprite->SetLineStyle(lineWidth, rgba, hinting, scaling, caps, joins, miterLimit);
|
||
}
|
||
else
|
||
{
|
||
psprite->SetNoLine();
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteMoveTo(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs >= 2)
|
||
{
|
||
Float x = (Float)(fn.Arg(0).ToNumber(fn.Env));
|
||
Float y = (Float)(fn.Arg(1).ToNumber(fn.Env));
|
||
psprite->MoveTo(x, y);
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteLineTo(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs >= 2)
|
||
{
|
||
Float x = (Float)(fn.Arg(0).ToNumber(fn.Env));
|
||
Float y = (Float)(fn.Arg(1).ToNumber(fn.Env));
|
||
psprite->LineTo(x, y);
|
||
}
|
||
}
|
||
|
||
static void GFx_SpriteCurveTo(const GASFnCall& fn)
|
||
{
|
||
GFxSprite* psprite = GFx_SpriteGetTarget(fn);
|
||
if (psprite == NULL) return;
|
||
|
||
if (fn.NArgs >= 4)
|
||
{
|
||
Float cx = (Float)(fn.Arg(0).ToNumber(fn.Env));
|
||
Float cy = (Float)(fn.Arg(1).ToNumber(fn.Env));
|
||
Float ax = (Float)(fn.Arg(2).ToNumber(fn.Env));
|
||
Float ay = (Float)(fn.Arg(3).ToNumber(fn.Env));
|
||
psprite->CurveTo(cx, cy, ax, ay);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////
|
||
void GASMovieClipObject::commonInit ()
|
||
{
|
||
ButtonEventMask = 0;
|
||
HasButtonHandlers = 0;
|
||
}
|
||
|
||
UInt16 GASMovieClipObject::GetButtonEventNameMask(GASStringContext* psc, const GASString& name)
|
||
{
|
||
if (psc->GetBuiltin(GASBuiltin_onPress) == name)
|
||
return Mask_onPress;
|
||
else if (psc->GetBuiltin(GASBuiltin_onRelease) == name)
|
||
return Mask_onRelease;
|
||
else if (psc->GetBuiltin(GASBuiltin_onReleaseOutside) == name)
|
||
return Mask_onReleaseOutside;
|
||
else if (psc->GetBuiltin(GASBuiltin_onRollOver) == name)
|
||
return Mask_onRollOver;
|
||
else if (psc->GetBuiltin(GASBuiltin_onRollOut) == name)
|
||
return Mask_onRollOut;
|
||
else if (psc->GetBuiltin(GASBuiltin_onDragOver) == name)
|
||
return Mask_onDragOver;
|
||
else if (psc->GetBuiltin(GASBuiltin_onDragOut) == name)
|
||
return Mask_onDragOut;
|
||
|
||
else if (psc->GetBuiltin(GASBuiltin_onPressAux) == name)
|
||
return Mask_onPressAux;
|
||
else if (psc->GetBuiltin(GASBuiltin_onReleaseAux) == name)
|
||
return Mask_onReleaseAux;
|
||
else if (psc->GetBuiltin(GASBuiltin_onReleaseOutsideAux) == name)
|
||
return Mask_onReleaseOutsideAux;
|
||
else if (psc->GetBuiltin(GASBuiltin_onDragOverAux) == name)
|
||
return Mask_onDragOverAux;
|
||
else if (psc->GetBuiltin(GASBuiltin_onDragOutAux) == name)
|
||
return Mask_onDragOutAux;
|
||
|
||
return 0;
|
||
}
|
||
|
||
// Updates DynButtonHandlerCount if name is begin added through SetMember or
|
||
// deleted through DeleteMember.
|
||
void GASMovieClipObject::TrackMemberButtonHandler(GASStringContext* psc, const GASString& name, bool deleteFlag)
|
||
{
|
||
// If we are one of button handlers, do the check.
|
||
if (name.GetSize() > 2 && name[0] == 'o' && name[1] == 'n')
|
||
{
|
||
GASValue val;
|
||
//!if (ASEnvironment.GetMember(name, &val))
|
||
if (GetMemberRaw(psc, name, &val))
|
||
{
|
||
// Set and member already exists? No tracking increment.
|
||
if (!deleteFlag)
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// Delete and does not exist? No decrement.
|
||
if (deleteFlag)
|
||
return;
|
||
}
|
||
// Any button handler enables button behavior.
|
||
UInt16 bemask;
|
||
if ((bemask = GetButtonEventNameMask(psc, name)) != 0)
|
||
{
|
||
if (deleteFlag)
|
||
ButtonEventMask &= ~bemask;
|
||
else
|
||
ButtonEventMask |= bemask;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GASMovieClipObject::Set__proto__(GASStringContext *psc, GASObject* protoObj)
|
||
{
|
||
GASObject::Set__proto__(psc, protoObj);
|
||
// proto can be NULL, when we reset the proto in OnEventUnload
|
||
//GASSERT(protoObj);
|
||
if (protoObj && protoObj->GetObjectType() != Object_MovieClipObject)
|
||
{
|
||
//AB: if proto is being set to the sprite, and this proto is not
|
||
// an instance of MovieClipObject then we need to rescan it for button's
|
||
// handlers to react on events correctly. This usually happen if
|
||
// component is extending MovieClip as a class and defines
|
||
// buttons' handler as methods.
|
||
struct MemberVisitor : GASObjectInterface::MemberVisitor
|
||
{
|
||
GPtr<GASMovieClipObject> obj;
|
||
GASStringContext* pStringContext;
|
||
|
||
MemberVisitor(GASStringContext* psc, GASMovieClipObject* _obj)
|
||
: obj(_obj), pStringContext(psc) { }
|
||
|
||
virtual void Visit(const GASString& name, const GASValue& , UByte flags)
|
||
{
|
||
GUNUSED(flags);
|
||
if (IsButtonEventName(pStringContext, name))
|
||
{
|
||
obj->SetHasButtonHandlers(true);
|
||
}
|
||
}
|
||
} visitor (psc, this);
|
||
pProto->VisitMembers(psc, &visitor,
|
||
GASObjectInterface::VisitMember_Prototype |
|
||
GASObjectInterface::VisitMember_DontEnum |
|
||
GASObjectInterface::VisitMember_NamesOnly);
|
||
}
|
||
}
|
||
|
||
bool GASMovieClipObject::ActsAsButton() const
|
||
{
|
||
if ((ButtonEventMask != 0) || HasButtonHandlers)
|
||
return true;
|
||
for(GASObject* pproto = pProto; pproto != 0; pproto = pproto->Get__proto__())
|
||
{
|
||
if (pProto->GetObjectType() == Object_MovieClipObject)
|
||
{
|
||
// MA: No need to AddRef to 'pproto'.
|
||
GASMovieClipProto* proto = static_cast<GASMovieClipProto*>(pproto);
|
||
return proto->ActsAsButton();
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void GASMovieClipObject::SetMemberCommon(GASStringContext *psc,
|
||
const GASString& name,
|
||
const GASValue& val)
|
||
{
|
||
// _levelN can't function as a button
|
||
// MovieClip.prototype has no sprite (GetSprite() returns 0) but
|
||
// it should track button handlers.
|
||
GPtr<GFxSprite> spr = GetSprite();
|
||
if (!spr || spr->GetASRootMovie() != spr)
|
||
TrackMemberButtonHandler(psc, name);
|
||
|
||
// Intercept the user data properties and cache them in pSprite
|
||
if (spr && name.IsBuiltin())
|
||
{
|
||
if (name == psc->GetBuiltin(GASBuiltin_rendererString))
|
||
{
|
||
spr->SetRendererString(val.ToString(spr->GetASEnvironment()));
|
||
}
|
||
else if (name == psc->GetBuiltin(GASBuiltin_rendererFloat))
|
||
{
|
||
spr->SetRendererFloat((float)val.ToNumber(spr->GetASEnvironment()));
|
||
}
|
||
else if (name == psc->GetBuiltin(GASBuiltin_rendererMatrix))
|
||
{
|
||
GPtr<GASObject> pObj = val.ToObject(spr->GetASEnvironment());
|
||
if (pObj && pObj->GetObjectType() == GASObjectInterface::Object_Array)
|
||
{
|
||
GASArrayObject* pArray = (GASArrayObject*)pObj.GetPtr();
|
||
float f[16];
|
||
UInt size = G_Min(16, pArray->GetSize());
|
||
for (UInt i = 0; i < size; i++)
|
||
f[i] = (float)pArray->GetElementPtr(i)->ToNumber(spr->GetASEnvironment());
|
||
spr->SetRendererMatrix(f, size);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Set the named member to the value. Return true if we have
|
||
// that member; false otherwise.
|
||
// Note, we need to define both - SetMember and SetMemberRaw, since both of them
|
||
// might be called independently. So, moved common part to SetMemberCommon.
|
||
bool GASMovieClipObject::SetMemberRaw(GASStringContext *psc,
|
||
const GASString& name,
|
||
const GASValue& val,
|
||
const GASPropFlags& flags)
|
||
{
|
||
SetMemberCommon(psc, name, val);
|
||
return GASObject::SetMemberRaw(psc, name, val, flags);
|
||
}
|
||
|
||
bool GASMovieClipObject::SetMember(GASEnvironment *penv,
|
||
const GASString& name,
|
||
const GASValue& val,
|
||
const GASPropFlags& flags)
|
||
{
|
||
SetMemberCommon(penv->GetSC(), name, val);
|
||
return GASObject::SetMember(penv, name, val, flags);
|
||
}
|
||
|
||
// Set *val to the value of the named member and
|
||
// return true, if we have the named member.
|
||
// Otherwise leave *val alone and return false.
|
||
/*bool GASMovieClipObject::GetMember(GASEnvironment* penv, const GASString& name, GASValue* val)
|
||
{
|
||
return GASObject::GetMember(penv, name, val);
|
||
}*/
|
||
|
||
// Delete a member field, if not read-only. Return true if deleted.
|
||
bool GASMovieClipObject::DeleteMember(GASStringContext *psc, const GASString& name)
|
||
{
|
||
TrackMemberButtonHandler(psc, name, 1);
|
||
return GASObject::DeleteMember(psc, name);
|
||
}
|
||
|
||
void GASMovieClipObject::ForceShutdown ()
|
||
{
|
||
// traverse through all members, look for FUNCTION and release local frames.
|
||
// Needed to prevent memory leak until we don't have garbage collection.
|
||
#ifdef GFC_NO_GC
|
||
for (MemberHash::Iterator iter = Members.Begin(); iter != Members.End(); ++iter)
|
||
{
|
||
GASValue& value = iter->Second.Value;
|
||
if (value.IsFunction ())
|
||
{
|
||
value.V.FunctionValue.SetLocalFrame (0);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static const GASNameFunction MovieClipFunctionTable[] =
|
||
{
|
||
{ "play", &GFx_SpritePlay },
|
||
{ "stop", &GFx_SpriteStop },
|
||
{ "gotoAndStop", &GFx_SpriteGotoAndStop },
|
||
{ "gotoAndPlay", &GFx_SpriteGotoAndPlay },
|
||
{ "nextFrame", &GFx_SpriteNextFrame },
|
||
{ "prevFrame", &GFx_SpritePrevFrame },
|
||
{ "getBytesLoaded", &GFx_SpriteGetBytesLoaded },
|
||
{ "getBytesTotal", &GFx_SpriteGetBytesTotal },
|
||
|
||
{ "getBounds", &GFx_SpriteGetBounds },
|
||
{ "getRect", &GFx_SpriteGetRect },
|
||
{ "localToGlobal", &GFx_SpriteLocalToGlobal },
|
||
{ "globalToLocal", &GFx_SpriteGlobalToLocal },
|
||
{ "hitTest", &GFx_SpriteHitTest },
|
||
|
||
{ "attachBitmap", &GFx_SpriteAttachBitmap },
|
||
{ "attachMovie", &GFx_SpriteAttachMovie },
|
||
#if !defined(GFC_NO_SOUND) && !defined(GFC_NO_VIDEO)
|
||
{ "attachAudio", &GFx_SpriteAttachAudio },
|
||
#endif
|
||
{ "duplicateMovieClip", &GFx_SpriteDuplicateMovieClip },
|
||
{ "removeMovieClip", &GFx_SpriteRemoveMovieClip },
|
||
{ "createEmptyMovieClip", &GFx_SpriteCreateEmptyMovieClip },
|
||
{ "createTextField", &GFx_SpriteCreateTextField },
|
||
|
||
{ "getDepth", &GFxASCharacter::CharacterGetDepth },
|
||
{ "swapDepths", &GFxSprite::SpriteSwapDepths },
|
||
{ "getNextHighestDepth", &GFx_SpriteGetNextHighestDepth },
|
||
{ "getInstanceAtDepth", &GFx_SpriteGetInstanceAtDepth },
|
||
|
||
{ "getTextSnapshot", &GFx_SpriteGetTextSnapshot },
|
||
|
||
{ "getSWFVersion", &GFx_SpriteGetSWFVersion },
|
||
|
||
{ "startDrag", &GFx_SpriteStartDrag },
|
||
{ "stopDrag", &GFx_SpriteStopDrag },
|
||
|
||
{ "setMask", &GFx_SpriteSetMask },
|
||
|
||
{ "loadMovie", &GFx_SpriteLoadMovie },
|
||
{ "unloadMovie", &GFx_SpriteUnloadMovie },
|
||
|
||
{ "loadVariables", &GFx_SpriteLoadVariables },
|
||
|
||
// Drawing API
|
||
|
||
{ "clear", &GFx_SpriteClear },
|
||
{ "beginFill", &GFx_SpriteBeginFill },
|
||
{ "beginGradientFill", &GFx_SpriteBeginGradientFill },
|
||
{ "beginBitmapFill", &GFx_SpriteBeginBitmapFill },
|
||
{ "lineGradientStyle", &GFx_SpriteLineGradientStyle },
|
||
{ "endFill", &GFx_SpriteEndFill },
|
||
{ "lineStyle", &GFx_SpriteLineStyle },
|
||
{ "moveTo", &GFx_SpriteMoveTo },
|
||
{ "lineTo", &GFx_SpriteLineTo },
|
||
{ "curveTo", &GFx_SpriteCurveTo },
|
||
|
||
{ 0, 0 }
|
||
};
|
||
|
||
GASMovieClipProto::GASMovieClipProto(GASStringContext* psc, GASObject* prototype, const GASFunctionRef& constructor) :
|
||
GASPrototype<GASMovieClipObject>(psc, prototype, constructor)
|
||
{
|
||
InitFunctionMembers(psc, MovieClipFunctionTable);
|
||
SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_useHandCursor), GASValue(true),
|
||
GASPropFlags::PropFlag_DontDelete | GASPropFlags::PropFlag_DontEnum);
|
||
|
||
SetConstMemberRaw(psc, "enabled", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
SetConstMemberRaw(psc, "scale9Grid", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
// can't add tabEnabled, focusEnabled and tabChildren here, since their presence will stop prototype chain lookup, even if
|
||
// it is UNSET or "undefined".
|
||
//SetConstMemberRaw(psc, "focusEnabled", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
//SetConstMemberRaw(psc, "tabChildren", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
//SetConstMemberRaw(psc, "tabEnabled", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
SetConstMemberRaw(psc, "tabIndex", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
SetConstMemberRaw(psc, "trackAsMenu", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
SetConstMemberRaw(psc, "transform", GASValue::UNSET, GASPropFlags::PropFlag_DontDelete|GASPropFlags::PropFlag_DontEnum);
|
||
}
|
||
|
||
GASObject* GASMovieClipCtorFunction::CreateNewObject(GASEnvironment* penv) const
|
||
{
|
||
return GHEAP_NEW(penv->GetHeap()) GASMovieClipObject(penv);
|
||
}
|
||
|
||
void GASMovieClipCtorFunction::GlobalCtor(const GASFnCall& fn)
|
||
{
|
||
if (fn.ThisPtr && fn.ThisPtr->GetObjectType() == Object_MovieClipObject)
|
||
{
|
||
GASMovieClipObject* pobj = static_cast<GASMovieClipObject*>(fn.ThisPtr);
|
||
fn.Result->SetAsObject(pobj);
|
||
}
|
||
else
|
||
fn.Result->SetUndefined();
|
||
}
|
||
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
#ifdef GFC_BUILD_DEBUG
|
||
void GFxSprite::DumpDisplayList(int indent, const char* title)
|
||
{
|
||
char indentStr[900];
|
||
memset(indentStr, 32, indent*3);
|
||
indentStr[indent*3] = 0;
|
||
|
||
if (title)
|
||
printf("%s -- %s --\n", indentStr, title);
|
||
else
|
||
printf("\n");
|
||
|
||
if (indent == 0)
|
||
{
|
||
printf("%sSprite %s\n", indentStr, GetCharacterHandle()->GetNamePath().ToCStr());
|
||
printf("%sId = %d\n", indentStr, (int)GetId().GetIdValue());
|
||
printf("%sCreateFrame = %d\n", indentStr, (int)GetCreateFrame());
|
||
printf("%sDepth = %d\n", indentStr, (int)GetDepth());
|
||
printf("%sVisibility = %s\n", indentStr, GetVisible() ? "true" : "false");
|
||
indent++;
|
||
memset(indentStr, 32, indent*3);
|
||
indentStr[indent*3] = 0;
|
||
}
|
||
|
||
for (UInt i = 0; i<DisplayList.DisplayObjectArray.GetSize(); i++)
|
||
{
|
||
const GFxDisplayList::DisplayEntry& dobj = DisplayList.DisplayObjectArray[i];
|
||
GFxCharacter* ch = dobj.GetCharacter();
|
||
|
||
GFxASCharacter* pasch = NULL;
|
||
if (ch->IsCharASCharacter())
|
||
{
|
||
pasch = ch->CharToASCharacter_Unsafe();
|
||
if (pasch->IsSprite())
|
||
printf("%sSprite %s\n", indentStr, pasch->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
else
|
||
printf("%sCharacter %s\n", indentStr, pasch->GetCharacterHandle()->GetNamePath().ToCStr());
|
||
}
|
||
else
|
||
printf("%sCharacter (generic)\n", indentStr);
|
||
if (dobj.IsMarkedForRemove())
|
||
printf("%s(marked for remove)\n", indentStr);
|
||
printf("%sId = %d\n", indentStr, (int)ch->GetId().GetIdValue());
|
||
printf("%sCreateFrame = %d\n", indentStr, (int)ch->GetCreateFrame());
|
||
printf("%sDepth = %d\n", indentStr, (int)ch->GetDepth());
|
||
printf("%sClipDepth = %d\n", indentStr, (int)ch->GetClipDepth());
|
||
printf("%sVisibility = %s\n", indentStr, ch->GetVisible() ? "true" : "false");
|
||
|
||
char buff[2560];
|
||
const Cxform& cx = ch->GetCxform();
|
||
if (!cx.IsIdentity())
|
||
{
|
||
cx.Format(buff);
|
||
printf("%sCxform:\n%s\n", indentStr, buff);
|
||
}
|
||
const Matrix& mx = ch->GetMatrix();
|
||
if (mx != Matrix::Identity)
|
||
{
|
||
mx.Format(buff);
|
||
printf("%sMatrix:\n%s\n", indentStr, buff);
|
||
}
|
||
|
||
if (pasch && pasch->IsSprite())
|
||
{
|
||
// printf("\n");
|
||
pasch->ToSprite()->DumpDisplayList(indent+1);
|
||
}
|
||
else
|
||
printf("\n");
|
||
}
|
||
if (title)
|
||
printf("%s-----------------\n", indentStr);
|
||
}
|
||
#endif // GFC_BUILD_DEBUG
|