Difference between revisions of "Implementing Adobe Flash:ru"

From Valve Developer Community
Jump to: navigation, search
(Blanked the page)
Line 1: Line 1:
{{otherlang2
 
|title= Добавление Adobe Flash
 
|en=Implementing_Adobe_Flash
 
}}
 
  
РУССКАЯ ВЕРСИЯ СТАТЬИ В РАЗРАБОТКЕ!
 
 
== Введение ==
 
Adobe Flash - это мощный и универсальный инструмент для разработки пользовательских интерфейсов. Во многих современных играх AAA используют Adobe Flash для создания пользовательского интерфейса; среди этих игр - Crysis 2, Dead Space 2, Mass Effect 2 и Counter-Strike: Global Offensive. Из этого туториала вы узнаете, как настроить собственный интерфейс Adobe Flash для его использования с Source SDK. Однако этот метод потребует некоторых исправлений исходного кода.
 
 
Помните, что интерфейс, который мы будем реализовывать, не оптимизирован для игр. Однако, используя тот же способ, можно так же легко использовать профессиональное Flash-решение, такое как Autodesk Scaleform, или веб-интерфейс, такой как Awesomium. Также имейте в виду, что код, который мы напишем здесь, не соответствует стандартам игровой индустрии; если вы это используете для профессионального мода, вы можете повеселиться, всячески исправляя исходный код.
 
 
Пример использования Adobe Flash в Source SDK мы можете найти здесь: [http://www.youtube.com/watch?v=FGpy1UfTJWg Flash Implementation Video]
 
 
== Предисловия ==
 
Сначала вам нужно будет скачать копию [http://code.google.com/p/flash-to-directx/ FlashDX]. Flash-To-DirectX действительно поможет ускорить процесс внедрения пользовательских Flash-интерфейсов. Имейте в виду, что FlashDX содержит только COM-компонент Flash; Это решение не оптимизировано для игр и значительно снизит частоту кадров при использовании сложных flash-файлов. Если скорость и совместимость для вас на первом месте, тщательно изучите способ внедрения, но используйте Scaleform или нечто подобное вместо FlashDX.
 
 
Затем загрузите версию DirectX SDK, выпущенную в июне 2010 года. В более новых версиях может не хватать некоторых возможностей, поэтому, чтобы избавиться от проблем при отладке, установите версию 2010 года выпуска.
 
 
== Начинаем ==
 
Я немного изменил свою копию Flash DX; Я избавился от механизма прозрачности и заменил его собственным алгоритмом написания цветных ключей, также я написал метод для рендеринга Flash-объектов в сетке и написал метод для создания спроецированной флэш-матрицы. Однако для данного руководства нам вполне хватит немодифицированной версии FlashDX. Скомпилируйте FlashDX в многопоточную (/ MT) статическую библиотеку (в режиме выпуска). Если вы не измените тип потоков, то компоновщик выдаст много странных ошибок.
 
 
Затем переключитесь на ваш мод Source SDK. Перейдите в «Проект» > «Свойства клиентского SDK» (лично я использую Visual Studio 2012; аналогичным образом все работает в VS 2010 - просто щелкните правой кнопкой мыши фильтр проекта Client SDK и выберите «Свойства»). В дереве выберите узел «Каталоги VC++». Добавьте библиотеки FlashDX и DirectX SDK и включите их в свои каталоги VC++. Надеюсь, мне не нужно вам по шагам объяснять, как это сделать.
 
 
== The Client-side Code ==
 
Once you have everything linked, you are now ready to start coding. I wrote a few basic classes to get you started, but I suggest writing your own if you need more control; the important thing here is the hack to get the whole Flash system working. I come from a C# .NET background so the way I name variables may be different. I occasionally try to use C++ naming conventions too.
 
 
Anyway, start by creating two new files in your Client project: FlashManager.h and FlashManager.cpp
 
 
We're going to need a few includes for the Flash Manager class that we are about to create. The class will allow us to load SWF files, catch Flash commands from a custom env_flash map entity, and allow us to play, scale, and render Flash frames. Begin the FlashManager.h file with the following:
 
 
<syntaxhighlight lang="cpp">
 
#pragma once
 
 
#include <Windows.h>
 
#include <TCHAR.h>
 
#include <Shlwapi.h>
 
#include <d3d9.h>
 
#include <d3dx9.h>
 
#include <d3dx9math.h>
 
#include <d3dx9mesh.h>
 
#include <ddraw.h>
 
#include <string>
 
#include <vector>
 
#include "IFlashDX.h"
 
#include "ASInterface.h"
 
#include <GameEventListener.h>
 
</syntaxhighlight>
 
 
You may have noticed that the implementation is highly Windows based right from the get go with <code>#include <Windows.h></code>. This is only on the client side. If cross-platform compatibility is very important to you, then I'll leave you the work of finding *nix equivalents of various Windows functions. Note that you will also have to find something other than FlashDX as well (look into GameSWF). In this tutorial, I assume you are writing a Source SDK mod for the Windows OS. The server project remains cross-platform so that we may build *.so libraries for dedicated Linux servers in the future.
 
 
Next, we need a method of storing Flash movie information. Let's create a <code>struct</code> to store basic SWF movie information.
 
<syntaxhighlight lang="cpp">
 
// This object acts as the Flash Container by loading SWF files and rendering textures
 
struct FlashMovie
 
{
 
private:
 
int m_height, m_width, m_x, m_y;
 
std::string m_path;
 
bool init;
 
 
public:
 
// Creates a FlashMovie container object. If Width and Height are not specified, the game window's width and height are used.
 
FlashMovie(std::string SWFPath, int Width = -1, int Height = -1, int screen_x = 0, int screen_y = 0);
 
 
// Destroys and unloads the Flash movie
 
~FlashMovie();
 
 
// Returns the flash movie absolute file path
 
std::string GetPath();
 
 
// Gets the user-defined flash movie width
 
int GetWidth();
 
 
// Gets the user-defined flash movie height
 
int GetHeight();
 
 
// Returns the x and y coordinates of the default screen position.
 
std::pair<int, int> GetScreenPosition();
 
 
// Sets the position of the movie on the screen
 
void SetScreenPosition(uint x, uint y);
 
 
 
// -----------------------------------------------------------------
 
// The following in general should not be used by the end user.
 
// -----------------------------------------------------------------
 
 
// Resizes the FlashMovie to the specified width and height given a IFlashDXPlayer pointer. Use FlashManager's ResizeMovie instead of this.
 
void ResizeMovie(int Width, int Height, IFlashDXPlayer* m_flashPlayer);
 
};
 
</syntaxhighlight>
 
 
Next, we need a way of managing multiple SWF files. For example, we may have separate SWF files for the title screen, in-game UI, server browser, etc. To manage them, we would need to create a FlashManager class:
 
 
<syntaxhighlight lang="cpp">
 
class FlashManager : public CGameEventListener
 
{
 
private:
 
// Managment objects
 
std::vector<FlashMovie> m_movieArray;
 
int playingFlashFile, lastFlashIndex, w, h;
 
bool recreatedTargets;
 
bool enableInputProcessing;
 
bool screenBlend;
 
 
// D3D classes
 
D3DXVECTOR2 Translation;
 
D3DXMATRIX Mat;
 
D3DXVECTOR2 Scaling;
 
LPD3DXSPRITE Sprite;
 
D3DSURFACE_DESC Desc;
 
std::pair<int,int> currentPos;
 
 
// Flash objects
 
IFlashDX* m_flashDX;
 
IFlashDXPlayer* m_flashPlayer;
 
ASInterface* m_playerASI;
 
 
private:
 
bool bCompare(const BYTE* pData, const BYTE* bMask, const char* szMask);
 
DWORD FindPattern(DWORD dwAddress, DWORD dwLen, BYTE* bMask,char* szMask);
 
void* DetourFunction(BYTE* source, const BYTE* destination, const int length);
 
 
public:
 
FlashManager();
 
~FlashManager();
 
 
// Adds a flash movie to the list and returns the index. Returns -1 if the renderer has not loaded completely.
 
int AddFlashMovie(FlashMovie fl_movie);
 
 
// Plays a flash movie provided an index. Use with AddFlashMovie()
 
void PlayFlashMovie(int index);
 
 
// Returns the index of the current movie that is playing, or -1 if no movie is playing.
 
int GetPlayingMovieIndex();
 
 
// Returns true if a flash movie is currently playing.
 
bool IsPlayingMovie();
 
 
// Hooks an ActionScript FSCommand with your function. That is, whenever fscommand() is called in flash, that FSCommand is registered with your c++ function.
 
void HookFSCommand(std::string, void (*)(const wchar_t*));
 
 
// Resizes a FlashMovie given the flash movie index.
 
void ResizeMovie(int index, int Width, int Height);
 
 
// Forces the playing flash movie to skip to the specified frame.
 
void ChangeFrame(int frame);
 
 
// Gets the rendered screen location of the flash movie.
 
std::pair<int,int> GetCurrentPos();
 
 
// Enables or disables input processing through the hijacked message pump. In other words, if input processing is disabled, every mouse/keyboard input
 
// is sent only to Flash instead of both the game and Flash.
 
void SetGameInputProcessing(bool enableInput);
 
 
// Returns true if the game's input systems are processing.
 
bool IsGameInputProcessing();
 
 
// Unloads the flash movie and resets flash-related variables.
 
void StopPlayingMovie();
 
 
// Returns the last played movie's index
 
int GetLastIndex();
 
 
// Returns the index of the specified movie if it has already been added, or -1 if it has not
 
int IsMovieInList(const char* filename);
 
 
// Sets blend mode (true = photoshop-like Screen filter, false = opaque)
 
void SetScreenBlending(bool enableScreen);
 
 
// Returns the scale factor
 
D3DXVECTOR2 GetScale();
 
 
// -----------------------------------------------------------------
 
// The following in general should not be used by the programmer.
 
// -----------------------------------------------------------------
 
 
// Returns true if the Flash manager has initialized all of its variables and has attained a pointer to the hacked D3D device
 
bool HasRecreatedTargets();
 
 
// Recreates D3D targets and assigns the d3d device pointer through the vtable hack
 
bool RecreateTargets(IDirect3DDevice9* pD3DDevice);
 
 
// Renders the flash movie onto the screen utilizing the direct 3d device pointer. Note: do not use, as it is used internally.
 
void SceneDataHook();
 
 
// Returns the d3d device pointer (in case you need it for your other projects).
 
IDirect3DDevice9* GetD3DDevice();
 
 
// Returns the IFlashDXPlayer pointer to utilize FlashDX manually.
 
IFlashDXPlayer* GetFlashPlayer();
 
 
// Fires a flash related event (either render_flash_hud or flash_hud_toggle)
 
void FireGameEvent(IGameEvent* event);
 
};
 
</syntaxhighlight>
 
 
Alright, so we got the header stuff down. If the fact that I put more than one class into a header file annoys you, you can split up the classes into different headers. It's more fun to keep things simple and put both relevant classes into one header.
 
 
Next, it's time to go into the meat of the code. Add the following includes to your FlashManager.cpp. Note that I may have some extra headers because I coded a deeper implementation than the one covered in this tutorial.
 
 
<syntaxhighlight lang="cpp">
 
#include "cbase.h"
 
 
#include "FlashManager.h"
 
 
#include <Windows.h>
 
#include <TCHAR.h>
 
#include <Shlwapi.h>
 
#include <d3d9.h>
 
#include <d3dx9.h>
 
#include <d3dx9math.h>
 
#include <d3dx9mesh.h>
 
#include <ddraw.h>
 
#include <string>
 
#include <vector>
 
 
#include "ienginevgui.h"
 
#include <vgui/isurface.h>
 
#include <vgui/IVGui.h>
 
#include <vgui/IInput.h>
 
#include <vgui_controls/Panel.h>
 
#include <tier3/tier3.h>
 
</syntaxhighlight>
 
 
Next, some global variables, defines, and functions:
 
 
<syntaxhighlight lang="cpp">
 
// This will store the HWND of the game window later
 
HWND hwndMain;
 
 
// Some macros to simply things later
 
 
#ifndef MSGBOX_FLASH
 
#define MSGBOX(text) MessageBoxA(hwndMain, text, "Mutation:Source", MB_OK);
 
#endif
 
 
#ifndef MSGBOX_NULL
 
#define MSGBOXN(text) MessageBoxA(NULL, text, "Mutation:Source", MB_OK);
 
#endif
 
 
#ifndef GET_X_LPARAM
 
#define GET_X_LPARAM(lp)                        ((int)(short)LOWORD(lp))
 
#endif
 
 
#ifndef GET_Y_LPARAM
 
#define GET_Y_LPARAM(lp)                        ((int)(short)HIWORD(lp))
 
#endif
 
 
// Displays a MessageBox
 
void msgf(char* fmt, ...)
 
{
 
    va_list args;
 
    va_start(args, fmt);
 
    char str_buffer[512];
 
    vsprintf(str_buffer, fmt, args);
 
    MSGBOX(str_buffer);
 
    va_end(args);
 
}
 
 
// Hooks
 
HRESULT APIENTRY d3dSceneHook(LPDIRECT3DDEVICE9 pDevice);
 
HRESULT (APIENTRY *SceneObject)(LPDIRECT3DDEVICE9 pDevice);
 
 
// Variables for WndProc hijacking
 
WNDPROC OldWndProc;
 
void InitWinProcHook();
 
LRESULT CALLBACK myWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
 
 
// Flash and other D3D globals; would be nice to have all these variables in one place at the top
 
FlashManager* g_FlashManager = NULL;
 
IDirect3DTexture9* g_Texture = NULL;
 
IDirect3DDevice9* g_pD3DDevice = NULL;
 
 
// A small helper function to convert a string to a wide string
 
std::wstring StringToWString(const std::string& s)
 
{
 
    std::wstring temp(s.length(),L' ');
 
    std::copy(s.begin(), s.end(), temp.begin());
 
    return temp;
 
}
 
 
// Flash and D3D texture related vars
 
const IFlashDXPlayer::ETransparencyMode transparency_mode = IFlashDXPlayer::TMODE_OPAQUE;
 
const int num_textures_in_rotation = 2;
 
IDirect3DTexture9* g_texturesRotation[num_textures_in_rotation] = { NULL };
 
int g_currentTexture = 0;
 
</syntaxhighlight>
 
 
The <code>MSGBOX_FLASH</code> and <code>MSGBOX_NULL</code> macros are there for debugging purposes. You can remove the references to these macros if you so desire.
 
 
Alrightie then, we have now (messily) defined our global variables and functions. Feel free to organize them for clarity. Next, it's time to define the various FlashMovie functions. Note that our FlashMovie struct is solely there to store SWF information.
 
 
<syntaxhighlight lang="cpp">
 
FlashMovie::FlashMovie(std::string SWFPath, int Width, int Height, int screen_x, int screen_y)
 
{
 
int w, h; engine->GetScreenSize(w, h);
 
if (Width == -1) Width = w;
 
if (Height == -1) Height = h;
 
 
SWFPath = SWFPath.insert(0, "\\");
 
SWFPath = SWFPath.insert(0, engine->GetGameDirectory());
 
 
char* sz = const_cast<char*>(SWFPath.c_str());
 
 
m_width = Width;
 
m_height = Height;
 
m_path = SWFPath;
 
m_x = screen_x;
 
m_y = screen_y;
 
 
init = false;
 
}
 
 
std::string FlashMovie::GetPath()
 
{
 
return m_path;
 
}
 
 
int FlashMovie::GetWidth()
 
{
 
return m_width;
 
}
 
 
int FlashMovie::GetHeight()
 
{
 
return m_height;
 
}
 
 
FlashMovie::~FlashMovie()
 
{
 
 
}
 
 
std::pair<int, int> FlashMovie::GetScreenPosition()
 
{
 
return std::pair<int, int>(m_x, m_y);
 
}
 
 
void FlashMovie::ResizeMovie(int Width, int Height, IFlashDXPlayer* m_flashPlayer)
 
{
 
int w = -1, h = -1;
 
engine->GetScreenSize(w, h);
 
 
if (Width == -1) Width = w;
 
if (Height == -1) Height = h;
 
 
m_width = Width;
 
m_height = Height;
 
 
if (m_flashPlayer)
 
m_flashPlayer->ResizePlayer(Width, Height);
 
}
 
 
void FlashMovie::SetScreenPosition(uint x, uint y)
 
{
 
m_x = x;
 
m_y = y;
 
}
 
</syntaxhighlight>
 
 
The FlashManager class will manage FlashMovie objects and also do the hacking. It would be much more efficient to separate the hacking functions and methods to a different class (so they can be reused for other purposes), but for this tutorial, I will stick them into the FlashManager class for simplicity. Just keep in mind that we will end up creating a very amateurish implementation-- on the bright side, it's easy to understand.
 
 
<syntaxhighlight lang="cpp">
 
// Don't worry about how these functions work. I wish I knew who made them-- I found them over at MPGH
 
 
bool FlashManager::bCompare(const BYTE* pData, const BYTE* bMask, const char* szMask)
 
{
 
for(;*szMask;++szMask,++pData,++bMask)
 
if(*szMask=='x' && *pData!=*bMask )
 
return false;
 
return (*szMask) == NULL;
 
}
 
 
DWORD FlashManager::FindPattern(DWORD dwAddress, DWORD dwLen, BYTE* bMask,char* szMask)
 
{
 
for(DWORD i=0; i < dwLen; i++)
 
if( bCompare( (BYTE*)( dwAddress+i ),bMask,szMask) )
 
return (DWORD)(dwAddress+i);
 
return 0;
 
}
 
 
void* FlashManager::DetourFunction(BYTE* source, const BYTE* destination, const int length)
 
{
 
BYTE* jmp = (BYTE*)malloc(length + 5);
 
DWORD dwBack;
 
 
VirtualProtect(source, length, PAGE_EXECUTE_READWRITE, &dwBack);
 
 
memcpy(jmp, source, length);
 
jmp += length;
 
 
jmp[0] = 0xE9;
 
*(DWORD *)(jmp + 1) = (DWORD)(source + length - jmp) - 5;
 
 
source[0] = 0xE9;
 
*(DWORD*)(source + 1) = (DWORD)(destination - source) - 5;
 
 
for(int i = 5; i < length; i++)
 
{
 
source[i] = 0x90;
 
}
 
 
VirtualProtect(source, length, dwBack, &dwBack);
 
 
return (jmp - length);
 
}
 
</syntaxhighlight>
 
 
When the FlashManager object is created, we will initiate the hack. This is a bad way to go about it, but it works for our purposes.
 
<syntaxhighlight lang="cpp">
 
FlashManager::FlashManager()
 
{
 
engine->GetScreenSize(w, h);
 
 
// Flash init
 
m_flashDX = GetFlashToDirectXInstance();
 
m_flashPlayer = m_flashDX->CreatePlayer(w, h);
 
 
if (!m_flashPlayer)
 
{
 
MessageBox(NULL, "Flash Player failed to initialize.", "Error", MB_OK);
 
abort();
 
}
 
 
m_playerASI = new ASInterface(m_flashPlayer);
 
 
// DirectX hack
 
DWORD ES_Address;
 
DWORD* vtbl = 0;
 
DWORD table = FindPattern((DWORD)GetModuleHandle("d3d9.dll"), 0x128000, (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", "xx????xx????xx");
 
memcpy(&vtbl, (void*)(table+2), 4);
 
ES_Address = vtbl[42];   
 
if (ES_Address)
 
SceneObject = (HRESULT (WINAPI *)(LPDIRECT3DDEVICE9 pDevice))(DetourFunction((PBYTE)ES_Address, (PBYTE)d3dSceneHook, 5));
 
else
 
{
 
MSGBOX("Error: Could not detour D3D9 EndScene() hook which is needed for Flash initialization.");
 
abort();
 
}
 
 
// Set up default matrix
 
Translation.x = 0;
 
Translation.y = 0;
 
currentPos.first = 0;
 
currentPos.second = 0;
 
Scaling.x = 1.0f;
 
Scaling.y = 1.0f;
 
D3DXMatrixTransformation2D(&Mat, NULL, 0, &Scaling, NULL, 0, &Translation);
 
 
// Flash manager data
 
playingFlashFile = -1;
 
lastFlashIndex = -1;
 
recreatedTargets = false;
 
screenBlend = false;
 
 
// Flash events
 
ListenForGameEvent("flash_hud_toggle");
 
ListenForGameEvent("render_flash_hud");
 
 
// WinProc hack
 
enableInputProcessing = true;
 
InitWinProcHook();
 
}
 
</syntaxhighlight>
 
 
Later, in the server section, we will create a <code>env_flash</code> entity. In order for the flash manager to communicate with the flash entity, we will need to capture networked events.
 
<syntaxhighlight lang="cpp">
 
void FlashManager::FireGameEvent(IGameEvent* event)
 
{
 
const char* eventName = event->GetName();
 
 
if (FStrEq(eventName, "flash_hud_toggle"))
 
{
 
if (event->GetBool("enable"))
 
PlayFlashMovie(GetLastIndex());
 
else
 
StopPlayingMovie();
 
}
 
else if (FStrEq(eventName, "render_flash_hud"))
 
{
 
int flashIndex = -1;
 
if ((flashIndex = g_FlashManager->IsMovieInList(event->GetString("file"))) == -1)
 
{
 
FlashMovie renderMovie(event->GetString("file"), event->GetInt("width"), event->GetInt("height"), event->GetInt("x"), event->GetInt("y"));
 
flashIndex = g_FlashManager->AddFlashMovie(renderMovie);
 
}
 
else
 
{
 
// Update the old movie parameters
 
if (m_movieArray[flashIndex].GetHeight() != event->GetInt("height") || m_movieArray[flashIndex].GetWidth() != event->GetInt("width"))
 
ResizeMovie(flashIndex, event->GetInt("width"), event->GetInt("height"));
 
 
m_movieArray[flashIndex].SetScreenPosition(event->GetInt("x"), event->GetInt("y"));
 
}
 
 
SetScreenBlending(event->GetBool("blend"));
 
 
PlayFlashMovie(flashIndex);
 
}
 
}
 
</syntaxhighlight>
 
 
Because the client DLL loads long after DirectX is initialized, we will need to set up our DirectX variables mid-render.
 
<syntaxhighlight lang="cpp">
 
bool FlashManager::RecreateTargets(IDirect3DDevice9* pD3DDevice)
 
{
 
HRESULT hr;
 
 
int movieIndex = GetPlayingMovieIndex();
 
int newWidth = -1, newHeight = -1;
 
 
if (movieIndex > -1)
 
{
 
newWidth = m_movieArray[movieIndex].GetWidth();
 
newHeight = m_movieArray[movieIndex].GetHeight();
 
}
 
else
 
{
 
newWidth = w;
 
newHeight = h;
 
}
 
 
hr = pD3DDevice->CreateTexture(newWidth, newHeight, 1, 0, transparency_mode ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &g_Texture, NULL);
 
if (FAILED(hr))
 
return false;
 
 
D3DXCreateSprite(pD3DDevice, &Sprite);
 
g_pD3DDevice = pD3DDevice;
 
 
if (m_flashPlayer)
 
m_flashPlayer->ResizePlayer(newWidth, newHeight);
 
 
for (int i = 0; i < num_textures_in_rotation; ++i)
 
{
 
hr = pD3DDevice->CreateTexture(newWidth, newHeight, 1, 0,
 
transparency_mode ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &g_texturesRotation[i], NULL);
 
 
if (FAILED(hr))
 
return false;
 
}
 
 
recreatedTargets = true;
 
 
return true;
 
}
 
</syntaxhighlight>
 
 
The following is the code that draws our Flash file as a sprite on the screen.
 
<syntaxhighlight lang="cpp">
 
void FlashManager::SceneDataHook()
 
{
 
if (!HasRecreatedTargets())
 
{
 
assert("Tried to use SceneDataHook without initialization of texture buffer");
 
return;
 
}
 
 
if (!IsPlayingMovie())
 
return;
 
 
HRESULT hr;
 
 
// Update flash movie as necessary
 
unsigned int numDirtyRects; const RECT* dirtyRects;
 
if (m_flashPlayer->IsNeedUpdate(NULL, &dirtyRects, &numDirtyRects))
 
{
 
IDirect3DTexture9* pTexToUpdate = g_texturesRotation[g_currentTexture];
 
if (++g_currentTexture == num_textures_in_rotation)
 
g_currentTexture = 0;
 
 
IDirect3DSurface9* pSrcSurface;
 
hr = pTexToUpdate->GetSurfaceLevel(0, &pSrcSurface);
 
assert(SUCCEEDED(hr));
 
 
HDC surfaceDC;
 
hr = pSrcSurface->GetDC(&surfaceDC);
 
assert(SUCCEEDED(hr));
 
 
// Draw flash frame
 
m_flashPlayer->DrawFrame(surfaceDC);
 
 
hr = pSrcSurface->ReleaseDC(surfaceDC);
 
assert(SUCCEEDED(hr));
 
 
// Update our GUI texture
 
IDirect3DSurface9* pDestSurface;
 
hr = g_Texture->GetSurfaceLevel(0, &pDestSurface);
 
assert(SUCCEEDED(hr));
 
 
for (unsigned int i = 0; i < numDirtyRects; ++i)
 
{
 
POINT destPoint = { dirtyRects[i].left, dirtyRects[i].top };
 
hr = g_pD3DDevice->UpdateSurface(pSrcSurface, dirtyRects + i, pDestSurface, &destPoint);
 
assert(SUCCEEDED(hr));
 
}
 
 
pDestSurface->Release();
 
pSrcSurface->Release();
 
}
 
 
// Render our flash file
 
if (g_Texture)
 
{
 
// Generate our new position matrix
 
currentPos = m_movieArray[playingFlashFile].GetScreenPosition();
 
Translation.x = currentPos.first;
 
Translation.y = currentPos.second;
 
g_Texture->GetLevelDesc(0, &Desc);
 
Scaling.x = w / (float)m_movieArray[playingFlashFile].GetWidth() /*/ (float)Desc.Width*/;
 
Scaling.y = h / (float)m_movieArray[playingFlashFile].GetHeight() /*/ (float)Desc.Height*/;
 
D3DXMatrixTransformation2D(&Mat, NULL, 0, &Scaling, NULL, 0, &Translation);
 
 
DWORD oldBlendOP, DestBlend, SrcBlend;
 
if (screenBlend)
 
{
 
// Store old
 
g_pD3DDevice->GetRenderState(D3DRS_BLENDOP, &oldBlendOP);
 
g_pD3DDevice->GetRenderState(D3DRS_DESTBLEND, &DestBlend);
 
g_pD3DDevice->GetRenderState(D3DRS_SRCBLEND, &SrcBlend);
 
 
// Set new; photoshop-like Screen filter. Remove the black background
 
g_pD3DDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
 
g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
 
g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE );
 
}
 
 
// Render sprite
 
Sprite->Begin(0);
 
Sprite->SetTransform(&Mat);
 
Sprite->Draw(g_Texture, NULL, NULL, NULL, 0xFFFFFFFF);
 
Sprite->End();
 
 
// Reset blending
 
if (screenBlend)
 
{
 
g_pD3DDevice->SetRenderState(D3DRS_BLENDOP, oldBlendOP);
 
g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, DestBlend);
 
g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, SrcBlend);
 
}
 
}
 
}
 
</syntaxhighlight>
 
 
The rest of the FlashManager functions are easily understandable:
 
<syntaxhighlight lang="cpp">
 
void FlashManager::PlayFlashMovie(int index)
 
{
 
if (index >= m_movieArray.size() || index < 0)
 
{
 
assert("Bad index");
 
return;
 
}
 
 
FlashMovie movie = m_movieArray[index];
 
 
bool loaded = m_flashPlayer->LoadMovie(StringToWString(movie.GetPath()).c_str());
 
m_flashPlayer->SetTransparencyMode(transparency_mode);
 
m_flashPlayer->SetBackgroundColor(RGB(0, 0, 0));
 
 
if (!loaded)
 
{
 
std::string error = "Could not load flash file: \n";
 
error += m_movieArray[index].GetPath();
 
MSGBOX(error.c_str());
 
assert("Flash load error");
 
abort();
 
}
 
 
playingFlashFile = index;
 
lastFlashIndex = index;
 
}
 
 
void FlashManager::SetScreenBlending(bool enableScreen)
 
{
 
screenBlend = enableScreen;
 
}
 
 
IDirect3DDevice9* FlashManager::GetD3DDevice()
 
{
 
return g_pD3DDevice;
 
}
 
 
FlashManager::~FlashManager()
 
{
 
delete m_playerASI;
 
m_flashDX->DestroyPlayer(m_flashPlayer);
 
}
 
 
bool FlashManager::HasRecreatedTargets()
 
{
 
return recreatedTargets;
 
}
 
 
int FlashManager::IsMovieInList(const char* filename)
 
{
 
for (int i = 0; i < m_movieArray.size(); i++)
 
{
 
if (m_movieArray[i].GetPath().compare(filename) == 0)
 
return i;
 
}
 
 
return -1;
 
}
 
 
int FlashManager::GetPlayingMovieIndex()
 
{
 
return playingFlashFile;
 
}
 
 
int FlashManager::GetLastIndex()
 
{
 
return lastFlashIndex;
 
}
 
 
bool FlashManager::IsPlayingMovie()
 
{
 
return (playingFlashFile > -1);
 
}
 
 
int FlashManager::AddFlashMovie(FlashMovie fl_movie)
 
{
 
m_movieArray.push_back(fl_movie);
 
return m_movieArray.size()-1;
 
}
 
 
void FlashManager::HookFSCommand(std::string fscommand, void (*func)(const wchar_t* args))
 
{
 
m_playerASI->AddFSCCallback(StringToWString(fscommand), func);
 
}
 
 
void FlashManager::ResizeMovie(int index, int Width, int Height)
 
{
 
if (index >= m_movieArray.size() || index < 0)
 
{
 
assert("Bad index");
 
return;
 
}
 
 
m_movieArray[index].ResizeMovie(Width, Height, m_flashPlayer);
 
}
 
 
void FlashManager::ChangeFrame(int frame)
 
{
 
if (!IsPlayingMovie())
 
return;
 
 
m_flashPlayer->CallFrame(frame);
 
}
 
 
IFlashDXPlayer* FlashManager::GetFlashPlayer()
 
{
 
return m_flashPlayer;
 
}
 
 
std::pair<int,int> FlashManager::GetCurrentPos()
 
{
 
return currentPos;
 
}
 
 
void FlashManager::SetGameInputProcessing(bool enableInput)
 
{
 
enableInputProcessing = enableInput;
 
}
 
 
bool FlashManager::IsGameInputProcessing()
 
{
 
return enableInputProcessing;
 
}
 
 
void FlashManager::StopPlayingMovie()
 
{
 
m_flashPlayer->StopPlaying();
 
m_flashPlayer->LoadMovie(L"NULL");
 
playingFlashFile = -1;
 
}
 
 
D3DXVECTOR2 FlashManager::GetScale()
 
{
 
return Scaling;
 
}
 
</syntaxhighlight>
 
 
To complete our FlashManager.cpp file, the hacked hooks:
 
<syntaxhighlight lang="cpp">
 
HRESULT APIENTRY d3dSceneHook(LPDIRECT3DDEVICE9 pDevice)
 
{
 
if (g_FlashManager)
 
{
 
if (g_FlashManager->IsPlayingMovie())
 
{
 
if (!g_FlashManager->HasRecreatedTargets())
 
g_FlashManager->RecreateTargets(pDevice);
 
 
g_FlashManager->SceneDataHook();
 
}
 
}
 
 
return SceneObject(pDevice);
 
}
 
 
// A method to grab the parent HWND from our DLL
 
BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam)
 
