/// local includes
#include "GameSWFHelper.h"
#include "GameSWFLocalization.h"
#include "Utils/RangeFor.h"
/// library includes
#include <CasualCore.h>
#include <RKDevice.h>
#include <RKCamera.h>
#include "RKEngine/Private/Include/RKRender.h" //!< For RKRender_SetRenderLayerCustomViewport / RKRender_SetRenderLayerCustomCamera
#include "Utils/SoundEmulator.h"
namespace gameswf
{
static RKList<RKString> s_preserved(4);
static RKList<RKString> s_preservedCopy;
static bool s_visible = true;
FlashFX* load(const char* filename, bool background /*= false*/) {
FlashFX* fx = CGAME->GetFlashManager()->GetFlashFX(filename);
if (fx == nullptr) {
fx = CGAME->GetFlashManager()->LoadFlashFX(filename, background);
#if defined(CC_DEFAULT_SWF_SCALE)
CasualCore::GameSettings* settings = CGAME->GetSettings();
float scale = (settings->m_defaultSWFCreationHeight > settings->m_defaultSWFCreationWidth)
? (static_cast<float>(RKDevice_GetWidth()) / settings->m_defaultSWFCreationWidth)
: (static_cast<float>(RKDevice_GetHeight()) / 960.f); // settings->m_defaultSWFCreationHeight;
CGAME->GetFlashManager()->SetFlashScale(fx, scale);
#endif // CC_DEFAULT_SWF_SCALE
}
else
{
fx = CGAME->GetFlashManager()->LoadFlashFX(filename, background);
}
if (s_visible)
return fx;
for (const RKString& name : s_preserved) {
if (name == filename) return fx;
}
CGAME->GetFlashManager()->SetVisible(filename, false);
return fx;
}
void unload(FlashFX* swf) {
if (swf == nullptr) return;
if (GameSWFLocalization::IsValid())
GameSWFLocalization::GetInstance().RemoveLocalizedElements(swf);
CGAME->GetFlashManager()->UnloadFlashFX(swf);
}
void preserveVisibility(const char* name) {
s_preserved.AppendUnique(name);
}
void pushVisibiliyList() {
s_preservedCopy = s_preserved;
s_preserved.Clear();
}
void popVisibiliyList() {
s_preserved = s_preservedCopy;
s_preservedCopy.Clear();
}
} // namespace gameswf
GameSWFHelper::GameSWFHelper()
{
gameswf::registerNativeFunction("PlaySWFSound", &GameSWFHelper::PlaySound);
gameswf::registerNativeFunction("ButtonPressedToScript", &GameSWFHelper::ButtonPressedToScript);
gameswf::registerNativeFunction("GetDeviceWidth", &GameSWFHelper::GetDeviceWidth);
gameswf::registerNativeFunction("GetDeviceHeight", &GameSWFHelper::GetDeviceHeight);
gameswf::registerNativeFunction("GetDeviceAspect", &GameSWFHelper::GetDeviceAspect);
gameswf::registerNativeFunction("IsOnFrameWithLabel", &GameSWFHelper::IsOnFrameWithLabel);
}
void GameSWFHelper::ClearCustomCamera(RKRenderLayer* a_pRenderLayer)
{
RKRender_SetRenderLayerCustomCamera(a_pRenderLayer, nullptr);
}
void GameSWFHelper::SetCameraViewportToCharacterHandle(const RKRect &viewport, RKCamera* a_pCamera, RKRenderLayer* a_pRenderLayer)
{
RKRender_SetRenderLayerCustomCamera(a_pRenderLayer, a_pCamera);
RKIntRect viewportRect;
viewportRect.x = static_cast<int>(viewport.x);
viewportRect.y = static_cast<int>(viewport.y);
viewportRect.w = static_cast<int>(viewport.w);
viewportRect.h = static_cast<int>(viewport.h);
RKRender_SetRenderLayerCustomViewport(a_pRenderLayer, &viewportRect);
}
gameswf::Point GameSWFHelper::WorldToSWFCoordinates(const gameswf::FlashFX* a_pFX, const RKVector& a_worldPosition)
{
RKVector screenCoords(RKVector::Zero);
RKCamera* camera = RKCamera_GetCurrent();
if (camera != nullptr)
{
// Does projection transformation.
RKMatrix viewProjectionMatrix = camera->GetViewProjection();
RKVector transformedPoint = viewProjectionMatrix.Transform(a_worldPosition);
screenCoords = transformedPoint / transformedPoint.w;
// Adjusts if from (x,y) = (-1 to 1, -1 to 1) to screen (x,y) = (0 to 1, 1 to 0)
screenCoords.x = 0.5f + screenCoords.x / 2.0f;
screenCoords.y = 0.5f - screenCoords.y / 2.0f;
float deviceRatio = RKDevice_GetAspect();
float flashRatio = static_cast<float>(a_pFX->getBoundWidth()) / static_cast<float>(a_pFX->getBoundHeight());
float ratio = deviceRatio / flashRatio;
//Adjusts to the screen size.
screenCoords.x *= a_pFX->getBoundWidth() * ratio;
screenCoords.y *= a_pFX->getBoundHeight();
}
return gameswf::Point(screenCoords.x, screenCoords.y);
}
void GameSWFHelper::PlaySound(const gameswf::FunctionCall& a_fn)
{
if (a_fn.nargs != 0)
{
const char* arg = a_fn.arg(0).toCStr();
if (arg != nullptr) {
SOUND->Play(arg);
}
else {
RKASSERT(false, "Attempting to play NULL sound");
}
}
}
void GameSWFHelper::ButtonPressedToScript(const gameswf::FunctionCall& a_fn)
{
if (a_fn.nargs > 0)
{
const char* handleName = a_fn.arg(0).toCStr();
if (handleName != nullptr) {
const char* functionName = a_fn.nargs > 1 ? a_fn.arg(1).toCStr() : "";
const char* functionParameter = a_fn.nargs > 2 ? a_fn.arg(2).toCStr() : "";
SCRIPT->CallScene("ButtonPressed", "sss", handleName, functionName, functionParameter);
}
else {
RKLOGt("ui", "Attempting to pass scene script a button press with no handle name");
}
}
}
void GameSWFHelper::GetDeviceWidth(const gameswf::FunctionCall& a_fn)
{
a_fn.result->setInt(RKDevice_GetWidth());
}
void GameSWFHelper::GetDeviceHeight(const gameswf::FunctionCall& a_fn)
{
a_fn.result->setInt(RKDevice_GetHeight());
}
void GameSWFHelper::GetDeviceAspect(const gameswf::FunctionCall& a_fn)
{
a_fn.result->setDouble((static_cast<double>(RKDevice_GetWidth()) / RKDevice_GetHeight()));
}
void GameSWFHelper::IsOnFrameWithLabel(const gameswf::FunctionCall& a_fn)
{
gameswf::CharacterHandle obj = gameswf::CharacterHandle(a_fn.this_ptr);
if (obj.isValid())
{
const bool bIsOnFrame = obj.getCurrentFrame() == obj.getFrameIdFromLabel(a_fn.arg(0).toCStr());
a_fn.result->setBool(bIsOnFrame);
}
}
bool GameSWFHelper::IsHandleValidLog(gameswf::CharacterHandle a_hHandle, const char* a_sErrorMessage /*= nullptr*/)
{
if (!a_hHandle.isValid())
{
RKString errorMessage = "gameswf::CharacterHandle invalid";
if (a_sErrorMessage != nullptr)
{
RKString extraMessage;
extraMessage.MakeFormatted(": %s", a_sErrorMessage);
errorMessage.Append(extraMessage);
}
RKLOGt_ERROR("ui", errorMessage.GetString());
return false;
}
return true;
}
bool GameSWFHelper::SetHandleText(const char* a_sHandleName, const char* a_sText, gameswf::FlashFX* a_pFX, const char* a_sErrorMessage /*= nullptr*/)
{
gameswf::CharacterHandle hHandle = a_pFX->find(a_sHandleName);
if (GameSWFHelper::IsHandleValidLog(hHandle, a_sErrorMessage)) {
hHandle.setText(a_sText);
return true;
}
return false;
}
bool GameSWFHelper::AreAllAncestorsVisible(gameswf::CharacterHandle& a_hHandle)
{
if (!a_hHandle.isValid()) return true; // If the handle is invalid and we haven't yet returned false, everything is visible.
if (!a_hHandle.isVisible()) return false;
gameswf::CharacterHandle hParent = a_hHandle.getParent();
return AreAllAncestorsVisible(hParent);
}
void GameSWFHelper::SetAllChildrenVisible(gameswf::CharacterHandle& a_hHandle, bool a_bVisible)
{
if (a_hHandle.isValid()) {
gameswf::array<gameswf::CharacterHandle> pChildren;
a_hHandle.getChildren(pChildren);
int iSize = pChildren.size();
for (int i = 0; i < iSize; ++i)
{
const char* sName = pChildren[i].getName().c_str();
pChildren[i].setVisible(a_bVisible);
pChildren[i].setEnabled(a_bVisible);
}
}
return;
}
void GameSWFHelper::SetRelativeSWFOrder(gameswf::FlashFX* a_pSWFBeingReordered, gameswf::FlashFX* a_pRelativeSWF, int a_order)
{
CGAME->GetFlashManager()->SetFlashOrder(a_pSWFBeingReordered, CGAME->GetFlashManager()->GetFlashOrder(a_pRelativeSWF) + a_order);
}
bool GameSWFHelper::IsOnFrame(gameswf::CharacterHandle& a_hHandle, const char* a_sLabelName)
{
return a_hHandle.getCurrentFrame() == a_hHandle.getFrameIdFromLabel(a_sLabelName);
}
gameswf::Point GameSWFHelper::GetHandleWorldPosition(gameswf::CharacterHandle& a_hHandle)
{
gameswf::Point result;
static const float ratio_4_3 = (4.0f / 3.0f) * 1.05f; // Give us a 5% buffer - some 4:3 aspects are a touch larger than 4/3.
bool bScaleForLargerAspect = RKDevice_GetAspect() >= ratio_4_3;
while (a_hHandle.isValid())
{
if (bScaleForLargerAspect) // Check if we need to scale so we don't unecessarily query for scale on each element in the hierarchy
{
gameswf::ASValue ASScale = a_hHandle.getMember("Scale_for_larger_aspect_ratio");
if (ASScale.isNumber())
{
float scale = ASScale.toFloat();
if (scale != 0.0f)
{
// If we are an AnchorNode with a scale applied, we want to factor that scale into our children's positions
result.m_x *= scale;
result.m_y *= scale;
}
}
}
result.m_x += a_hHandle.getMember("_x").toFloat();
result.m_y += a_hHandle.getMember("_y").toFloat();
a_hHandle = a_hHandle.getParent();
}
return result;
}
void GameSWFHelper::PrintLeafNames(gameswf::CharacterHandle& a_hHandle)
{
if (a_hHandle.isValid())
{
gameswf::array<gameswf::CharacterHandle> pChildren;
a_hHandle.getChildren(pChildren);
int iSize = pChildren.size();
for (int i = 0; i < iSize; ++i)
{
PrintLeafNames(pChildren[i]);
}
// Leaf element, print path
if (iSize == 0)
{
// Recurse up the hierarchy to determine the full handle path
RKString sFullName = "";
gameswf::CharacterHandle hCurrentHandle = a_hHandle;
const char* MISSING_IDENTIFIER = "NAME_MISSING";
while (hCurrentHandle.isValid())
{
RKString sPrevName = sFullName;
const char* sCurrentName = hCurrentHandle.getName().size() > 0 ? hCurrentHandle.getName().c_str() : MISSING_IDENTIFIER; // If the name is missing, the handle path cannot be relied upon. a name of "_instanceX" will be generated.
sFullName = RKString::MakeFormatted("%s.%s", sCurrentName, sPrevName.GetString());
hCurrentHandle = hCurrentHandle.getParent();
}
// Clean up the string
sFullName.StripLeft(MISSING_IDENTIFIER); // what should be "_level0" is displaying as empty. Remove it.
sFullName.StripLeft(".");
sFullName.StripRight(".");
RKLOGt_INFO("ui", sFullName.GetString());
}
}
return;
}
void GameSWFHelper::SetLocalizationID(gameswf::CharacterHandle& a_hHandle, const char* a_sLocalizationID)
{
gameswf::ASValue sLocalizationParamater = a_sLocalizationID;
a_hHandle.invokeMethod("setTranslationID", &sLocalizationParamater, 1);
}
void GameSWFHelper::ClearAutomaticLocalization(gameswf::CharacterHandle& a_hHandle)
{
a_hHandle.setMember("untranslated", gameswf::ASValue()); // Clear untranslated string
a_hHandle.setMember("translationID", gameswf::ASValue()); // Clear loca ID
a_hHandle.invokeMethod("TextUnload"); // Unregister it as auto-loca so we don't override custom messages
}
RKString GameSWFHelper::GetHtmlText(const char* a_sFontName, const char* a_sText)
{
return RKString::MakeFormatted("<font face='%s'>%s</font>", a_sFontName, a_sText);
}
void GameSWFHelper::SetButtonEnabled(gameswf::CharacterHandle& a_hButton, bool a_bIsEnabled)
{
gameswf::ASValue bDisabled = !a_bIsEnabled;
a_hButton.invokeMethod("setDisabled", &bDisabled, 1);
}
bool GameSWFHelper::IsBetweenAnimations(gameswf::CharacterHandle& a_hElement, const char* a_sStartLabelName, const char* a_sEndLabelName, bool a_bInclusiveOfEndLabel/* = false*/)
{
int currentFrame = a_hElement.getCurrentFrame();
int startFrame = a_hElement.getFrameIdFromLabel(a_sStartLabelName);
int endFrame = a_hElement.getFrameIdFromLabel(a_sEndLabelName);
// Reduce final frame test if required (checking bounds)
if (!a_bInclusiveOfEndLabel && endFrame > 0)
{
--endFrame;
}
return currentFrame >= startFrame && currentFrame <= endFrame;
}