2011-08-03 20:05:33 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
|
|
|
|
This file is part of Aquaria.
|
|
|
|
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "Core.h"
|
|
|
|
#include "Texture.h"
|
|
|
|
#include "AfterEffect.h"
|
|
|
|
#include "Particles.h"
|
2016-07-09 02:18:40 +00:00
|
|
|
#include "GLLoad.h"
|
|
|
|
#include "RenderBase.h"
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
#include <time.h>
|
2011-08-11 00:26:46 +00:00
|
|
|
#include <iostream>
|
2016-07-09 02:18:40 +00:00
|
|
|
#include <fstream>
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
#ifdef BBGE_BUILD_UNIX
|
|
|
|
#include <limits.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#if __APPLE__
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
#endif
|
|
|
|
|
2011-11-20 22:47:24 +00:00
|
|
|
#if BBGE_BUILD_WINDOWS
|
2013-06-19 00:08:24 +00:00
|
|
|
#include <direct.h>
|
2011-11-20 22:47:24 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
#include "SDL_syswm.h"
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
static SDL_Window *gScreen=0;
|
|
|
|
static SDL_GLContext gGLctx=0;
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
static SDL_Surface *gScreen=0;
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
bool ignoreNextMouse=false;
|
|
|
|
Vector unchange;
|
|
|
|
|
2014-04-15 17:48:06 +00:00
|
|
|
#ifdef BBGE_BUILD_VFS
|
|
|
|
#include "ttvfs.h"
|
|
|
|
#endif
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
Core *core = 0;
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
static std::ofstream _logOut;
|
|
|
|
|
2013-12-12 10:34:15 +00:00
|
|
|
#ifndef KMOD_GUI
|
|
|
|
#define KMOD_GUI KMOD_META
|
|
|
|
#endif
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
void Core::resetCamera()
|
|
|
|
{
|
|
|
|
cameraPos = Vector(0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParticleEffect* Core::createParticleEffect(const std::string &name, const Vector &position, int layer, float rotz)
|
|
|
|
{
|
|
|
|
ParticleEffect *e = new ParticleEffect();
|
|
|
|
e->load(name);
|
|
|
|
e->position = position;
|
|
|
|
e->start();
|
|
|
|
e->setDie(true);
|
|
|
|
e->rotation.z = rotz;
|
|
|
|
core->getTopStateData()->addRenderObject(e, layer);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::unloadDevice()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < renderObjectLayers.size(); i++)
|
|
|
|
{
|
|
|
|
RenderObjectLayer *r = &renderObjectLayers[i];
|
|
|
|
RenderObject *robj = r->getFirst();
|
|
|
|
while (robj)
|
|
|
|
{
|
|
|
|
robj->unloadDevice();
|
|
|
|
robj = r->getNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
frameBuffer.unloadDevice();
|
|
|
|
|
|
|
|
if (afterEffectManager)
|
|
|
|
afterEffectManager->unloadDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::reloadDevice()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < renderObjectLayers.size(); i++)
|
|
|
|
{
|
|
|
|
RenderObjectLayer *r = &renderObjectLayers[i];
|
|
|
|
r->reloadDevice();
|
|
|
|
RenderObject *robj = r->getFirst();
|
|
|
|
while (robj)
|
|
|
|
{
|
|
|
|
robj->reloadDevice();
|
|
|
|
robj = r->getNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
frameBuffer.reloadDevice();
|
|
|
|
|
|
|
|
if (afterEffectManager)
|
|
|
|
afterEffectManager->reloadDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::resetGraphics(int w, int h, int fullscreen, int vsync, int bpp)
|
|
|
|
{
|
|
|
|
if (fullscreen == -1)
|
|
|
|
fullscreen = _fullscreen;
|
|
|
|
|
|
|
|
if (vsync == -1)
|
|
|
|
vsync = _vsync;
|
|
|
|
|
|
|
|
if (w == -1)
|
|
|
|
w = width;
|
|
|
|
|
|
|
|
if (h == -1)
|
|
|
|
h = height;
|
|
|
|
|
|
|
|
if (bpp == -1)
|
|
|
|
bpp = _bpp;
|
|
|
|
|
|
|
|
unloadDevice();
|
|
|
|
unloadResources();
|
|
|
|
|
|
|
|
shutdownGraphicsLibrary();
|
|
|
|
|
|
|
|
initGraphicsLibrary(w, h, fullscreen, vsync, bpp);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2012-02-19 03:57:04 +00:00
|
|
|
enable2DWide(w, h);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
reloadResources();
|
|
|
|
reloadDevice();
|
|
|
|
|
|
|
|
|
|
|
|
resetTimer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::toggleScreenMode(int t)
|
|
|
|
{
|
|
|
|
sound->pause();
|
|
|
|
resetGraphics(-1, -1, t);
|
|
|
|
cacheRender();
|
|
|
|
resetTimer();
|
|
|
|
sound->resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setWindowCaption(const std::string &caption, const std::string &icon)
|
|
|
|
{
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifndef BBGE_BUILD_SDL2
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_WM_SetCaption(caption.c_str(), icon.c_str());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderObjectLayer *Core::getRenderObjectLayer(int i)
|
|
|
|
{
|
|
|
|
if (i == LR_NONE)
|
|
|
|
return 0;
|
|
|
|
return &renderObjectLayers[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::getShiftState()
|
|
|
|
{
|
|
|
|
return getKeyState(KEY_LSHIFT) || getKeyState(KEY_RSHIFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::getAltState()
|
|
|
|
{
|
|
|
|
return getKeyState(KEY_LALT) || getKeyState(KEY_RALT);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::getCtrlState()
|
|
|
|
{
|
|
|
|
return getKeyState(KEY_LCONTROL) || getKeyState(KEY_RCONTROL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::errorLog(const std::string &s)
|
|
|
|
{
|
2013-06-23 16:50:10 +00:00
|
|
|
messageBox("Error!", s);
|
2011-08-03 20:05:33 +00:00
|
|
|
debugLog(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::messageBox(const std::string &title, const std::string &msg)
|
|
|
|
{
|
2013-06-23 16:50:10 +00:00
|
|
|
::messageBox(title, msg);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::debugLog(const std::string &s)
|
|
|
|
{
|
|
|
|
if (debugLogActive)
|
|
|
|
{
|
2011-08-11 00:26:46 +00:00
|
|
|
_logOut << s << std::endl;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
2011-08-11 00:26:46 +00:00
|
|
|
#ifdef _DEBUG
|
|
|
|
std::cout << s << std::endl;
|
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2011-11-20 22:47:24 +00:00
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
|
|
static bool checkWritable(const std::string& path, bool warn, bool critical)
|
|
|
|
{
|
|
|
|
bool writeable = false;
|
|
|
|
std::string f = path + "/~chk_wrt.tmp";
|
|
|
|
FILE *fh = fopen(f.c_str(), "w");
|
|
|
|
if(fh)
|
|
|
|
{
|
|
|
|
writeable = fwrite("abcdef", 5, 1, fh) == 1;
|
|
|
|
fclose(fh);
|
|
|
|
unlink(f.c_str());
|
|
|
|
}
|
|
|
|
if(!writeable)
|
|
|
|
{
|
|
|
|
if(warn)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Trying to use \"" << path << "\" as user data path, but it is not writeable.\n"
|
|
|
|
<< "Please make sure the game is allowed to write to that directory.\n"
|
|
|
|
<< "You can move the game to another location and run it there,\n"
|
|
|
|
<< "or try running it as administrator, that may help as well.";
|
|
|
|
if(critical)
|
|
|
|
os << "\n\nWill now exit.";
|
|
|
|
MessageBoxA(NULL, os.str().c_str(), "Need to write but can't!", MB_OK | MB_ICONERROR);
|
|
|
|
}
|
|
|
|
if(critical)
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return writeable;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-06-19 00:08:24 +00:00
|
|
|
Core::Core(const std::string &filesystem, const std::string& extraDataDir, int numRenderLayers, const std::string &appName, int particleSize, std::string userDataSubFolder)
|
2011-08-03 20:05:33 +00:00
|
|
|
: ActionMapper(), StateManager(), appName(appName)
|
|
|
|
{
|
2011-08-11 00:26:46 +00:00
|
|
|
sound = NULL;
|
2011-08-03 20:05:33 +00:00
|
|
|
screenCapScale = Vector(1,1,1);
|
2013-06-19 00:08:24 +00:00
|
|
|
_extraDataDir = extraDataDir;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (userDataSubFolder.empty())
|
|
|
|
userDataSubFolder = appName;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
|
|
const char *envr = getenv("HOME");
|
|
|
|
if (envr == NULL)
|
|
|
|
envr = "."; // oh well.
|
|
|
|
const std::string home(envr);
|
|
|
|
|
2013-11-14 19:07:39 +00:00
|
|
|
createDir(home); // just in case.
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
// "/home/icculus/.Aquaria" or something. Spaces are okay.
|
|
|
|
#ifdef BBGE_BUILD_MACOSX
|
|
|
|
const std::string prefix("Library/Application Support/");
|
|
|
|
#else
|
|
|
|
const std::string prefix(".");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
userDataFolder = home + "/" + prefix + userDataSubFolder;
|
2013-11-14 19:07:39 +00:00
|
|
|
createDir(userDataFolder);
|
2011-08-03 20:05:33 +00:00
|
|
|
debugLogPath = userDataFolder + "/";
|
2013-11-14 19:07:39 +00:00
|
|
|
createDir(userDataFolder + "/screenshots");
|
2011-08-03 20:05:33 +00:00
|
|
|
std::string prefpath(getPreferencesFolder());
|
2013-11-14 19:07:39 +00:00
|
|
|
createDir(prefpath);
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
#else
|
|
|
|
debugLogPath = "";
|
2011-11-20 22:47:24 +00:00
|
|
|
userDataFolder = ".";
|
|
|
|
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
|
|
{
|
|
|
|
if(checkWritable(userDataFolder, true, true)) // working dir?
|
|
|
|
{
|
|
|
|
puts("Using working directory as user directory.");
|
|
|
|
}
|
|
|
|
// TODO: we may want to use a user-specific path under windows as well
|
|
|
|
// if the code below gets actually used, pass 2x false to checkWritable() above.
|
|
|
|
// not sure about this right now -- FG
|
|
|
|
/*else
|
|
|
|
{
|
2013-11-14 19:07:39 +00:00
|
|
|
puts("Working directory is not writable...");
|
2011-11-20 22:47:24 +00:00
|
|
|
char pathbuf[MAX_PATH];
|
|
|
|
if(SHGetSpecialFolderPathA(NULL, &pathbuf[0], CSIDL_APPDATA, 0))
|
|
|
|
{
|
|
|
|
userDataFolder = pathbuf;
|
|
|
|
userDataFolder += '/';
|
|
|
|
userDataFolder += userDataSubFolder;
|
|
|
|
for(uint32 i = 0; i < userDataFolder.length(); ++i)
|
|
|
|
if(userDataFolder[i] == '\\')
|
|
|
|
userDataFolder[i] = '/';
|
|
|
|
debugLogPath = userDataFolder + "/";
|
|
|
|
puts(("Using \"" + userDataFolder + "\" as user directory.").c_str());
|
2013-11-14 19:07:39 +00:00
|
|
|
createDir(userDataFolder);
|
2011-11-20 22:47:24 +00:00
|
|
|
checkWritable(userDataFolder, true, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
puts("Failed to retrieve appdata path, using working dir."); // too bad, but can't do anything about it
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
#endif
|
2011-11-20 15:27:55 +00:00
|
|
|
|
|
|
|
_logOut.open((debugLogPath + "debug.log").c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
debugLogActive = true;
|
|
|
|
|
|
|
|
debugLogTextures = true;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
grabInputOnReentry = -1;
|
|
|
|
|
|
|
|
srand(time(NULL));
|
|
|
|
old_dt = 0;
|
2013-07-15 01:22:41 +00:00
|
|
|
current_dt = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
aspectX = 4;
|
|
|
|
aspectY = 3;
|
|
|
|
|
|
|
|
virtualOffX = virtualOffY = 0;
|
|
|
|
|
|
|
|
viewOffX = viewOffY = 0;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
particleManager = new ParticleManager(particleSize);
|
|
|
|
nowTicks = thenTicks = 0;
|
|
|
|
_hasFocus = false;
|
|
|
|
lib_graphics = lib_sound = lib_input = false;
|
|
|
|
clearColor = Vector(0,0,0);
|
|
|
|
updateCursorFromMouse = true;
|
|
|
|
mouseConstraint = false;
|
|
|
|
mouseCircle = 0;
|
|
|
|
overrideStartLayer = 0;
|
|
|
|
overrideEndLayer = 0;
|
|
|
|
frameOutputMode = false;
|
|
|
|
updateMouse = true;
|
|
|
|
particlesPaused = false;
|
|
|
|
joystickAsMouse = false;
|
|
|
|
currentLayerPass = 0;
|
|
|
|
flipMouseButtons = 0;
|
|
|
|
joystickEnabled = false;
|
|
|
|
doScreenshot = false;
|
|
|
|
baseCullRadius = 1;
|
|
|
|
width = height = 0;
|
|
|
|
afterEffectManagerLayer = 0;
|
|
|
|
renderObjectLayers.resize(1);
|
|
|
|
invGlobalScale = 1.0;
|
|
|
|
invGlobalScaleSqr = 1.0;
|
|
|
|
renderObjectCount = 0;
|
|
|
|
avgFPS.resize(1);
|
|
|
|
minimized = false;
|
|
|
|
numSavedScreenshots = 0;
|
|
|
|
shuttingDown = false;
|
|
|
|
nestedMains = 0;
|
|
|
|
afterEffectManager = 0;
|
|
|
|
loopDone = false;
|
|
|
|
core = this;
|
|
|
|
|
|
|
|
for (int i = 0; i < KEY_MAXARRAY; i++)
|
|
|
|
{
|
|
|
|
keys[i] = 0;
|
|
|
|
}
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
aspect = (aspectX/aspectY);
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
globalResolutionScale = globalScale = Vector(1,1,1);
|
|
|
|
|
|
|
|
initRenderObjectLayers(numRenderLayers);
|
|
|
|
|
|
|
|
initPlatform(filesystem);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::initPlatform(const std::string &filesystem)
|
|
|
|
{
|
|
|
|
#if defined(BBGE_BUILD_MACOSX) && !defined(BBGE_BUILD_MACOSX_NOBUNDLEPATH)
|
|
|
|
// FIXME: filesystem not handled
|
|
|
|
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
CFURLRef resourcesURL = CFBundleCopyBundleURL(mainBundle);
|
|
|
|
char path[PATH_MAX];
|
|
|
|
if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
|
|
|
|
{
|
|
|
|
// error!
|
|
|
|
debugLog("CFURLGetFileSystemRepresentation");
|
|
|
|
}
|
|
|
|
CFRelease(resourcesURL);
|
|
|
|
debugLog(path);
|
|
|
|
chdir(path);
|
|
|
|
#elif defined(BBGE_BUILD_UNIX)
|
|
|
|
if (!filesystem.empty())
|
|
|
|
{
|
|
|
|
if (chdir(filesystem.c_str()) == 0)
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
debugLog("Failed to chdir to filesystem path " + filesystem);
|
|
|
|
}
|
|
|
|
#ifdef BBGE_DATA_PREFIX
|
|
|
|
if (chdir(BBGE_DATA_PREFIX) == 0 && chdir(appName.c_str()) == 0)
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
debugLog("Failed to chdir to filesystem path " BBGE_DATA_PREFIX + appName);
|
|
|
|
#endif
|
|
|
|
char path[PATH_MAX];
|
|
|
|
// always a symlink to this process's binary, on modern Linux systems.
|
|
|
|
const ssize_t rc = readlink("/proc/self/exe", path, sizeof (path));
|
|
|
|
if ( (rc == -1) || (rc >= sizeof (path)) )
|
|
|
|
{
|
|
|
|
// error!
|
|
|
|
debugLog("readlink");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path[rc] = '\0';
|
|
|
|
char *ptr = strrchr(path, '/');
|
|
|
|
if (ptr != NULL)
|
|
|
|
{
|
|
|
|
*ptr = '\0';
|
|
|
|
debugLog(path);
|
|
|
|
if (chdir(path) != 0)
|
|
|
|
debugLog("Failed to chdir to executable path" + std::string(path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
2013-06-19 00:08:24 +00:00
|
|
|
if(filesystem.length())
|
|
|
|
{
|
|
|
|
if(_chdir(filesystem.c_str()) != 0)
|
|
|
|
{
|
|
|
|
debugLog("chdir failed: " + filesystem);
|
|
|
|
}
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
// FIXME: filesystem not handled
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Core::getPreferencesFolder()
|
|
|
|
{
|
|
|
|
#ifdef BBGE_BUILD_UNIX
|
|
|
|
return userDataFolder + "/preferences";
|
|
|
|
#endif
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
|
|
return "";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Core::getUserDataFolder()
|
|
|
|
{
|
|
|
|
return userDataFolder;
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::~Core()
|
|
|
|
{
|
2016-07-17 20:25:24 +00:00
|
|
|
clearActionButtons();
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (particleManager)
|
|
|
|
{
|
|
|
|
delete particleManager;
|
|
|
|
}
|
|
|
|
if (sound)
|
|
|
|
{
|
|
|
|
delete sound;
|
|
|
|
sound = 0;
|
|
|
|
}
|
2011-08-11 00:26:46 +00:00
|
|
|
debugLog("~Core()");
|
|
|
|
_logOut.close();
|
2011-08-03 20:05:33 +00:00
|
|
|
core = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::hasFocus()
|
|
|
|
{
|
|
|
|
return _hasFocus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setInputGrab(bool on)
|
|
|
|
{
|
|
|
|
if (isWindowFocus())
|
|
|
|
{
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_SetWindowGrab(gScreen, on ? SDL_TRUE : SDL_FALSE);
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_WM_GrabInput(on?SDL_GRAB_ON:SDL_GRAB_OFF);
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setReentryInputGrab(int on)
|
|
|
|
{
|
|
|
|
if (grabInputOnReentry == -1)
|
|
|
|
{
|
|
|
|
setInputGrab(on);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setInputGrab(grabInputOnReentry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::isFullscreen()
|
|
|
|
{
|
|
|
|
return _fullscreen;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::isShuttingDown()
|
|
|
|
{
|
|
|
|
return shuttingDown;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::init()
|
|
|
|
{
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
setupFileAccess();
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
quitNestedMainFlag = false;
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifndef BBGE_BUILD_SDL2
|
2011-08-03 20:05:33 +00:00
|
|
|
// Disable relative mouse motion at the edges of the screen, which breaks
|
|
|
|
// mouse control for absolute input devices like Wacom tablets and touchscreens.
|
|
|
|
SDL_putenv((char *) "SDL_MOUSE_RELATIVE=0");
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if((SDL_Init(0))==-1)
|
|
|
|
{
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error("Failed to init SDL");
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
loopDone = false;
|
|
|
|
|
2013-11-14 16:58:33 +00:00
|
|
|
initLocalization();
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::initRenderObjectLayers(int num)
|
|
|
|
{
|
|
|
|
renderObjectLayers.resize(num);
|
|
|
|
renderObjectLayerOrder.resize(num);
|
|
|
|
for (int i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
renderObjectLayerOrder[i] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::initSoundLibrary(const std::string &defaultDevice)
|
|
|
|
{
|
|
|
|
debugLog("Creating SoundManager");
|
|
|
|
sound = new SoundManager(defaultDevice);
|
|
|
|
debugLog("Done");
|
|
|
|
return sound != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector Core::getGameCursorPosition()
|
|
|
|
{
|
2014-03-07 16:59:36 +00:00
|
|
|
return getGamePosition(mouse.position);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector Core::getGamePosition(const Vector &v)
|
|
|
|
{
|
2014-03-07 16:59:36 +00:00
|
|
|
return cameraPos + (v * invGlobalScale);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::getMouseButtonState(int m)
|
|
|
|
{
|
|
|
|
int mcode=m;
|
|
|
|
|
|
|
|
switch(m)
|
|
|
|
{
|
|
|
|
case 0: mcode=1; break;
|
|
|
|
case 1: mcode=3; break;
|
|
|
|
case 2: mcode=2; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Uint8 mousestate = SDL_GetMouseState(0,0);
|
|
|
|
|
|
|
|
return mousestate & SDL_BUTTON(mcode);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::getKeyState(int k)
|
|
|
|
{
|
2016-07-03 13:48:40 +00:00
|
|
|
return k > 0 && k < KEY_MAXARRAY ? keys[k] : 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2016-07-03 16:07:13 +00:00
|
|
|
void Core::initJoystickLibrary()
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER);
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
2016-07-13 03:00:19 +00:00
|
|
|
detectJoysticks();
|
2011-08-03 20:05:33 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-13 03:00:19 +00:00
|
|
|
joystickEnabled = true;
|
2016-07-03 16:07:13 +00:00
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-07-03 16:07:13 +00:00
|
|
|
void Core::clearJoysticks()
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < joysticks.size(); ++i)
|
|
|
|
delete joysticks[i];
|
|
|
|
joysticks.clear();
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-07-13 03:00:19 +00:00
|
|
|
|
|
|
|
// Only used for SDL 1.2 code path.
|
|
|
|
// SDL2 automatically fires joystick added events upon startup
|
2016-07-03 16:07:13 +00:00
|
|
|
void Core::detectJoysticks()
|
|
|
|
{
|
|
|
|
clearJoysticks();
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
const unsigned n = SDL_NumJoysticks();
|
|
|
|
os << "Found [" << n << "] joysticks";
|
|
|
|
debugLog(os.str());
|
|
|
|
|
2016-07-13 03:00:19 +00:00
|
|
|
joysticks.reserve(n);
|
2016-07-03 16:07:13 +00:00
|
|
|
for(unsigned i = 0; i < n; ++i)
|
|
|
|
{
|
|
|
|
Joystick *j = new Joystick;
|
|
|
|
if(j->init(i))
|
|
|
|
joysticks.push_back(j);
|
|
|
|
else
|
|
|
|
delete j;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::initInputLibrary()
|
|
|
|
{
|
|
|
|
core->mouse.position = Vector(getWindowWidth()/2, getWindowHeight()/2);
|
|
|
|
|
|
|
|
for (int i = 0; i < KEY_MAXARRAY; i++)
|
|
|
|
{
|
|
|
|
keys[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::onUpdate(float dt)
|
|
|
|
{
|
|
|
|
if (minimized) return;
|
|
|
|
|
|
|
|
core->mouse.lastPosition = core->mouse.position;
|
|
|
|
core->mouse.lastScrollWheel = core->mouse.scrollWheel;
|
|
|
|
|
2016-07-18 21:14:20 +00:00
|
|
|
pollEvents(dt);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-08-03 23:55:32 +00:00
|
|
|
|
|
|
|
|
2016-07-18 21:22:42 +00:00
|
|
|
ActionMapper::onUpdate(dt);
|
|
|
|
StateManager::onUpdate(dt);
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
onMouseInput();
|
|
|
|
|
|
|
|
globalScale.update(dt);
|
2014-03-07 16:59:36 +00:00
|
|
|
core->globalScaleChanged();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (afterEffectManager)
|
|
|
|
{
|
|
|
|
afterEffectManager->update(dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-07 16:59:36 +00:00
|
|
|
void Core::globalScaleChanged()
|
|
|
|
{
|
|
|
|
invGlobalScale = 1.0f/globalScale.x;
|
|
|
|
invGlobalScaleSqr = invGlobalScale * invGlobalScale;
|
|
|
|
}
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
Vector Core::getClearColor()
|
|
|
|
{
|
|
|
|
return clearColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setClearColor(const Vector &c)
|
|
|
|
{
|
|
|
|
clearColor = c;
|
|
|
|
|
|
|
|
glClearColor(c.x, c.y, c.z, 0.0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setSDLGLAttributes()
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "setting vsync: " << _vsync;
|
|
|
|
debugLog(os.str());
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifndef BBGE_BUILD_SDL2
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync);
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Core::initGraphicsLibrary(int width, int height, bool fullscreen, int vsync, int bpp, bool recreate)
|
2016-05-05 17:40:28 +00:00
|
|
|
{
|
2011-08-03 20:05:33 +00:00
|
|
|
static bool didOnce = false;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
aspectX = width;
|
|
|
|
aspectY = height;
|
|
|
|
|
|
|
|
aspect = (aspectX/aspectY);
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
this->width = width;
|
|
|
|
this->height = height;
|
|
|
|
_vsync = vsync;
|
|
|
|
_fullscreen = fullscreen;
|
|
|
|
_bpp = bpp;
|
|
|
|
|
|
|
|
_hasFocus = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifndef BBGE_BUILD_SDL2
|
|
|
|
#if !defined(BBGE_BUILD_MACOSX)
|
2011-08-03 20:05:33 +00:00
|
|
|
// have to cast away constness, since SDL_putenv() might be #defined to
|
|
|
|
// putenv(), which takes a (char *), and freaks out newer GCC releases
|
|
|
|
// when you try to pass a (const!) string literal here... --ryan.
|
|
|
|
SDL_putenv((char *) "SDL_VIDEO_CENTERED=1");
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
|
|
|
#endif
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (recreate)
|
|
|
|
{
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
|
|
{
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error(std::string("SDL Error: ") + std::string(SDL_GetError()));
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if BBGE_BUILD_OPENGL_DYNAMIC
|
|
|
|
if (SDL_GL_LoadLibrary(NULL) == -1)
|
|
|
|
{
|
2013-06-23 16:50:10 +00:00
|
|
|
std::string err = std::string("SDL_GL_LoadLibrary Error: ") + std::string(SDL_GetError());
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_Quit();
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error(err);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
setWindowCaption(appName, appName);
|
|
|
|
|
2016-08-06 17:50:07 +00:00
|
|
|
initIcon(gScreen);
|
2011-08-03 20:05:33 +00:00
|
|
|
// Create window
|
|
|
|
|
|
|
|
setSDLGLAttributes();
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
Uint32 flags = 0;
|
|
|
|
flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
|
|
|
if (fullscreen)
|
|
|
|
flags |= SDL_WINDOW_FULLSCREEN;
|
|
|
|
gScreen = SDL_CreateWindow(appName.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
|
|
|
|
if (gScreen == NULL)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Couldn't set resolution [" << width << "x" << height << "]\n" << SDL_GetError();
|
|
|
|
errorLog(os.str());
|
|
|
|
SDL_Quit();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
gGLctx = SDL_GL_CreateContext(gScreen);
|
|
|
|
if (gGLctx == NULL)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Couldn't create OpenGL context!\n" << SDL_GetError();
|
|
|
|
errorLog(os.str());
|
|
|
|
SDL_Quit();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
Uint32 flags = 0;
|
|
|
|
flags = SDL_OPENGL;
|
|
|
|
if (fullscreen)
|
|
|
|
flags |= SDL_FULLSCREEN;
|
|
|
|
|
|
|
|
gScreen = SDL_SetVideoMode(width, height, bpp, flags);
|
|
|
|
if (gScreen == NULL)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Couldn't set resolution [" << width << "x" << height << "]\n" << SDL_GetError();
|
|
|
|
SDL_Quit();
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error(os.str());
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
#if BBGE_BUILD_OPENGL_DYNAMIC
|
|
|
|
if (!lookup_all_glsyms())
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Couldn't load OpenGL symbols we need\n";
|
|
|
|
SDL_Quit();
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error(os.str());
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
setWindowCaption(appName, appName);
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
SDL_GL_SwapWindow(gScreen);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
SDL_GL_SwapWindow(gScreen);
|
|
|
|
if ((_vsync != 1) || (SDL_GL_SetSwapInterval(-1) == -1))
|
|
|
|
SDL_GL_SetSwapInterval(_vsync);
|
|
|
|
const char *name = SDL_GetCurrentVideoDriver();
|
|
|
|
SDL_SetWindowGrab(gScreen, SDL_TRUE);
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_WM_GrabInput(grabInputOnReentry==0 ? SDL_GRAB_OFF : SDL_GRAB_ON);
|
|
|
|
char name[256];
|
|
|
|
SDL_VideoDriverName((char*)name, 256);
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
glScissor(0, 0, width, height);
|
|
|
|
|
|
|
|
std::ostringstream os2;
|
|
|
|
os2 << "Video Driver Name [" << name << "]";
|
|
|
|
debugLog(os2.str());
|
|
|
|
|
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
SDL_PumpEvents();
|
|
|
|
|
|
|
|
for (int i = 0; i < KEY_MAXARRAY; i++)
|
|
|
|
{
|
|
|
|
keys[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
|
|
|
|
glClearDepth(1.0); // Depth Buffer Setup
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
glLoadIdentity();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
glFinish();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setClearColor(clearColor);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
clearBuffers();
|
|
|
|
showBuffer();
|
|
|
|
|
|
|
|
lib_graphics = true;
|
|
|
|
|
|
|
|
_hasFocus = true;
|
|
|
|
|
|
|
|
enumerateScreenModes();
|
|
|
|
|
|
|
|
if (!didOnce)
|
|
|
|
didOnce = true;
|
|
|
|
|
|
|
|
// init success
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::enumerateScreenModes()
|
|
|
|
{
|
|
|
|
screenModes.clear();
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
const int modecount = SDL_GetNumDisplayModes(0);
|
|
|
|
if(modecount == 0){
|
|
|
|
debugLog("No modes available!");
|
|
|
|
return;
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
for (int i = 0; i < modecount; i++) {
|
|
|
|
SDL_GetDisplayMode(0, i, &mode);
|
|
|
|
if (mode.w && mode.h && (mode.w > mode.h))
|
|
|
|
{
|
2015-07-12 18:25:46 +00:00
|
|
|
screenModes.push_back(ScreenMode(i, mode.w, mode.h, mode.refresh_rate));
|
2013-07-18 21:29:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_Rect **modes;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
|
|
|
|
|
|
|
|
if(modes == (SDL_Rect **)0){
|
|
|
|
debugLog("No modes available!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(modes == (SDL_Rect **)-1){
|
|
|
|
debugLog("All resolutions available.");
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
int c=0;
|
|
|
|
for(i=0;modes[i];++i){
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
for (i=c-1;i>=0;i--)
|
|
|
|
{
|
|
|
|
if (modes[i]->w > modes[i]->h)
|
|
|
|
{
|
2015-08-03 18:32:41 +00:00
|
|
|
screenModes.push_back(ScreenMode(i, modes[i]->w, modes[i]->h, 0));
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::shutdownSoundLibrary()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::shutdownGraphicsLibrary(bool killVideo)
|
|
|
|
{
|
|
|
|
glFinish();
|
|
|
|
if (killVideo) {
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_SetWindowGrab(gScreen, SDL_FALSE);
|
|
|
|
SDL_GL_MakeCurrent(gScreen, NULL);
|
|
|
|
SDL_GL_DeleteContext(gGLctx);
|
|
|
|
SDL_DestroyWindow(gScreen);
|
|
|
|
gGLctx = 0;
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
FrameBuffer::resetOpenGL();
|
|
|
|
|
|
|
|
gScreen = 0;
|
|
|
|
#if BBGE_BUILD_OPENGL_DYNAMIC
|
2016-07-09 02:18:40 +00:00
|
|
|
unload_all_glsyms();
|
2011-08-03 20:05:33 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
_hasFocus = false;
|
|
|
|
|
|
|
|
lib_graphics = false;
|
|
|
|
|
2016-08-06 17:50:07 +00:00
|
|
|
destroyIcon();
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::quit()
|
|
|
|
{
|
|
|
|
enqueueJumpState("STATE_QUIT");
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::applyState(const std::string &state)
|
|
|
|
{
|
|
|
|
if (nocasecmp(state, "state_quit")==0)
|
|
|
|
{
|
|
|
|
loopDone = true;
|
|
|
|
}
|
|
|
|
StateManager::applyState(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
|
|
void centerWindow(HWND hwnd)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
HWND hwndDeskTop;
|
|
|
|
RECT rcWnd, rcDeskTop;
|
|
|
|
// Get a handle to the desktop window
|
|
|
|
hwndDeskTop = ::GetDesktopWindow();
|
|
|
|
// Get dimension of desktop in a rect
|
|
|
|
::GetWindowRect(hwndDeskTop, &rcDeskTop);
|
|
|
|
// Get dimension of main window in a rect
|
|
|
|
::GetWindowRect(hwnd, &rcWnd);
|
|
|
|
// Find center of desktop
|
|
|
|
x = (rcDeskTop.right - rcDeskTop.left)/2;
|
|
|
|
y = (rcDeskTop.bottom - rcDeskTop.top)/2;
|
|
|
|
x -= (rcWnd.right - rcWnd.left)/2;
|
|
|
|
y -= (rcWnd.bottom - rcWnd.top)/2;
|
|
|
|
// Set top and left to center main window on desktop
|
|
|
|
::SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
bool Core::createWindow(int width, int height, int bits, bool fullscreen, std::string windowTitle)
|
|
|
|
{
|
|
|
|
this->width = width;
|
|
|
|
this->height = height;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// No longer part of C/C++ standard
|
|
|
|
#ifndef M_PI
|
|
|
|
#define M_PI 3.14159265358979323846
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
bbgePerspective(float fovy, float aspect, float zNear, float zFar)
|
|
|
|
{
|
|
|
|
float sine, cotangent, deltaZ;
|
|
|
|
float radians = fovy / 2.0f * M_PI / 180.0f;
|
|
|
|
|
|
|
|
deltaZ = zFar - zNear;
|
|
|
|
sine = sinf(radians);
|
|
|
|
if ((deltaZ == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cotangent = cosf(radians) / sine;
|
|
|
|
|
|
|
|
GLfloat m[4][4] = {
|
|
|
|
{ 1.0f, 0.0f, 0.0f, 0.0f },
|
|
|
|
{ 0.0f, 1.0f, 0.0f, 0.0f },
|
|
|
|
{ 0.0f, 0.0f, 1.0f, 0.0f },
|
|
|
|
{ 0.0f, 0.0f, 0.0f, 1.0f }
|
|
|
|
};
|
|
|
|
m[0][0] = (GLfloat) (cotangent / aspect);
|
|
|
|
m[1][1] = (GLfloat) cotangent;
|
|
|
|
m[2][2] = (GLfloat) (-(zFar + zNear) / deltaZ);
|
|
|
|
m[2][3] = -1.0f;
|
|
|
|
m[3][2] = (GLfloat) (-2.0f * zNear * zFar / deltaZ);
|
|
|
|
m[3][3] = 0.0f;
|
|
|
|
|
|
|
|
glMultMatrixf(&m[0][0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setPixelScale(int pixelScaleX, int pixelScaleY)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
virtualWidth = pixelScaleX;
|
2016-05-05 17:40:28 +00:00
|
|
|
virtualHeight = pixelScaleY; //assumes 4:3 aspect ratio
|
2014-03-07 16:59:36 +00:00
|
|
|
this->baseCullRadius = 1.1f * sqrtf(sqr(getVirtualWidth()/2) + sqr(getVirtualHeight()/2));
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "virtual(" << virtualWidth << ", " << virtualHeight << ")";
|
|
|
|
debugLog(os.str());
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
center = Vector(baseVirtualWidth/2, baseVirtualHeight/2);
|
|
|
|
|
|
|
|
virtualOffX = 0;
|
|
|
|
virtualOffY = 0;
|
|
|
|
|
|
|
|
int diff = 0;
|
|
|
|
|
|
|
|
diff = virtualWidth-baseVirtualWidth;
|
|
|
|
if (diff > 0)
|
|
|
|
virtualOffX = ((virtualWidth-baseVirtualWidth)/2);
|
|
|
|
else
|
|
|
|
virtualOffX = 0;
|
|
|
|
|
|
|
|
|
|
|
|
diff = virtualHeight-baseVirtualHeight;
|
|
|
|
if (diff > 0)
|
|
|
|
virtualOffY = ((virtualHeight-baseVirtualHeight)/2);
|
|
|
|
else
|
|
|
|
virtualOffY = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// forcePixelScale used by Celu
|
|
|
|
|
|
|
|
void Core::enable2DWide(int rx, int ry)
|
|
|
|
{
|
|
|
|
float aspect = float(rx) / float(ry);
|
|
|
|
if (aspect >= 1.3f)
|
|
|
|
{
|
|
|
|
int vw = int(float(baseVirtualHeight) * (float(rx)/float(ry)));
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
core->enable2D(vw, baseVirtualHeight, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int vh = int(float(baseVirtualWidth) * (float(ry)/float(rx)));
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
core->enable2D(baseVirtualWidth, vh, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bbgeOrtho2D(float left, float right, float bottom, float top)
|
|
|
|
{
|
|
|
|
glOrtho(left, right, bottom, top, -1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::enable2D(int pixelScaleX, int pixelScaleY, bool forcePixelScale)
|
|
|
|
{
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
GLint viewPort[4];
|
|
|
|
glGetIntegerv(GL_VIEWPORT, viewPort);
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
float vw=0,vh=0;
|
|
|
|
|
|
|
|
viewOffX = viewOffY = 0;
|
|
|
|
|
|
|
|
float aspect = float(width)/float(height);
|
|
|
|
|
|
|
|
if (aspect >= 1.4f)
|
|
|
|
{
|
|
|
|
vw = float(baseVirtualWidth * viewPort[3]) / float(baseVirtualHeight);
|
|
|
|
|
|
|
|
viewOffX = (viewPort[2] - vw) * 0.5f;
|
|
|
|
}
|
|
|
|
else if (aspect < 1.3f)
|
|
|
|
{
|
|
|
|
vh = float(baseVirtualHeight * viewPort[2]) / float(baseVirtualWidth);
|
|
|
|
|
|
|
|
viewOffY = (viewPort[3] - vh) * 0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
bbgeOrtho2D(0.0f-viewOffX,viewPort[2]-viewOffX,viewPort[3]-viewOffY,0.0f-viewOffY);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
setupRenderPositionAndScale();
|
|
|
|
|
|
|
|
|
|
|
|
if (forcePixelScale || (pixelScaleX!=0 && core->width!=pixelScaleX) || (pixelScaleY!=0 && core->height!=pixelScaleY))
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
float widthFactor = core->width/float(pixelScaleX);
|
|
|
|
float heightFactor = core->height/float(pixelScaleY);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
core->globalResolutionScale = Vector(widthFactor,heightFactor,1.0f);
|
|
|
|
setPixelScale(pixelScaleX, pixelScaleY);
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
setPixelScale(pixelScaleX, pixelScaleY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::quitNestedMain()
|
|
|
|
{
|
|
|
|
if (getNestedMains() > 1)
|
|
|
|
{
|
|
|
|
quitNestedMainFlag = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::resetTimer()
|
|
|
|
{
|
|
|
|
nowTicks = thenTicks = SDL_GetTicks();
|
|
|
|
|
|
|
|
for (int i = 0; i < avgFPS.size(); i++)
|
|
|
|
{
|
|
|
|
avgFPS[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setDockIcon(const std::string &ident)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setMousePosition(const Vector &p)
|
|
|
|
{
|
|
|
|
Vector lp = core->mouse.position;
|
|
|
|
|
|
|
|
core->mouse.position = p;
|
|
|
|
float px = p.x + virtualOffX;
|
2016-05-05 17:40:28 +00:00
|
|
|
float py = p.y;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_WarpMouseInWindow(gScreen, px * (float(width)/float(virtualWidth)), py * (float(height)/float(virtualHeight)));
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_WarpMouse( px * (float(width)/float(virtualWidth)), py * (float(height)/float(virtualHeight)));
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// used to update all render objects either uniformly or as part of a time sliced update process
|
|
|
|
void Core::updateRenderObjects(float dt)
|
|
|
|
{
|
|
|
|
for (int c = 0; c < renderObjectLayers.size(); c++)
|
|
|
|
{
|
|
|
|
|
|
|
|
RenderObjectLayer *rl = &renderObjectLayers[c];
|
|
|
|
|
|
|
|
if (!rl->update)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (RenderObject *r = rl->getFirst(); r; r = rl->getNext())
|
|
|
|
{
|
|
|
|
r->update(dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loopDone)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Core::getEnqueuedJumpState()
|
|
|
|
{
|
|
|
|
return this->enqueuedJumpState;
|
|
|
|
}
|
|
|
|
|
|
|
|
int screenshotNum = 0;
|
|
|
|
std::string getScreenshotFilename()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << core->getUserDataFolder() << "/screenshots/screen" << screenshotNum << ".tga";
|
|
|
|
screenshotNum ++;
|
|
|
|
std::string str(os.str());
|
|
|
|
if (!core->exists(str)) // keep going until we hit an unused filename.
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 18:05:38 +00:00
|
|
|
unsigned Core::getTicks()
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
return SDL_GetTicks();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::isWindowFocus()
|
|
|
|
{
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
return ((SDL_GetWindowFlags(gScreen) & SDL_WINDOW_INPUT_FOCUS) != 0);
|
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
return ((SDL_GetAppState() & SDL_APPINPUTFOCUS) != 0);
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-24 01:39:48 +00:00
|
|
|
void Core::onBackgroundUpdate()
|
|
|
|
{
|
|
|
|
SDL_Delay(200);
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
void Core::run(float runTime)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
// cannot nest loops when the game is over
|
|
|
|
if (loopDone) return;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
float dt;
|
|
|
|
float counter = 0;
|
|
|
|
int frames = 0;
|
|
|
|
float real_dt = 0;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
#if (!defined(_DEBUG) || defined(BBGE_BUILD_UNIX)) && defined(BBGE_BUILD_SDL)
|
|
|
|
bool wasInactive = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nowTicks = thenTicks = SDL_GetTicks();
|
|
|
|
nestedMains++;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
while((runTime == -1 && !loopDone) || (runTime >0))
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
BBGE_PROF(Core_main);
|
|
|
|
|
2016-05-14 15:23:48 +00:00
|
|
|
nowTicks = SDL_GetTicks();
|
2011-08-03 20:05:33 +00:00
|
|
|
dt = (nowTicks-thenTicks)/1000.0;
|
|
|
|
thenTicks = nowTicks;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (!avgFPS.empty())
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
int i = 0;
|
|
|
|
for (i = avgFPS.size()-1; i > 0; i--)
|
|
|
|
{
|
|
|
|
avgFPS[i] = avgFPS[i-1];
|
|
|
|
}
|
|
|
|
avgFPS[0] = dt;
|
|
|
|
|
|
|
|
float c=0;
|
|
|
|
int n = 0;
|
|
|
|
for (i = 0; i < avgFPS.size(); i++)
|
|
|
|
{
|
|
|
|
if (avgFPS[i] > 0)
|
|
|
|
{
|
|
|
|
c += avgFPS[i];
|
|
|
|
n ++;
|
|
|
|
}
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
if (n > 0)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
c /= n;
|
|
|
|
dt = c;
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
#if !defined(_DEBUG) && defined(BBGE_BUILD_SDL)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (lib_graphics && (wasInactive || !settings.runInBackground))
|
|
|
|
{
|
|
|
|
if (isWindowFocus())
|
|
|
|
{
|
|
|
|
_hasFocus = true;
|
|
|
|
if (wasInactive)
|
|
|
|
{
|
|
|
|
debugLog("WINDOW ACTIVE");
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
setReentryInputGrab(1);
|
|
|
|
|
|
|
|
wasInactive = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_hasFocus)
|
|
|
|
{
|
|
|
|
if (!wasInactive)
|
|
|
|
debugLog("WINDOW INACTIVE");
|
|
|
|
|
|
|
|
wasInactive = true;
|
|
|
|
_hasFocus = false;
|
|
|
|
|
|
|
|
setReentryInputGrab(0);
|
|
|
|
|
|
|
|
sound->pause();
|
|
|
|
|
|
|
|
while (!isWindowFocus())
|
|
|
|
{
|
2016-07-18 21:14:20 +00:00
|
|
|
pollEvents(dt);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2013-06-24 01:39:48 +00:00
|
|
|
onBackgroundUpdate();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
resetTimer();
|
|
|
|
}
|
|
|
|
|
|
|
|
debugLog("app back in focus, reset");
|
|
|
|
|
|
|
|
// Don't do this on Linux, it's not necessary and causes big stalls.
|
|
|
|
// We don't actually _lose_ the device like Direct3D anyhow.
|
|
|
|
#ifndef BBGE_BUILD_UNIX
|
|
|
|
if (_fullscreen)
|
|
|
|
{
|
|
|
|
// calls reload device - reloadDevice()
|
|
|
|
resetGraphics(width, height);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
resetTimer();
|
|
|
|
|
|
|
|
sound->resume();
|
|
|
|
|
|
|
|
resetTimer();
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
old_dt = dt;
|
|
|
|
|
|
|
|
modifyDt(dt);
|
|
|
|
|
2013-07-15 01:22:41 +00:00
|
|
|
current_dt = dt;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (quitNestedMainFlag)
|
|
|
|
{
|
|
|
|
quitNestedMainFlag = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (runTime>0)
|
|
|
|
{
|
|
|
|
runTime -= dt;
|
|
|
|
if (runTime < 0)
|
|
|
|
runTime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// UPDATE
|
|
|
|
postProcessingFx.update(dt);
|
|
|
|
|
|
|
|
updateRenderObjects(dt);
|
|
|
|
|
|
|
|
if (particleManager)
|
|
|
|
particleManager->update(dt);
|
|
|
|
|
|
|
|
sound->update(dt);
|
|
|
|
|
|
|
|
onUpdate(dt);
|
|
|
|
|
|
|
|
if (nestedMains == 1)
|
|
|
|
clearGarbage();
|
|
|
|
|
|
|
|
if (loopDone)
|
|
|
|
break;
|
|
|
|
|
|
|
|
updateCullData();
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
g_dbg_numRenderCalls = 0;
|
2013-04-22 21:36:31 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (settings.renderOn)
|
|
|
|
{
|
|
|
|
if (darkLayer.isUsed())
|
|
|
|
{
|
|
|
|
darkLayer.preRender();
|
|
|
|
}
|
|
|
|
|
|
|
|
render();
|
|
|
|
|
|
|
|
showBuffer();
|
|
|
|
|
|
|
|
BBGE_PROF(STOP);
|
|
|
|
|
|
|
|
if (nestedMains == 1)
|
|
|
|
clearGarbage();
|
|
|
|
|
|
|
|
frames++;
|
|
|
|
|
|
|
|
counter += dt;
|
|
|
|
if (counter > 1)
|
|
|
|
{
|
|
|
|
fps = frames;
|
|
|
|
frames = counter = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-22 01:29:57 +00:00
|
|
|
sound->setListenerPos(screenCenter.x, screenCenter.y);
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (doScreenshot)
|
|
|
|
{
|
|
|
|
doScreenshot = false;
|
|
|
|
|
|
|
|
saveScreenshotTGA(getScreenshotFilename());
|
|
|
|
prepScreen(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
quitNestedMainFlag = false;
|
|
|
|
if (nestedMains==1)
|
|
|
|
clearGarbage();
|
|
|
|
nestedMains--;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::clearBuffers()
|
|
|
|
{
|
2016-05-05 18:05:38 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setupRenderPositionAndScale()
|
|
|
|
{
|
|
|
|
glScalef(globalScale.x*globalResolutionScale.x*screenCapScale.x, globalScale.y*globalResolutionScale.y*screenCapScale.y, globalScale.z*globalResolutionScale.z);
|
|
|
|
glTranslatef(-(cameraPos.x+cameraOffset.x), -(cameraPos.y+cameraOffset.y), -(cameraPos.z+cameraOffset.z));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setupGlobalResolutionScale()
|
|
|
|
{
|
|
|
|
glScalef(globalResolutionScale.x, globalResolutionScale.y, globalResolutionScale.z);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::initFrameBuffer()
|
|
|
|
{
|
|
|
|
frameBuffer.init(-1, -1, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::setMouseConstraint(bool on)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
mouseConstraint = on;
|
|
|
|
}
|
|
|
|
|
2014-03-10 01:26:01 +00:00
|
|
|
void Core::setMouseConstraintCircle(const Vector& pos, float circle)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
mouseConstraint = true;
|
|
|
|
mouseCircle = circle;
|
2014-03-10 01:26:01 +00:00
|
|
|
mouseConstraintCenter = pos;
|
|
|
|
mouseConstraintCenter.z = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
int Core::getVirtualOffX()
|
|
|
|
{
|
|
|
|
return virtualOffX;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Core::getVirtualOffY()
|
|
|
|
{
|
|
|
|
return virtualOffY;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::centerMouse()
|
|
|
|
{
|
|
|
|
setMousePosition(Vector((virtualWidth/2) - core->getVirtualOffX(), virtualHeight/2));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::doMouseConstraint()
|
|
|
|
{
|
|
|
|
if (mouseConstraint)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2014-03-10 01:26:01 +00:00
|
|
|
Vector h = mouseConstraintCenter;
|
2011-08-03 20:05:33 +00:00
|
|
|
Vector d = mouse.position - h;
|
|
|
|
if (!d.isLength2DIn(mouseCircle))
|
|
|
|
{
|
|
|
|
d.setLength2D(mouseCircle);
|
|
|
|
mouse.position = h+d;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-07-18 21:14:20 +00:00
|
|
|
void Core::pollEvents(float dt)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
bool warpMouse=false;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (updateMouse)
|
|
|
|
{
|
|
|
|
int x, y;
|
2016-07-13 03:00:19 +00:00
|
|
|
unsigned mousestate = SDL_GetMouseState(&x,&y);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (mouse.buttonsEnabled)
|
|
|
|
{
|
|
|
|
mouse.buttons.left = mousestate & SDL_BUTTON(1)?DOWN:UP;
|
|
|
|
mouse.buttons.right = mousestate & SDL_BUTTON(3)?DOWN:UP;
|
|
|
|
mouse.buttons.middle = mousestate & SDL_BUTTON(2)?DOWN:UP;
|
|
|
|
|
2016-07-13 03:00:19 +00:00
|
|
|
for(unsigned i = 0; i < mouseExtraButtons; ++i)
|
|
|
|
mouse.buttons.extra[i] = mousestate & SDL_BUTTON(3+i)?DOWN:UP;
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
mouse.pure_buttons = mouse.buttons;
|
2016-07-13 03:00:19 +00:00
|
|
|
mouse.rawButtonMask = mousestate;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (flipMouseButtons)
|
|
|
|
{
|
|
|
|
std::swap(mouse.buttons.left, mouse.buttons.right);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mouse.buttons.left = mouse.buttons.right = mouse.buttons.middle = UP;
|
|
|
|
}
|
|
|
|
|
|
|
|
mouse.scrollWheelChange = 0;
|
|
|
|
mouse.change = Vector(0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SDL_Event event;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
while ( SDL_PollEvent (&event) ) {
|
|
|
|
switch (event.type) {
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
{
|
|
|
|
#if __APPLE__
|
2013-12-11 02:06:59 +00:00
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
|
|
if ((event.key.keysym.sym == SDLK_q) && (event.key.keysym.mod & KMOD_GUI))
|
|
|
|
#else
|
|
|
|
if ((event.key.keysym.sym == SDLK_q) && (event.key.keysym.mod & KMOD_META))
|
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
#else
|
|
|
|
if ((event.key.keysym.sym == SDLK_F4) && (event.key.keysym.mod & KMOD_ALT))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
quitNestedMain();
|
|
|
|
quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((event.key.keysym.sym == SDLK_g) && (event.key.keysym.mod & KMOD_CTRL))
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
grabInputOnReentry = (grabInputOnReentry)?0:-1;
|
|
|
|
setReentryInputGrab(1);
|
|
|
|
}
|
|
|
|
else if (_hasFocus)
|
|
|
|
{
|
2016-07-03 13:48:40 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
unsigned kidx = event.key.keysym.scancode;
|
|
|
|
#else
|
|
|
|
unsigned kidx = event.key.keysym.sym;
|
|
|
|
#endif
|
|
|
|
if(kidx < KEY_MAXARRAY)
|
|
|
|
keys[kidx] = 1;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_KEYUP:
|
|
|
|
{
|
|
|
|
if (_hasFocus)
|
|
|
|
{
|
2016-07-03 13:48:40 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
unsigned kidx = event.key.keysym.scancode;
|
|
|
|
#else
|
|
|
|
unsigned kidx = event.key.keysym.sym;
|
|
|
|
#endif
|
|
|
|
if(kidx < KEY_MAXARRAY)
|
|
|
|
keys[kidx] = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEMOTION:
|
|
|
|
{
|
|
|
|
if (_hasFocus && updateMouse)
|
|
|
|
{
|
|
|
|
mouse.lastPosition = mouse.position;
|
|
|
|
|
|
|
|
mouse.position.x = ((event.motion.x) * (float(virtualWidth)/float(getWindowWidth()))) - getVirtualOffX();
|
|
|
|
mouse.position.y = event.motion.y * (float(virtualHeight)/float(getWindowHeight()));
|
|
|
|
|
|
|
|
mouse.change = mouse.position - mouse.lastPosition;
|
|
|
|
|
|
|
|
if (doMouseConstraint()) warpMouse = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
{
|
|
|
|
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
|
|
|
|
{
|
|
|
|
SDL_Quit();
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
{
|
|
|
|
if (_hasFocus && updateMouse)
|
|
|
|
{
|
|
|
|
if (event.wheel.y > 0)
|
|
|
|
mouse.scrollWheelChange = 1;
|
|
|
|
else if (event.wheel.y < 0)
|
|
|
|
mouse.scrollWheelChange = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-06-25 21:59:34 +00:00
|
|
|
|
|
|
|
case SDL_JOYDEVICEADDED:
|
|
|
|
onJoystickAdded(event.jdevice.which);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_JOYDEVICEREMOVED:
|
|
|
|
onJoystickRemoved(event.jdevice.which);
|
|
|
|
break;
|
|
|
|
|
2013-07-18 21:29:55 +00:00
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
{
|
|
|
|
if (_hasFocus && updateMouse)
|
|
|
|
{
|
|
|
|
switch(event.button.button)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
mouse.scrollWheelChange = 1;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
mouse.scrollWheelChange = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
{
|
|
|
|
if (_hasFocus && updateMouse)
|
|
|
|
{
|
|
|
|
switch(event.button.button)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
mouse.scrollWheelChange = 1;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
mouse.scrollWheelChange = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-07-18 21:29:55 +00:00
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
case SDL_QUIT:
|
|
|
|
SDL_Quit();
|
|
|
|
_exit(0);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_SYSWMEVENT:
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (updateMouse)
|
|
|
|
{
|
|
|
|
mouse.scrollWheel += mouse.scrollWheelChange;
|
|
|
|
|
|
|
|
if (warpMouse)
|
|
|
|
{
|
|
|
|
setMousePosition(mouse.position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-18 21:14:20 +00:00
|
|
|
for(size_t i = 0; i < joysticks.size(); ++i)
|
|
|
|
if(joysticks[i])
|
|
|
|
joysticks[i]->update(dt);
|
|
|
|
|
|
|
|
// all input done; update button states
|
|
|
|
updateActionButtons();
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define _VLN(x, y, x2, y2) glVertex2f(x, y); glVertex2f(x2, y2);
|
|
|
|
|
|
|
|
void Core::print(int x, int y, const char *str, float sz)
|
|
|
|
{
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
glPushMatrix();
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
float xx = x;
|
|
|
|
float yy = y;
|
|
|
|
glTranslatef(x, y-0.5f*sz, 0);
|
|
|
|
x = y = 0;
|
|
|
|
xx = 0; yy = 0;
|
|
|
|
bool isLower = false, wasLower = false;
|
|
|
|
int c=0;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
glLineWidth(1);
|
|
|
|
glScalef(sz*0.75f, sz, 1);
|
|
|
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
|
|
|
|
while (str[c] != '\0')
|
|
|
|
{
|
|
|
|
if (str[c] <= 'z' && str[c] >= 'a')
|
|
|
|
isLower = true;
|
|
|
|
else
|
|
|
|
isLower = false;
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
switch(toupper(str[c]))
|
|
|
|
{
|
|
|
|
case '_':
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
_VLN(xx, y+0.5f, xx+0.25f, y+0.4f)
|
|
|
|
_VLN(xx+0.25f, y+0.4f, xx+0.75f, y+0.6f)
|
|
|
|
_VLN(xx+0.75f, y+0.6f, xx+1, y+0.5f)
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
_VLN(xx, y, xx+1, y+0.2f)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx+1, y+0.2f, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'E':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
break;
|
|
|
|
case 'G':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
_VLN(xx+0.5f, y, xx+0.5f, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'J':
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx, y+0.75f)
|
|
|
|
break;
|
|
|
|
case 'K':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.25f, xx+1, y)
|
|
|
|
_VLN(xx, y+0.25f, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+0.5f, y+0.5f)
|
|
|
|
_VLN(xx+1, y, xx+0.5f, y+0.5f)
|
|
|
|
break;
|
|
|
|
case 'N':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'O':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y)
|
|
|
|
break;
|
|
|
|
case 'Q':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y+0.5f, xx+1.25f, y+1.25f)
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+0.5f)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+0.5f, y, xx+0.5f, y+1)
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
_VLN(xx, y, xx+0.5f, y+1)
|
|
|
|
_VLN(xx+1, y, xx+0.5f, y+1)
|
|
|
|
break;
|
|
|
|
case 'W':
|
|
|
|
_VLN(xx, y, xx+0.25f, y+1)
|
|
|
|
_VLN(xx+0.25f, y+1, xx+0.5f, y+0.5f)
|
|
|
|
_VLN(xx+0.5f, y+0.5f, xx+0.75f, y+1)
|
|
|
|
_VLN(xx+1, y, xx+0.75f, y+1)
|
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
_VLN(xx, y, xx+1, y+1)
|
|
|
|
_VLN(xx+1, y, xx, y+1)
|
|
|
|
break;
|
|
|
|
case 'Y':
|
|
|
|
_VLN(xx, y, xx+0.5f, y+0.5f)
|
|
|
|
_VLN(xx+1, y, xx+0.5f, y+0.5f)
|
|
|
|
_VLN(xx+0.5f, y+0.5f, xx+0.5f, y+1)
|
|
|
|
break;
|
|
|
|
case 'Z':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y+1, xx+1, y)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '1':
|
|
|
|
_VLN(xx+0.5f, y, xx+0.5f, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx+0.5f, y, xx+0.25f, y+0.25f)
|
|
|
|
break;
|
|
|
|
case '2':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+1, y, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx, y+0.5f)
|
|
|
|
_VLN(xx, y+0.5f, xx, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '3':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '4':
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx+1, y, xx, y+0.5f)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
break;
|
|
|
|
case '5':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '6':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y+0.5f, xx, y+0.5f)
|
|
|
|
_VLN(xx+1, y+0.5f, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '7':
|
|
|
|
_VLN(xx+1, y, xx+0.5f, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
break;
|
|
|
|
case '8':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '9':
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y+0.5f, xx+1, y+0.5f)
|
|
|
|
_VLN(xx, y+0.5f, xx, y)
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
_VLN(xx, y, xx, y+1)
|
|
|
|
_VLN(xx+1, y, xx+1, y+1)
|
|
|
|
_VLN(xx, y+1, xx+1, y+1)
|
|
|
|
_VLN(xx, y, xx+1, y)
|
|
|
|
_VLN(xx, y, xx+1, y+1)
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
_VLN(xx+0.4f, y+1, xx+0.6f, y+1)
|
|
|
|
break;
|
|
|
|
case ',':
|
|
|
|
_VLN(xx+0.5f, y+0.75f, xx+0.5f, y+1.0f);
|
|
|
|
_VLN(xx+0.5f, y+1.0f, xx+0.2f, y+1.25f);
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
case '[':
|
|
|
|
_VLN(xx, y, xx, y+1);
|
|
|
|
_VLN(xx, y, xx+0.25f, y);
|
|
|
|
_VLN(xx, y+1, xx+0.25f, y+1);
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
case ']':
|
|
|
|
_VLN(xx+1, y, xx+1, y+1);
|
|
|
|
_VLN(xx+1, y, xx+0.75f, y);
|
|
|
|
_VLN(xx+1, y+1, xx+0.75f, y+1);
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
_VLN(xx+0.5f, y, xx+0.5f, y+0.25f);
|
|
|
|
_VLN(xx+0.5f, y+0.75f, xx+0.5f, y+1);
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
_VLN(xx, y+1, xx+1, y);
|
|
|
|
break;
|
|
|
|
default:
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isLower)
|
|
|
|
{
|
|
|
|
wasLower = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
c++;
|
|
|
|
xx += 1.4f;
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::cacheRender()
|
|
|
|
{
|
|
|
|
render();
|
|
|
|
// what if the screen was full white? then you wouldn't want to clear buffers
|
|
|
|
//clearBuffers();
|
|
|
|
showBuffer();
|
|
|
|
resetTimer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::updateCullData()
|
|
|
|
{
|
2014-03-07 16:59:36 +00:00
|
|
|
cullRadius = baseCullRadius * invGlobalScale;
|
|
|
|
cullRadiusSqr = cullRadius * cullRadius;
|
|
|
|
screenCenter = cullCenter = cameraPos + Vector(400.0f*invGlobalScale,300.0f*invGlobalScale);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::render(int startLayer, int endLayer, bool useFrameBufferIfAvail)
|
|
|
|
{
|
|
|
|
|
|
|
|
BBGE_PROF(Core_render);
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
if (startLayer == -1 && endLayer == -1 && overrideStartLayer != 0)
|
|
|
|
{
|
|
|
|
startLayer = overrideStartLayer;
|
|
|
|
endLayer = overrideEndLayer;
|
|
|
|
}
|
|
|
|
|
2014-03-07 16:59:36 +00:00
|
|
|
globalScaleChanged();
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (core->minimized) return;
|
|
|
|
onRender();
|
|
|
|
|
|
|
|
RenderObject::lastTextureApplied = 0;
|
|
|
|
|
|
|
|
updateCullData();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderObjectCount = 0;
|
|
|
|
processedRenderObjectCount = 0;
|
|
|
|
totalRenderObjectCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glLoadIdentity(); // Reset The View
|
|
|
|
clearBuffers();
|
|
|
|
|
|
|
|
if (afterEffectManager && frameBuffer.isInited() && useFrameBufferIfAvail)
|
|
|
|
{
|
|
|
|
frameBuffer.startCapture();
|
|
|
|
}
|
|
|
|
|
|
|
|
setupRenderPositionAndScale();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RenderObject::rlayer = 0;
|
|
|
|
|
|
|
|
for (int c = 0; c < renderObjectLayerOrder.size(); c++)
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
int i = renderObjectLayerOrder[c];
|
|
|
|
if (i == -1) continue;
|
|
|
|
if ((startLayer != -1 && endLayer != -1) && (i < startLayer || i > endLayer)) continue;
|
|
|
|
|
2011-11-20 14:44:17 +00:00
|
|
|
if (i == postProcessingFx.layer)
|
|
|
|
{
|
|
|
|
postProcessingFx.preRender();
|
|
|
|
}
|
|
|
|
if (i == postProcessingFx.renderLayer)
|
|
|
|
{
|
|
|
|
postProcessingFx.render();
|
|
|
|
}
|
2011-10-31 17:28:57 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (darkLayer.isUsed() )
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
if (i == darkLayer.getRenderLayer())
|
|
|
|
{
|
|
|
|
darkLayer.render();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == darkLayer.getLayer() && startLayer != i)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-15 01:00:20 +00:00
|
|
|
if (afterEffectManager && afterEffectManager->active && i == afterEffectManagerLayer)
|
|
|
|
{
|
|
|
|
afterEffectManager->render();
|
|
|
|
}
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
RenderObjectLayer *r = &renderObjectLayers[i];
|
|
|
|
RenderObject::rlayer = r;
|
|
|
|
if (r->visible)
|
|
|
|
{
|
|
|
|
if (r->startPass == r->endPass)
|
|
|
|
{
|
|
|
|
r->renderPass(RenderObject::RENDER_ALL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (int pass = r->startPass; pass <= r->endPass; pass++)
|
|
|
|
{
|
|
|
|
r->renderPass(pass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::showBuffer()
|
|
|
|
{
|
|
|
|
BBGE_PROF(Core_showBuffer);
|
2013-07-18 21:29:55 +00:00
|
|
|
#ifdef BBGE_BUILD_SDL2
|
|
|
|
SDL_GL_SwapWindow(gScreen);
|
2016-05-05 01:49:41 +00:00
|
|
|
#else
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_GL_SwapBuffers();
|
2013-07-18 21:29:55 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
#endif
|
2013-07-18 21:29:55 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: only for use during shutdown
|
|
|
|
// otherwise, textures will try to remove themselves
|
|
|
|
// when destroy is called on them
|
|
|
|
void Core::clearResources()
|
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
if(resources.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
debugLog("Warning: The following resources were not cleared:");
|
|
|
|
for(size_t i = 0; i < resources.size(); ++i)
|
|
|
|
debugLog(resources[i]->name);
|
|
|
|
resources.clear(); // nothing we can do; refcounting is messed up
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::shutdownInputLibrary()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::shutdownJoystickLibrary()
|
|
|
|
{
|
|
|
|
if (joystickEnabled) {
|
2016-07-03 16:07:13 +00:00
|
|
|
clearJoysticks();
|
2011-08-03 20:05:33 +00:00
|
|
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
|
|
|
joystickEnabled = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::clearRenderObjects()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < renderObjectLayers.size(); i++)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
RenderObject *r = renderObjectLayers[i].getFirst();
|
|
|
|
while (r)
|
|
|
|
{
|
|
|
|
if (r)
|
|
|
|
{
|
|
|
|
removeRenderObject(r, DESTROY_RENDER_OBJECT);
|
|
|
|
}
|
|
|
|
r = renderObjectLayers[i].getNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::shutdown()
|
|
|
|
{
|
|
|
|
// pop all the states
|
|
|
|
|
|
|
|
|
|
|
|
debugLog("Core::shutdown");
|
|
|
|
shuttingDown = true;
|
|
|
|
|
|
|
|
debugLog("Shutdown Joystick Library...");
|
|
|
|
shutdownJoystickLibrary();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Shutdown Input Library...");
|
|
|
|
shutdownInputLibrary();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Shutdown All States...");
|
|
|
|
popAllStates();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Clear State Instances...");
|
|
|
|
clearStateInstances();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Clear All Remaining RenderObjects...");
|
|
|
|
clearRenderObjects();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Clear All Resources...");
|
|
|
|
clearResources();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
|
|
|
|
debugLog("Clear State Objects...");
|
|
|
|
clearStateObjects();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
if (afterEffectManager)
|
|
|
|
{
|
|
|
|
debugLog("Delete AEManager...");
|
|
|
|
delete afterEffectManager;
|
|
|
|
afterEffectManager = 0;
|
|
|
|
debugLog("OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sound)
|
|
|
|
{
|
|
|
|
debugLog("Shutdown Sound Library...");
|
|
|
|
sound->stopAll();
|
|
|
|
delete sound;
|
|
|
|
sound = 0;
|
|
|
|
debugLog("OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
debugLog("Core's framebuffer...");
|
|
|
|
frameBuffer.unloadDevice();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
debugLog("Shutdown Graphics Library...");
|
|
|
|
shutdownGraphicsLibrary();
|
|
|
|
debugLog("OK");
|
|
|
|
|
|
|
|
|
|
|
|
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
#ifdef BBGE_BUILD_VFS
|
|
|
|
debugLog("Unload VFS...");
|
|
|
|
vfs.Clear();
|
|
|
|
debugLog("OK");
|
|
|
|
#endif
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
debugLog("SDL Quit...");
|
|
|
|
SDL_Quit();
|
|
|
|
debugLog("OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
//util funcs
|
|
|
|
|
|
|
|
bool Core::exists(const std::string &filename)
|
|
|
|
{
|
2011-08-11 00:26:46 +00:00
|
|
|
return ::exists(filename, false); // defined in Base.cpp
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 23:06:51 +00:00
|
|
|
CountedPtr<Texture> Core::findTexture(const std::string &name)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
int sz = resources.size();
|
|
|
|
for (int i = 0; i < sz; i++)
|
|
|
|
{
|
|
|
|
//out << resources[i]->name << " is " << name << " ?" << std::endl;
|
|
|
|
//NOTE: ensure all names are lowercase before this point
|
|
|
|
if (resources[i]->name == name)
|
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
return resources[i];
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-20 22:47:24 +00:00
|
|
|
// This handles unix/win32 relative paths: ./rel/path
|
|
|
|
// Unix abs paths: /home/user/...
|
|
|
|
// Win32 abs paths: C:/Stuff/.. and also C:\Stuff\...
|
|
|
|
#define ISPATHROOT(x) (x[0] == '.' || x[0] == '/' || ((x).length() > 1 && x[1] == ':'))
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
std::string Core::getTextureLoadName(const std::string &texture)
|
|
|
|
{
|
|
|
|
std::string loadName = texture;
|
|
|
|
|
|
|
|
if (texture.empty() || !ISPATHROOT(texture))
|
|
|
|
{
|
|
|
|
if (texture.find(baseTextureDirectory) == std::string::npos)
|
|
|
|
loadName = baseTextureDirectory + texture;
|
|
|
|
}
|
|
|
|
return loadName;
|
|
|
|
}
|
|
|
|
|
2016-03-13 00:37:43 +00:00
|
|
|
CountedPtr<Texture> Core::doTextureAdd(const std::string &texture, const std::string &loadName, std::string internalTextureName)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
if (texture.empty() || !ISPATHROOT(texture))
|
|
|
|
{
|
|
|
|
if (texture.find(baseTextureDirectory) != std::string::npos)
|
|
|
|
internalTextureName = internalTextureName.substr(baseTextureDirectory.size(), internalTextureName.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (internalTextureName.size() > 4)
|
|
|
|
{
|
|
|
|
if (internalTextureName[internalTextureName.size()-4] == '.')
|
|
|
|
{
|
|
|
|
internalTextureName = internalTextureName.substr(0, internalTextureName.size()-4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stringToLowerUserData(internalTextureName);
|
2015-03-23 23:06:51 +00:00
|
|
|
CountedPtr<Texture> t = core->findTexture(internalTextureName);
|
2011-08-03 20:05:33 +00:00
|
|
|
if (t)
|
2016-03-13 00:37:43 +00:00
|
|
|
return t;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
t = new Texture;
|
|
|
|
t->name = internalTextureName;
|
|
|
|
|
2016-03-15 02:38:01 +00:00
|
|
|
if(t->load(loadName))
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "LOADED TEXTURE FROM DISK: [" << internalTextureName << "] idx: " << resources.size()-1;
|
|
|
|
debugLog(os.str());
|
|
|
|
}
|
|
|
|
else
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
t->width = 64;
|
|
|
|
t->height = 64;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 00:37:43 +00:00
|
|
|
return t;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 00:37:43 +00:00
|
|
|
CountedPtr<Texture> Core::addTexture(const std::string &textureName)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
BBGE_PROF(Core_addTexture);
|
|
|
|
|
2015-06-09 23:49:12 +00:00
|
|
|
if (textureName.empty())
|
|
|
|
return NULL;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-03-13 00:37:43 +00:00
|
|
|
CountedPtr<Texture> ptex;
|
2011-08-03 20:05:33 +00:00
|
|
|
std::string texture = textureName;
|
|
|
|
stringToLowerUserData(texture);
|
|
|
|
std::string internalTextureName = texture;
|
|
|
|
std::string loadName = getTextureLoadName(texture);
|
|
|
|
|
|
|
|
if (!texture.empty() && texture[0] == '@')
|
|
|
|
{
|
|
|
|
texture = secondaryTexturePath + texture.substr(1, texture.size());
|
|
|
|
loadName = texture;
|
|
|
|
}
|
|
|
|
else if (!secondaryTexturePath.empty() && texture[0] != '.' && texture[0] != '/')
|
|
|
|
{
|
|
|
|
std::string t = texture;
|
|
|
|
std::string ln = loadName;
|
|
|
|
texture = secondaryTexturePath + texture;
|
|
|
|
loadName = texture;
|
2016-03-13 00:37:43 +00:00
|
|
|
ptex = doTextureAdd(texture, loadName, internalTextureName);
|
|
|
|
if (!ptex || ptex->getLoadResult() == TEX_FAILED)
|
|
|
|
ptex = doTextureAdd(t, ln, internalTextureName);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
else
|
2016-03-13 00:37:43 +00:00
|
|
|
ptex = doTextureAdd(texture, loadName, internalTextureName);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2016-03-13 00:37:43 +00:00
|
|
|
addTexture(ptex.content());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2015-03-23 23:06:51 +00:00
|
|
|
if(debugLogTextures)
|
|
|
|
{
|
2016-03-15 02:38:01 +00:00
|
|
|
if(!ptex || ptex->getLoadResult() != TEX_SUCCESS)
|
2015-03-23 23:06:51 +00:00
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "FAILED TO LOAD TEXTURE: [" << internalTextureName << "] idx: " << resources.size()-1;
|
|
|
|
debugLog(os.str());
|
|
|
|
}
|
|
|
|
}
|
2016-03-13 00:37:43 +00:00
|
|
|
return ptex;
|
2015-03-23 23:06:51 +00:00
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
void Core::addRenderObject(RenderObject *o, int layer)
|
|
|
|
{
|
|
|
|
if (!o) return;
|
|
|
|
o->layer = layer;
|
|
|
|
if (layer < 0 || layer >= renderObjectLayers.size())
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "attempted to add render object to invalid layer [" << layer << "]";
|
|
|
|
errorLog(os.str());
|
|
|
|
}
|
|
|
|
renderObjectLayers[layer].add(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::switchRenderObjectLayer(RenderObject *o, int toLayer)
|
|
|
|
{
|
|
|
|
if (!o) return;
|
|
|
|
renderObjectLayers[o->layer].remove(o);
|
|
|
|
renderObjectLayers[toLayer].add(o);
|
|
|
|
o->layer = toLayer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::unloadResources()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < resources.size(); i++)
|
|
|
|
{
|
|
|
|
resources[i]->unload();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::onReloadResources()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::reloadResources()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < resources.size(); i++)
|
|
|
|
{
|
|
|
|
resources[i]->reload();
|
|
|
|
}
|
|
|
|
onReloadResources();
|
|
|
|
}
|
|
|
|
|
2015-03-23 23:06:51 +00:00
|
|
|
void Core::addTexture(Texture *r)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
for(size_t i = 0; i < resources.size(); ++i)
|
|
|
|
if(resources[i] == r)
|
|
|
|
return;
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
resources.push_back(r);
|
|
|
|
if (r->name.empty())
|
|
|
|
{
|
|
|
|
debugLog("Empty name resource added");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-23 23:06:51 +00:00
|
|
|
void Core::removeTexture(Texture *res)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
std::vector<Texture*> copy;
|
|
|
|
copy.swap(resources);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2015-03-23 23:06:51 +00:00
|
|
|
for (size_t i = 0; i < copy.size(); ++i)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
if (copy[i] == res)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2015-03-23 23:06:51 +00:00
|
|
|
copy[i]->destroy();
|
|
|
|
copy[i] = copy.back();
|
|
|
|
copy.pop_back();
|
|
|
|
break;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-23 23:06:51 +00:00
|
|
|
|
|
|
|
resources.swap(copy);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::deleteRenderObjectMemory(RenderObject *r)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
delete r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag)
|
|
|
|
{
|
|
|
|
if (r)
|
|
|
|
{
|
|
|
|
if (r->layer != LR_NONE && !renderObjectLayers[r->layer].empty())
|
|
|
|
{
|
|
|
|
renderObjectLayers[r->layer].remove(r);
|
|
|
|
}
|
|
|
|
if (flag != DO_NOT_DESTROY_RENDER_OBJECT )
|
|
|
|
{
|
|
|
|
r->destroy();
|
|
|
|
|
|
|
|
deleteRenderObjectMemory(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Core::enqueueRenderObjectDeletion(RenderObject *object)
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
if (!object->_dead)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
garbage.push_back (object);
|
|
|
|
object->_dead = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::clearGarbage()
|
|
|
|
{
|
|
|
|
BBGE_PROF(Core_clearGarbage);
|
|
|
|
// HACK: optimize this (use a list instead of a queue)
|
|
|
|
|
2016-06-25 22:39:48 +00:00
|
|
|
for (RenderObjects::iterator i = garbage.begin(); i != garbage.end(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
removeRenderObject(*i, DO_NOT_DESTROY_RENDER_OBJECT);
|
|
|
|
|
|
|
|
(*i)->destroy();
|
|
|
|
}
|
|
|
|
|
2016-06-25 22:39:48 +00:00
|
|
|
for (RenderObjects::iterator i = garbage.begin(); i != garbage.end(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
|
|
|
deleteRenderObjectMemory(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
garbage.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::canChangeState()
|
|
|
|
{
|
|
|
|
return (nestedMains<=1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Take a screenshot of the specified region of the screen and store it
|
|
|
|
// in a 32bpp pixel buffer. delete[] the returned buffer when it's no
|
|
|
|
// longer needed.
|
|
|
|
unsigned char *Core::grabScreenshot(int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
|
|
|
|
unsigned char *imageData;
|
|
|
|
|
|
|
|
unsigned int size = sizeof(unsigned char) * w * h * 4;
|
|
|
|
imageData = new unsigned char[size];
|
|
|
|
|
|
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND);
|
|
|
|
glDisable(GL_DEPTH_TEST); glDisable(GL_DITHER); glDisable(GL_FOG);
|
|
|
|
glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP);
|
|
|
|
glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_1D);
|
|
|
|
glDisable(GL_TEXTURE_2D); glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
|
|
|
|
glPixelTransferi(GL_RED_SCALE, 1); glPixelTransferi(GL_RED_BIAS, 0);
|
|
|
|
glPixelTransferi(GL_GREEN_SCALE, 1); glPixelTransferi(GL_GREEN_BIAS, 0);
|
|
|
|
glPixelTransferi(GL_BLUE_SCALE, 1); glPixelTransferi(GL_BLUE_BIAS, 0);
|
|
|
|
glPixelTransferi(GL_ALPHA_SCALE, 1); glPixelTransferi(GL_ALPHA_BIAS, 0);
|
|
|
|
glRasterPos2i(0, 0);
|
|
|
|
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)imageData);
|
|
|
|
glPopAttrib();
|
|
|
|
|
|
|
|
// Force all alpha values to 255.
|
|
|
|
unsigned char *c = imageData;
|
|
|
|
for (int x = 0; x < w; x++)
|
|
|
|
{
|
|
|
|
for (int y = 0; y < h; y++, c += 4)
|
|
|
|
{
|
|
|
|
c[3] = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return imageData;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Like grabScreenshot(), but grab from the center of the screen.
|
|
|
|
unsigned char *Core::grabCenteredScreenshot(int w, int h)
|
|
|
|
{
|
|
|
|
return grabScreenshot(core->width/2 - w/2, core->height/2 - h/2, w, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
// takes a screen shot and saves it to a TGA image
|
|
|
|
int Core::saveScreenshotTGA(const std::string &filename)
|
|
|
|
{
|
|
|
|
int w = getWindowWidth(), h = getWindowHeight();
|
|
|
|
unsigned char *imageData = grabCenteredScreenshot(w, h);
|
|
|
|
return tgaSave(filename.c_str(),w,h,32,imageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::saveCenteredScreenshotTGA(const std::string &filename, int sz)
|
|
|
|
{
|
|
|
|
int w=sz, h=sz;
|
|
|
|
int hsm = (w * 3.0f) / 4.0f;
|
|
|
|
unsigned char *imageData = grabCenteredScreenshot(w, hsm);
|
|
|
|
|
|
|
|
int imageDataSize = sizeof(unsigned char) * w * hsm * 4;
|
|
|
|
int tgaImageSize = sizeof(unsigned char) * w * h * 4;
|
|
|
|
unsigned char *tgaImage = new unsigned char[tgaImageSize];
|
|
|
|
memcpy(tgaImage, imageData, imageDataSize);
|
|
|
|
memset(tgaImage + imageDataSize, 0, tgaImageSize - imageDataSize);
|
|
|
|
delete[] imageData;
|
|
|
|
|
|
|
|
int savebits = 32;
|
|
|
|
tgaSave(filename.c_str(),w,h,savebits,tgaImage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::saveSizedScreenshotTGA(const std::string &filename, int sz, int crop34)
|
|
|
|
{
|
|
|
|
debugLog("saveSizedScreenshot");
|
|
|
|
|
|
|
|
int w, h;
|
|
|
|
unsigned char *imageData;
|
|
|
|
w = sz;
|
|
|
|
h = sz;
|
|
|
|
float fsz = (float)sz;
|
|
|
|
|
|
|
|
unsigned int size = sizeof(unsigned char) * w * h * 3;
|
|
|
|
imageData = (unsigned char *)malloc(size);
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
float wbit = fsz;
|
2011-08-03 20:05:33 +00:00
|
|
|
float hbit = ((fsz)*(3.0f/4.0f));
|
|
|
|
|
|
|
|
int width = core->width-1;
|
|
|
|
int height = core->height-1;
|
|
|
|
int diff = 0;
|
|
|
|
|
|
|
|
if (crop34)
|
|
|
|
{
|
|
|
|
width = int((core->height*4.0f)/3.0f);
|
|
|
|
diff = (core->width - width)/2;
|
|
|
|
width--;
|
|
|
|
}
|
|
|
|
|
|
|
|
float zx = wbit/(float)width;
|
|
|
|
float zy = hbit/(float)height;
|
|
|
|
|
|
|
|
float copyw = w*(1/zx);
|
|
|
|
float copyh = h*(1/zy);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "wbit: " << wbit << " hbit: " << hbit << std::endl;
|
|
|
|
os << "zx: " << zx << " zy: " << zy << std::endl;
|
|
|
|
os << "w: " << w << " h: " << h << std::endl;
|
|
|
|
os << "width: " << width << " height: " << height << std::endl;
|
|
|
|
os << "copyw: " << copyw << " copyh: " << copyh << std::endl;
|
|
|
|
debugLog(os.str());
|
|
|
|
|
|
|
|
glRasterPos2i(0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debugLog("pixel zoom");
|
|
|
|
glPixelZoom(zx,zy);
|
|
|
|
glFlush();
|
|
|
|
|
|
|
|
glPixelZoom(1,1);
|
|
|
|
debugLog("copy pixels");
|
|
|
|
glCopyPixels(diff, 0, width, height, GL_COLOR);
|
|
|
|
glFlush();
|
|
|
|
|
|
|
|
debugLog("read pixels");
|
|
|
|
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)imageData);
|
|
|
|
glFlush();
|
|
|
|
|
|
|
|
int savebits = 24;
|
|
|
|
debugLog("saving bpp");
|
|
|
|
tgaSave(filename.c_str(),w,h,savebits,imageData);
|
|
|
|
|
|
|
|
debugLog("pop");
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
debugLog("done");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::save64x64ScreenshotTGA(const std::string &filename)
|
|
|
|
{
|
|
|
|
int w, h;
|
|
|
|
unsigned char *imageData;
|
|
|
|
|
|
|
|
// compute width and heidth of the image
|
|
|
|
//w = xmax - xmin;
|
|
|
|
//h = ymax - ymin;
|
|
|
|
w = 64;
|
|
|
|
h = 64;
|
|
|
|
|
|
|
|
// allocate memory for the pixels
|
|
|
|
imageData = (unsigned char *)malloc(sizeof(unsigned char) * w * h * 4);
|
|
|
|
|
|
|
|
// read the pixels from the frame buffer
|
|
|
|
|
|
|
|
//glReadPixels(xmin,ymin,xmax,ymax,GL_RGBA,GL_UNSIGNED_BYTE, (GLvoid *)imageData);
|
|
|
|
glPixelZoom(64.0f/(float)getVirtualWidth(), 48.0f/(float)getVirtualHeight());
|
|
|
|
glCopyPixels(0, 0, getVirtualWidth(), getVirtualHeight(), GL_COLOR);
|
|
|
|
|
|
|
|
glReadPixels(0,0,64,64,GL_RGBA,GL_UNSIGNED_BYTE, (GLvoid *)imageData);
|
|
|
|
|
|
|
|
|
|
|
|
unsigned char *c = imageData;
|
|
|
|
for (int x=0; x < w; x++)
|
|
|
|
{
|
|
|
|
for (int y=0; y< h; y++)
|
|
|
|
{
|
|
|
|
c += 3;
|
|
|
|
(*c) = 255;
|
|
|
|
c ++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// save the image
|
|
|
|
tgaSave(filename.c_str(),64,64,32,imageData);
|
|
|
|
glPixelZoom(1,1);
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
// saves an array of pixels as a TGA image (frees the image data passed in)
|
|
|
|
int Core::tgaSave( const char *filename,
|
|
|
|
short int width,
|
|
|
|
short int height,
|
|
|
|
unsigned char pixelDepth,
|
|
|
|
unsigned char *imageData) {
|
|
|
|
|
|
|
|
unsigned char cGarbage = 0, type,mode,aux;
|
|
|
|
short int iGarbage = 0;
|
|
|
|
int i;
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
// open file and check for errors
|
|
|
|
file = fopen(adjustFilenameCase(filename).c_str(), "wb");
|
|
|
|
if (file == NULL) {
|
2011-11-20 21:58:36 +00:00
|
|
|
delete [] imageData;
|
2011-08-03 20:05:33 +00:00
|
|
|
return (int)false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute image type: 2 for RGB(A), 3 for greyscale
|
|
|
|
mode = pixelDepth / 8;
|
|
|
|
if ((pixelDepth == 24) || (pixelDepth == 32))
|
|
|
|
type = 2;
|
|
|
|
else
|
|
|
|
type = 3;
|
|
|
|
|
|
|
|
// write the header
|
|
|
|
if (fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
|
|
|
|
|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
|
|
|
|
|| fwrite(&type, sizeof(unsigned char), 1, file) != 1
|
|
|
|
|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
|
|
|
|
|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&width, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&height, sizeof(short int), 1, file) != 1
|
|
|
|
|| fwrite(&pixelDepth, sizeof(unsigned char), 1, file) != 1
|
|
|
|
|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1)
|
|
|
|
{
|
|
|
|
fclose(file);
|
2011-11-20 21:58:36 +00:00
|
|
|
delete [] imageData;
|
2011-08-03 20:05:33 +00:00
|
|
|
return (int)false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert the image data from RGB(A) to BGR(A)
|
|
|
|
if (mode >= 3)
|
|
|
|
for (i=0; i < width * height * mode ; i+= mode) {
|
|
|
|
aux = imageData[i];
|
|
|
|
imageData[i] = imageData[i+2];
|
|
|
|
imageData[i+2] = aux;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save the image data
|
|
|
|
if (fwrite(imageData, sizeof(unsigned char),
|
|
|
|
width * height * mode, file) != width * height * mode)
|
|
|
|
{
|
|
|
|
fclose(file);
|
2011-11-20 21:58:36 +00:00
|
|
|
delete [] imageData;
|
2011-08-03 20:05:33 +00:00
|
|
|
return (int)false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
2011-11-20 21:58:36 +00:00
|
|
|
delete [] imageData;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
return (int)true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// saves a series of files with names "filenameX"
|
|
|
|
int Core::tgaSaveSeries(char *filename,
|
|
|
|
short int width,
|
|
|
|
short int height,
|
|
|
|
unsigned char pixelDepth,
|
|
|
|
unsigned char *imageData) {
|
|
|
|
|
|
|
|
char *newFilename;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
// compute the new filename by adding the
|
|
|
|
// series number and the extension
|
2016-07-14 03:01:30 +00:00
|
|
|
newFilename = (char *)malloc(sizeof(char) * strlen(filename)+16);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
sprintf(newFilename,"%s%d",filename,numSavedScreenshots);
|
|
|
|
|
|
|
|
// save the image
|
|
|
|
status = tgaSave(newFilename,width,height,pixelDepth,imageData);
|
|
|
|
|
|
|
|
//increase the counter
|
|
|
|
if (status == (int)true)
|
|
|
|
numSavedScreenshots++;
|
|
|
|
free(newFilename);
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
|
2016-07-14 03:01:30 +00:00
|
|
|
void Core::screenshot()
|
|
|
|
{
|
|
|
|
doScreenshot = true;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
|
|
|
#include "DeflateCompressor.h"
|
|
|
|
|
|
|
|
// saves an array of pixels as a TGA image (frees the image data passed in)
|
|
|
|
int Core::zgaSave( const char *filename,
|
|
|
|
short int w,
|
|
|
|
short int h,
|
|
|
|
unsigned char depth,
|
|
|
|
unsigned char *imageData) {
|
|
|
|
|
|
|
|
ByteBuffer::uint8 type,mode,aux, pixelDepth = depth;
|
|
|
|
ByteBuffer::uint8 cGarbage = 0;
|
|
|
|
ByteBuffer::uint16 iGarbage = 0;
|
|
|
|
ByteBuffer::uint16 width = w, height = h;
|
|
|
|
|
|
|
|
// open file and check for errors
|
|
|
|
FILE *file = fopen(adjustFilenameCase(filename).c_str(), "wb");
|
|
|
|
if (file == NULL) {
|
|
|
|
delete [] imageData;
|
|
|
|
return (int)false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute image type: 2 for RGB(A), 3 for greyscale
|
|
|
|
mode = pixelDepth / 8;
|
|
|
|
if ((pixelDepth == 24) || (pixelDepth == 32))
|
|
|
|
type = 2;
|
|
|
|
else
|
|
|
|
type = 3;
|
|
|
|
|
|
|
|
// convert the image data from RGB(A) to BGR(A)
|
|
|
|
if (mode >= 3)
|
|
|
|
for (int i=0; i < width * height * mode ; i+= mode) {
|
|
|
|
aux = imageData[i];
|
|
|
|
imageData[i] = imageData[i+2];
|
|
|
|
imageData[i+2] = aux;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZlibCompressor z;
|
|
|
|
z.SetForceCompression(true);
|
|
|
|
z.reserve(width * height * mode + 30);
|
|
|
|
z << cGarbage
|
|
|
|
<< cGarbage
|
|
|
|
<< type
|
|
|
|
<< iGarbage
|
|
|
|
<< iGarbage
|
|
|
|
<< cGarbage
|
|
|
|
<< iGarbage
|
|
|
|
<< iGarbage
|
|
|
|
<< width
|
|
|
|
<< height
|
|
|
|
<< pixelDepth
|
|
|
|
<< cGarbage;
|
|
|
|
|
|
|
|
z.append(imageData, width * height * mode);
|
|
|
|
z.Compress(3);
|
|
|
|
|
|
|
|
// save the image data
|
|
|
|
if (fwrite(z.contents(), 1, z.size(), file) != z.size())
|
|
|
|
{
|
|
|
|
fclose(file);
|
|
|
|
delete [] imageData;
|
|
|
|
return (int)false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
delete [] imageData;
|
|
|
|
|
|
|
|
return (int)true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
#ifdef BBGE_BUILD_VFS
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
#include "ttvfs_zip/VFSZipArchiveLoader.h"
|
2016-07-09 02:18:40 +00:00
|
|
|
#include "ttvfs.h"
|
|
|
|
#include "ttvfs_stdio.h"
|
|
|
|
#endif
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
|
|
|
void Core::setupFileAccess()
|
|
|
|
{
|
|
|
|
#ifdef BBGE_BUILD_VFS
|
|
|
|
debugLog("Init VFS...");
|
|
|
|
|
|
|
|
if(!ttvfs::checkCompat())
|
2013-06-23 16:50:10 +00:00
|
|
|
exit_error("ttvfs not compatible");
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
2014-04-07 00:10:05 +00:00
|
|
|
ttvfs_setroot(&vfs);
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
2014-04-07 00:10:05 +00:00
|
|
|
vfs.AddLoader(new ttvfs::DiskLoader);
|
|
|
|
vfs.AddArchiveLoader(new ttvfs::VFSZipArchiveLoader);
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
2014-04-07 00:10:05 +00:00
|
|
|
vfs.Mount("override", "");
|
2013-06-19 16:44:24 +00:00
|
|
|
|
|
|
|
// If we ever want to read from a container...
|
2014-04-07 00:10:05 +00:00
|
|
|
//vfs.AddArchive("aqfiles.zip");
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
|
2013-06-19 00:08:24 +00:00
|
|
|
if(_extraDataDir.length())
|
2013-06-19 16:45:19 +00:00
|
|
|
{
|
|
|
|
debugLog("Mounting extra data dir: " + _extraDataDir);
|
2014-04-07 00:10:05 +00:00
|
|
|
vfs.Mount(_extraDataDir.c_str(), "");
|
2013-06-19 16:45:19 +00:00
|
|
|
}
|
2013-06-19 00:08:24 +00:00
|
|
|
|
[vfs, #3] All file reading code goes through the VFS now, new mod downloader & mod selector in place. Also a bunch of other stuff. (...)
- HTTP networking support, mods can be downloaded via the builtin downloader.
All network activity runs in a seperate thread, which is started
as soon as any network activity is requested.
- The master server is hard-coded to fg.wzff.de/aqmods/ if not specified otherwise;
this setting can be overridden in the config file.
- The mod selector screen is now a grid-view for much better navigation;
also works with joystick.
- VFS code is functionally similar to the old molebox-packed release
for win32. The game could also have its data shipped in a Zip file
or any other kind of archive.
- It is still possible to build without VFS support, but then the mod
downloader and soft-patching will not be available.
The full commit history can be found here:
https://github.com/fgenesis/Aquaria_clean/compare/master...vfs
The most important commit messages follow:
[...]
This replaces all std::ifstream with InStream, and fopen(), ... with vfopen(), ...
Some code is #ifdef'd for better performance and less memory-copying.
VFILE is defined to whatever type of file is in use:
- FILE if BBGE_BUILD_VFS is not defined
- tttvfs::VFSFile if it is.
Other changes:
- [un]packFile() is now unused and obsolete. That code has not been adjusted to use VFILE.
- glpng can now load from a memory buffer.
- TinyXML uses the VFS for reading operations now.
- The rather clunky binary stream loading of glfont2 got replaced with ByteBuffer,
which gets its data in one block (necessary to use the VFS without implementing
a somewhat STL-compliant std::ifstream replacement.)
-------------
Implement loading mods from zip files.
-------------
Implement soft-patching game data files. (Replacing textures/audio/... on the fly)
-------------
Misc bits:
- Extended GUI focus handling a bit
- Fixed weirdness in texture loading... not sure but this seems more correct to me.
Actually, considering that the texture will have its native size after restarting the game,
the lines removed with this commit seem pretty useless.
2012-06-01 15:52:19 +00:00
|
|
|
debugLog("Done");
|
|
|
|
#endif
|
|
|
|
}
|
2013-11-14 16:58:33 +00:00
|
|
|
|
|
|
|
void Core::initLocalization()
|
|
|
|
{
|
|
|
|
InStream in(localisePath("data/localecase.txt"));
|
|
|
|
if(!in)
|
|
|
|
{
|
|
|
|
debugLog("data/localecase.txt does not exist, using internal locale data");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string low, up;
|
2016-07-09 02:18:40 +00:00
|
|
|
CharTranslationTable trans;
|
|
|
|
memset(&trans[0], -1, sizeof(trans));
|
2013-11-14 16:58:33 +00:00
|
|
|
while(in)
|
|
|
|
{
|
|
|
|
in >> low >> up;
|
2016-07-09 02:18:40 +00:00
|
|
|
|
2016-07-18 01:21:24 +00:00
|
|
|
trans[(unsigned char)(low[0])] = (unsigned char)up[0];
|
2013-11-14 16:58:33 +00:00
|
|
|
}
|
2016-07-09 02:18:40 +00:00
|
|
|
initCharTranslationTables(&trans);
|
2013-11-14 16:58:33 +00:00
|
|
|
}
|
|
|
|
|
2016-06-25 21:59:34 +00:00
|
|
|
void Core::onJoystickAdded(int deviceID)
|
|
|
|
{
|
2016-06-30 00:58:55 +00:00
|
|
|
debugLog("Add new joystick");
|
|
|
|
Joystick *j = new Joystick;
|
|
|
|
j->init(deviceID);
|
|
|
|
for(size_t i = 0; i < joysticks.size(); ++i)
|
|
|
|
if(!joysticks[i])
|
|
|
|
{
|
|
|
|
joysticks[i] = j;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
joysticks.push_back(j);
|
|
|
|
done:
|
2016-07-17 03:54:09 +00:00
|
|
|
;
|
2016-06-25 21:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::onJoystickRemoved(int instanceID)
|
|
|
|
{
|
2016-07-03 13:48:40 +00:00
|
|
|
debugLog("Joystick removed");
|
2016-06-30 00:58:55 +00:00
|
|
|
for(size_t i = 0; i < joysticks.size(); ++i)
|
2016-07-17 03:54:09 +00:00
|
|
|
if(Joystick *j = joysticks[i])
|
|
|
|
if(j->getInstanceID() == instanceID)
|
|
|
|
{
|
|
|
|
j->shutdown();
|
|
|
|
delete j;
|
|
|
|
joysticks[i] = NULL;
|
|
|
|
}
|
2016-06-25 21:59:34 +00:00
|
|
|
}
|
2016-07-15 01:22:27 +00:00
|
|
|
|
|
|
|
Joystick *Core::getJoystick(int idx)
|
|
|
|
{
|
|
|
|
size_t i = idx;
|
|
|
|
return i < joysticks.size() ? joysticks[i] : NULL;
|
|
|
|
}
|
2016-07-17 20:25:24 +00:00
|
|
|
|
|
|
|
void Core::updateActionButtons()
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < actionStatus.size(); ++i)
|
|
|
|
actionStatus[i]->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::clearActionButtons()
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < actionStatus.size(); ++i)
|
|
|
|
delete actionStatus[i];
|
|
|
|
actionStatus.clear();
|
|
|
|
}
|
|
|
|
|
2016-08-07 03:20:04 +00:00
|
|
|
Joystick *Core::getJoystickForSourceID(int sourceID)
|
2016-07-17 20:25:24 +00:00
|
|
|
{
|
2016-08-07 03:20:04 +00:00
|
|
|
if(unsigned(sourceID+1) < (unsigned)actionStatus.size())
|
|
|
|
return getJoystick(actionStatus[sourceID+1]->getJoystickID());
|
2016-07-17 20:25:24 +00:00
|
|
|
return NULL;
|
|
|
|
}
|