{
 
    HINSTANCE hinst=(HINSTANCE)GetModuleHandle(NULL);
 
 
    if((HINSTANCE)GetWindowLongPtr(hwnd, GWL_HINSTANCE)==hinst &&
 
        IsWindowVisible(hwnd))
 
    {
 
        hwndMain=hwnd;
 
        return FALSE;
 
    }
 
    else
 
        return TRUE;
 
}
 
 
void InitWinProcHook()
 
{
 
hwndMain = NULL;
 
EnumWindows(EnumWindowProc, 0);
 
if (hwndMain == NULL)
 
{
 
MSGBOX("Failed to find the parent HWND. Tell a programmer");
 
abort();
 
}
 
 
OldWndProc = (WNDPROC)SetWindowLong(hwndMain, GWL_WNDPROC, (LONG)myWndProcHook);
 
}
 
 
LRESULT CALLBACK myWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 
{
 
if (g_FlashManager)
 
{
 
IFlashDXPlayer* g_flashPlayer = g_FlashManager->GetFlashPlayer();
 
bool enableinputs = g_FlashManager->IsGameInputProcessing();
 
 
if (!g_FlashManager->IsPlayingMovie())
 
return CallWindowProc(OldWndProc, hWnd, message, wParam, lParam);
 
 
int mousex = GET_X_LPARAM(lParam);
 
int mousey = GET_Y_LPARAM(lParam);
 
if (g_FlashManager->IsPlayingMovie())
 
{
 
std::pair<int,int> currentPos = g_FlashManager->GetCurrentPos();
 
mousex -= currentPos.first;
 
mousey -= currentPos.second;
 
 
D3DXVECTOR2 scale = g_FlashManager->GetScale();
 
mousex /= scale.x;
 
mousey /= scale.y;
 
 
if (mousex < 0) mousex = 0;
 
if (mousey < 0) mousey = 0;
 
}
 
 
switch (message)
 
{
 
case WM_MOUSEMOVE:
 
if (g_flashPlayer)
 
g_flashPlayer->SetMousePos(mousex, mousey);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_LBUTTONDOWN:
 
if (g_flashPlayer)
 
g_flashPlayer->SetMouseButtonState(mousex, mousey, IFlashDXPlayer::eMouse1, true);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_LBUTTONUP:
 
if (g_flashPlayer)
 
g_flashPlayer->SetMouseButtonState(mousex, mousey, IFlashDXPlayer::eMouse1, false);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_KEYDOWN:
 
if (g_flashPlayer)
 
g_flashPlayer->SendKey(true, (int)wParam, (int)lParam);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_KEYUP:
 
if (g_flashPlayer)
 
g_flashPlayer->SendKey(false, (int)wParam, (int)lParam);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_CHAR:
 
if (g_flashPlayer)
 
g_flashPlayer->SendChar((int)wParam, (int)lParam);
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_SIZE:
 
if (g_flashPlayer)
 
{
 
 
}
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 0;
 
 
case WM_SETCURSOR:
 
if (g_flashPlayer)
 
{
 
static bool restoreCursor = true;
 
if (LOWORD(lParam) != HTCLIENT)
 
restoreCursor = true;
 
 
if (restoreCursor)
 
{
 
restoreCursor = false;
 
break;
 
}
 
return (enableinputs) ? CallWindowProc(OldWndProc, hWnd, message, wParam, lParam) : 1;
 
}
 
break;
 
default:
 
break;
 
}
 
}
 
 
return CallWindowProc(OldWndProc, hWnd, message, wParam, lParam);
 
}
 
</syntaxhighlight>
 
 
Alright, we now have a FlashManager class with hacked access to the D3D device and WndProc. We are nearly done. All we have to do next is initialize our global flashmanager object. Open vgui_int.cpp and add the following under <code>using namespace vgui</code>:
 
<syntaxhighlight lang="cpp">
 
#include "FlashManager.h"
 
#include <GameEventListener.h>
 
</syntaxhighlight>
 
 
Add this under <code>#include "tier0/memdbgon.h"</code>:
 
<syntaxhighlight lang="cpp">
 
// This is our global flash movie manager
 
extern FlashManager* g_FlashManager;
 
</syntaxhighlight>
 
 
In the VGui_Startup function, add this before <code>return true</code>:
 
<syntaxhighlight lang="cpp">
 
// Init Flash manager
 
g_FlashManager = new FlashManager();
 
 
// Hook some global commands which will be used with our env_flash entity later.
 
g_FlashManager->HookFSCommand("OnFSCommand1", FSCMD1);
 
g_FlashManager->HookFSCommand("OnFSCommand2", FSCMD2);
 
g_FlashManager->HookFSCommand("OnFSCommand3", FSCMD3);
 
g_FlashManager->HookFSCommand("OnFSCommand4", FSCMD4);
 
 
// EXAMPLE title screen hooks. Any time fscommand() is used in ActionScript with the first parameter, the second parameter function is called.
 
g_FlashManager->HookFSCommand("MSGUI_FASTCMD_QUICKMATCH", FSCMD_QuickMatch);
 
 
// Load our Flash Title Screen menu
 
// First check if the command-line contains a map
 
std::string cmdline = GetCommandLine();
 
if (cmdline.find(" map ") == std::string::npos)
 
{
 
// I created a flash folder (in the mod folder-- where the models, sound, addons, etc. folders are)
 
FlashMovie titleScreen("flash\\ui\\title.swf", 1366, 768);
 
g_FlashManager->PlayFlashMovie(g_FlashManager->AddFlashMovie(titleScreen));
 
}
 
</syntaxhighlight>
 
 
Next, delete the pointer when the game is exiting. Add this under <code>vgui::ivgui()->RunFrame();</code>
 
<syntaxhighlight lang="cpp">
 
// Remove flash manager object
 
delete g_FlashManager;
 
g_FlashManager = 0;
 
</syntaxhighlight>
 
 
Add the following functions at the top somewhere:
 
<syntaxhighlight lang="cpp">
 
void FSCMD1(const wchar_t*)
 
{
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("fscommand_one");
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
void FSCMD2(const wchar_t*)
 
{
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("fscommand_two");
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
void FSCMD3(const wchar_t*)
 
{
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("fscommand_three");
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
void FSCMD4(const wchar_t*)
 
{
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("fscommand_four");
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
// EXAMPLE function
 
void FSCMD_QuickMatch(const wchar_t*)
 
{
 
// Stop our title screen from playing
 
g_FlashManager->StopPlayingMovie();
 
 
// Now load our example map
 
engine->ExecuteClientCmd("map flash_movie_example");
 
}
 
</syntaxhighlight>
 
 
Now, everything in vgui_int.cpp is the actual usage of our FlashManager class. You interact between your Flash file and the FlashManager through ActionScript FSCommands. You can link fscommands to c++ functions using <code>g_FlashManager->HookFSCommand(...)</code>. The four "OnFSCommand" stuff will actually be used by our server entity class next.
 
 
== The Server-side Code ==
 
 
We're almost done with the implementation. Create a new file in your Server SDK project called env_flash.cpp and add this:
 
<syntaxhighlight lang="cpp">
 
#include "cbase.h"
 
#include <GameEventListener.h>
 
 
class CFlashEnvironment : public CLogicalEntity, public CGameEventListener
 
{
 
private:
 
COutputEvent m_FSOne, m_FSTwo, m_FSThree, m_FSFour;
 
char* m_szFlashName;
 
bool m_IsEnabled, enableScreenBlend;
 
int m_xPos, m_yPos, m_Width, m_Height;
 
 
public:
 
DECLARE_CLASS( CFlashEnvironment, CLogicalEntity );
 
DECLARE_DATADESC();
 
 
// Constructor
 
CFlashEnvironment()
 
{
 
// make sure events are loaded
 
gameeventmanager->LoadEventsFromFile("resource/modevents.res");
 
 
// set defaults
 
m_szFlashName = "env_flash filename not set";
 
m_IsEnabled = false;
 
m_xPos = 0;
 
m_yPos = 0;
 
m_Width = -1;
 
m_Height = -1;
 
 
// build our FSCommand table
 
ListenForGameEvent("fscommand_one");
 
ListenForGameEvent("fscommand_two");
 
ListenForGameEvent("fscommand_three");
 
ListenForGameEvent("fscommand_four");
 
}
 
 
void FireGameEvent(IGameEvent* pEvent)
 
{
 
if (!strcmp("fscommand_one", pEvent->GetName()))
 
FSCommand1();
 
else if (!strcmp("fscommand_two", pEvent->GetName()))
 
FSCommand2();
 
else if (!strcmp("fscommand_three", pEvent->GetName()))
 
FSCommand3();
 
else if (!strcmp("fscommand_four", pEvent->GetName()))
 
FSCommand4();
 
}
 
 
// Output functions
 
void FSCommand1();
 
void FSCommand2();
 
void FSCommand3();
 
void FSCommand4();
 
 
// Input functions
 
void RenderFlashMovie( inputdata_t &inputData );
 
void StopMovie( inputdata_t &inputData );
 
void StartMovie( inputdata_t &inputData );
 
};
 
 
LINK_ENTITY_TO_CLASS( env_flash, CFlashEnvironment );
 
 
BEGIN_DATADESC( CFlashEnvironment  )
 
 
// Stores the name of the flash movie to play
 
DEFINE_KEYFIELD( m_szFlashName, FIELD_STRING, "file" ),
 
 
// Storage of whether or not our flash movie is "playing"
 
DEFINE_FIELD( m_IsEnabled, FIELD_BOOLEAN ),
 
 
// X and Y position on the screen
 
DEFINE_KEYFIELD( m_xPos, FIELD_INTEGER, "x" ),
 
DEFINE_KEYFIELD( m_yPos, FIELD_INTEGER, "y" ),
 
 
// Width and Height
 
DEFINE_KEYFIELD( m_Width, FIELD_INTEGER, "width" ),
 
DEFINE_KEYFIELD( m_Height, FIELD_INTEGER, "height" ),
 
 
// Blending
 
DEFINE_KEYFIELD( enableScreenBlend, FIELD_BOOLEAN, "blend" ),
 
 
// Links our input for rendering event
 
DEFINE_INPUTFUNC( FIELD_VOID, "RenderFlashMovie", RenderFlashMovie ),
 
DEFINE_INPUTFUNC( FIELD_VOID, "StopMovie", StopMovie ),
 
DEFINE_INPUTFUNC( FIELD_VOID, "StartMovie", StartMovie ),
 
 
// Links our output member to the output name used by Hammer
 
DEFINE_OUTPUT( m_FSOne, "OnFSCommand1" ),
 
DEFINE_OUTPUT( m_FSTwo, "OnFSCommand2" ),
 
DEFINE_OUTPUT( m_FSThree, "OnFSCommand3" ),
 
DEFINE_OUTPUT( m_FSFour, "OnFSCommand4" ),
 
 
END_DATADESC()
 
 
//-----------------------------------------------------------------------------
 
// Purpose: Fire a network event to change rendering HUD
 
//-----------------------------------------------------------------------------
 
void CFlashEnvironment::RenderFlashMovie( inputdata_t &inputData )
 
{
 
Msg("Starting render_flash_hud event with flash file %s", m_szFlashName);
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("render_flash_hud");
 
pEvent->SetString("file", m_szFlashName);
 
pEvent->SetInt("x", m_xPos);
 
pEvent->SetInt("y", m_yPos);
 
pEvent->SetInt("width", m_Width);
 
pEvent->SetInt("height", m_Height);
 
pEvent->SetBool("blend", enableScreenBlend);
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
void CFlashEnvironment::StopMovie( inputdata_t &inputData )
 
{
 
m_IsEnabled = false;
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("flash_hud_toggle");
 
pEvent->SetBool("enable", m_IsEnabled);
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
void CFlashEnvironment::StartMovie( inputdata_t &inputData )
 
{
 
m_IsEnabled = true;
 
IGameEvent* pEvent = gameeventmanager->CreateEvent("flash_hud_toggle");
 
pEvent->SetBool("enable", m_IsEnabled);
 
gameeventmanager->FireEvent(pEvent);
 
}
 
 
//-----------------------------------------------------------------------------
 
// Purpose: Fire Hammer events based on FSCommands
 
//-----------------------------------------------------------------------------
 
 
void CFlashEnvironment::FSCommand1()
 
{
 
m_FSOne.FireOutput(this, this);
 
}
 
 
void CFlashEnvironment::FSCommand2()
 
{
 
m_FSTwo.FireOutput(this, this);
 
}
 
 
void CFlashEnvironment::FSCommand3()
 
{
 
m_FSThree.FireOutput(this, this);
 
}
 
 
void CFlashEnvironment::FSCommand4()
 
{
 
m_FSFour.FireOutput(this, this);
 
}
 
</syntaxhighlight>
 
 
The env_flash entity is a simple logical entity that hooks <code>fscommand('OnFSCommandX')</code> where X is a number between 1-4. It fires linked outputs when the command is sent from the flash file. This is useful to the mapper for a variety of things: e.g. having the user play a minigame and sending OnFSCommand1 for victory or OnFSCommand2 for a loss and firing entities subsequently that decide the fate of the character is one example of its usage.
 
 
Anyway, in order for the entity to be accessible, you need to add the following to your mod's FGD file:
 
 
<syntaxhighlight lang="cpp">
 
@PointClass base(Targetname) = env_flash : "Manages flash UI."
 
[
 
file(string) : "File Name" : "" : "Path to flash file (e.g. flash\ui\title.swf)"
 
x(integer) : "X Position" : 0 : "Distance from the left of the screen"
 
y(integer) : "Y Position" : 0 : "Distance from the top of the screen"
 
width(integer) : "Width" : -1 : "Width. Use -1 to auto-detect"
 
height(integer) : "Height" : -1 : "Height. Use -1 to auto-detect"
 
blend(integer) : "Blending" : 1 : "Enables alpha blending (similar to photoshop's Screen blend mode), where black colors are made transparent"
 
 
// Inputs
 
input RenderFlashMovie(void) : "Loads and renders the SWF flash movie"
 
input StopMovie(void) : "Stops any playing SWF flash movie"
 
input StartMovie(void) : "Stops any playing SWF flash movie"
 
 
// Outputs
 
output OnFSCommand1(void) : "Called whenever the Flash ActionScript uses fscommand('OnFSCommand1');"
 
output OnFSCommand2(void) : "Called whenever the Flash ActionScript uses fscommand('OnFSCommand2');"
 
output OnFSCommand3(void) : "Called whenever the Flash ActionScript uses fscommand('OnFSCommand3');"
 
output OnFSCommand4(void) : "Called whenever the Flash ActionScript uses fscommand('OnFSCommand4');"
 
]
 
</syntaxhighlight>
 
 
And add the following events to your mod's modevents.res:
 
 
<syntaxhighlight lang="cpp">
 
"render_flash_hud"
 
{
 
"file" "string" // name of flash file to render
 
"x" "short" // x position of file to render
 
"y" "short" // y position of file to render
 
"width" "short" // width to render the SWF in
 
"height" "short" // height to render the SWF in
 
"blend" "bool" // Enable screen alpha blending
 
}
 
 
"flash_hud_toggle"
 
{
 
"enable" "bool" // True to enable the flash HUD, false to disable
 
}
 
 
"fscommand_one"
 
{
 
}
 
 
"fscommand_two"
 
{
 
}
 
 
"fscommand_three"
 
{
 
}
 
 
"fscommand_four"
 
{
 
}
 
</syntaxhighlight>
 
 
== Conclusion ==
 
You have completed integrating Adobe Flash into your mod! At this point in time, you should be able to place a flash file in /sourcemods/yourmod/flash/ui/ called title.swf and have it display when you start up the game. You should also have access to the env_flash entity in Hammer (don't forget to add your mod's FGD to hammer options). Lastly, you may want to remove the old title screen by editing GameMenu.res to the following:
 
 
<syntaxhighlight lang="cpp">
 
"GameMenu"
 
{
 
}
 
</syntaxhighlight>
 

Revision as of 06:57, 24 May 2019