//------------------------------------------------------------------------------------- // // JGE++ is a hardware accelerated 2D game SDK for PSP/Windows. // // Licensed under the BSD license, see LICENSE in JGE root for details. // // Copyright (c) 2007 James Hui (a.k.a. Dr.Watson) // //------------------------------------------------------------------------------------- // Should we add PrecompiledHeader.h to more platforms here? PSP Doesn't support it in JGE (erwan 2011/12/11) #if defined (IOS) || defined (ANDROID) #include "PrecompiledHeader.h" #endif #include #include #include #include #include "../include/JGE.h" #include "../include/JApp.h" #include "../include/JRenderer.h" #include "../include/JSoundSystem.h" #include "../include/Vector2D.h" #include "../include/JResourceManager.h" #include "../include/JFileSystem.h" //#include "../include/JParticleSystem.h" #if defined (IOS) #import "wagicAppDelegate.h" #endif ////////////////////////////////////////////////////////////////////////// #if defined (WIN32) // WIN32 specific code #include "fmod.h" u8 JGE::GetAnalogX() { if (GetButtonState(JGE_BTN_LEFT)) return 0; if (GetButtonState(JGE_BTN_RIGHT)) return 0xff; return 0x80; } u8 JGE::GetAnalogY() { if (GetButtonState(JGE_BTN_UP)) return 0; if (GetButtonState(JGE_BTN_DOWN)) return 0xff; return 0x80; } #elif defined (LINUX) // Unix specific code #include #ifdef WITH_FMOD #include "fmod.h" #endif //WITH_FMOD u8 JGE::GetAnalogX() { if (GetButtonState(JGE_BTN_LEFT)) return 0; if (GetButtonState(JGE_BTN_RIGHT)) return 0xff; return 0x80; } u8 JGE::GetAnalogY() { if (GetButtonState(JGE_BTN_UP)) return 0; if (GetButtonState(JGE_BTN_DOWN)) return 0xff; return 0x80; } #endif static map holds; static map oldHolds; #define REPEAT_DELAY 0.5 #define REPEAT_PERIOD 0.07 static inline bool held(const JButton sym) { return holds.end() != holds.find(sym); } static inline pair, bool> triplet(LocalKeySym k, JButton b, bool h) { return make_pair(make_pair(k, b), h); } static inline pair, bool> triplet(pair p, bool h) { return make_pair(p, h); } void JGE::PressKey(const LocalKeySym sym) { const pair rng = keyBinds.equal_range(sym); if (rng.first == rng.second) keyBuffer.push(triplet(sym, JGE_BTN_NONE, false)); else for (keycodes_it it = rng.first; it != rng.second; ++it) { #if defined (WIN32) || defined (LINUX) if (JGE_BTN_FULLSCREEN == it->second) JGEToggleFullscreen(); #endif keyBuffer.push(triplet(*it, held(it->second))); } } void JGE::PressKey(const JButton sym) { #if defined (WIN32) || defined (LINUX) if (sym == JGE_BTN_FULLSCREEN) JGEToggleFullscreen(); #endif keyBuffer.push(triplet(LOCAL_KEY_NONE, sym, held(sym))); } void JGE::HoldKey(const LocalKeySym sym) { const pair rng = keyBinds.equal_range(sym); if (rng.first == rng.second) keyBuffer.push(triplet(sym, JGE_BTN_NONE, false)); else for (keycodes_it it = rng.first; it != rng.second; ++it) { #if defined (WIN32) || defined (LINUX) if (JGE_BTN_FULLSCREEN == it->second) JGEToggleFullscreen(); #endif if (!held(it->second)) { keyBuffer.push(triplet(*it, false)); holds[it->second] = REPEAT_DELAY; } else keyBuffer.push(triplet(*it, true)); } } void JGE::HoldKey_NoRepeat(const LocalKeySym sym) { const pair rng = keyBinds.equal_range(sym); if (rng.first == rng.second) keyBuffer.push(triplet(sym, JGE_BTN_NONE, false)); else for (keycodes_it it = rng.first; it != rng.second; ++it) { #if defined (WIN32) || defined (LINUX) if (JGE_BTN_FULLSCREEN == it->second) { JGEToggleFullscreen(); return; } #endif keyBuffer.push(triplet(*it, true)); if (!held(it->second)) holds[it->second] = std::numeric_limits::quiet_NaN(); } } void JGE::HoldKey(const JButton sym) { #if defined (WIN32) || defined (LINUX) if (JGE_BTN_FULLSCREEN == sym) JGEToggleFullscreen(); #endif if (!held(sym)) { keyBuffer.push(triplet(LOCAL_KEY_NONE, sym, false)); holds[sym] = REPEAT_DELAY; } else keyBuffer.push(triplet(LOCAL_KEY_NONE, sym, true)); } void JGE::HoldKey_NoRepeat(const JButton sym) { #if defined (WIN32) || defined (LINUX) if (JGE_BTN_FULLSCREEN == sym) JGEToggleFullscreen(); #endif keyBuffer.push(triplet(LOCAL_KEY_NONE, sym, true)); if (!held(sym)) holds[sym] = std::numeric_limits::quiet_NaN(); } void JGE::ReleaseKey(const LocalKeySym sym) { set s; const pair rng = keyBinds.equal_range(sym); for (keycodes_it it = rng.first; it != rng.second; ++it) { s.insert(it->second); holds.erase(it->second); } queue< pair, bool> > r; while (!keyBuffer.empty()) { pair, bool> q = keyBuffer.front(); keyBuffer.pop(); if ((!q.second) || (s.end() == s.find(q.first.second))) r.push(q); } keyBuffer = r; } void JGE::ReleaseKey(const JButton sym) { holds.erase(sym); queue< pair, bool> > r; while (!keyBuffer.empty()) { pair, bool> q = keyBuffer.front(); keyBuffer.pop(); if ((!q.second) || (q.first.second != sym)) r.push(q); } keyBuffer = r; } void JGE::Update(float dt) { for (map::iterator it = holds.begin(); it != holds.end(); ++it) { if (it->second < 0) { keyBuffer.push(triplet(LOCAL_KEY_NONE, it->first, true)); it->second = REPEAT_PERIOD; } it->second -= dt; } if (mApp != NULL) mApp->Update(); oldHolds = holds; } bool JGE::GetButtonState(JButton button) { return (holds.end() != holds.find(button)); } bool JGE::GetButtonClick(JButton button) { return ((holds.end() != holds.find(button)) && (oldHolds.end() == oldHolds.find(button))); } JButton JGE::ReadButton() { if (keyBuffer.empty()) return JGE_BTN_NONE; JButton val = keyBuffer.front().first.second; keyBuffer.pop(); return val; } LocalKeySym JGE::ReadLocalKey() { if (keyBuffer.empty()) return LOCAL_KEY_NONE; LocalKeySym val = keyBuffer.front().first.first; keyBuffer.pop(); return val; } u32 JGE::BindKey(LocalKeySym sym, JButton button) { keyBinds.insert(make_pair(sym, button)); return keyBinds.size(); } u32 JGE::UnbindKey(LocalKeySym sym, JButton button) { for (keycodes_it it = keyBinds.begin(); it != keyBinds.end(); ) if (sym == it->first && button == it->second) { keycodes_it er = it; ++it; keyBinds.erase(er); } else ++it; return keyBinds.size(); } u32 JGE::UnbindKey(LocalKeySym sym) { pair rng = keyBinds.equal_range(sym); keyBinds.erase(rng.first, rng.second); return keyBinds.size(); } void JGE::ClearBindings() { keyBinds.clear(); } void JGE::ResetBindings() { keyBinds.clear(); JGECreateDefaultBindings(); } JGE::keybindings_it JGE::KeyBindings_begin() { return keyBinds.begin(); } JGE::keybindings_it JGE::KeyBindings_end() { return keyBinds.end(); } void JGE::ResetInput() { while (!keyBuffer.empty()) keyBuffer.pop(); holds.clear(); LeftClickedProcessed(); } void JGE::LeftClicked(int x, int y) { mLastLeftClickX = x; mlastLeftClickY = y; } void JGE::LeftClickedProcessed() { mLastLeftClickX = -1; mlastLeftClickY = -1; } bool JGE::GetLeftClickCoordinates(int& x, int& y) { if(mLastLeftClickX != -1 || mlastLeftClickY != -1) { x = mLastLeftClickX; y = mlastLeftClickY; return true; } return false; } JGE::JGE() { mApp = NULL; #if defined (WIN32) || defined (LINUX) strcpy(mDebuggingMsg, ""); mCurrentMusic = NULL; #endif #if defined (ANDROID) mJNIEnv = NULL; #endif Init(); } JGE::~JGE() { JRenderer::Destroy(); JFileSystem::Destroy(); JSoundSystem::Destroy(); } #if defined (PSP) // PSP Specific code void JGE::Init() { #ifdef DEBUG_PRINT mDebug = true; #else mDebug = false; #endif if (mDebug) pspDebugScreenInit(); // do this so that we can use pspDebugScreenPrintf strcpy(mDebuggingMsg, ""); sceCtrlSetSamplingCycle(0); sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); //JRenderer::GetInstance(); Lazy loading //JFileSystem::GetInstance(); Lazy loading //JSoundSystem::GetInstance(); let's do lazy loading mDone = false; mPaused = false; mCriticalAssert = false; } u8 JGE::GetAnalogX() { return JGEGetAnalogX(); } u8 JGE::GetAnalogY() { return JGEGetAnalogY(); } static SceCtrlData gCtrlPad; void JGE::Run() { static const int keyCodeList[] = { PSP_CTRL_SELECT, // Select button. PSP_CTRL_START, // Start button. PSP_CTRL_UP, // Up D-Pad button. PSP_CTRL_RIGHT, // Right D-Pad button. PSP_CTRL_DOWN, // Down D-Pad button. PSP_CTRL_LEFT, // Left D-Pad button. PSP_CTRL_LTRIGGER, // Left trigger. PSP_CTRL_RTRIGGER, // Right trigger. PSP_CTRL_TRIANGLE, // Triangle button. PSP_CTRL_CIRCLE, // Circle button. PSP_CTRL_CROSS, // Cross button. PSP_CTRL_SQUARE, // Square button. PSP_CTRL_HOLD, // Hold button. }; u64 curr; long long int nextInput = 0; u64 lastTime; u32 oldButtons; u32 veryOldButtons; u32 gTickFrequency = sceRtcGetTickResolution(); sceRtcGetCurrentTick(&lastTime); oldButtons = veryOldButtons = 0; while (!mDone) { if (!mPaused) { sceRtcGetCurrentTick(&curr); float dt = (curr - lastTime) / (float)gTickFrequency; mDeltaTime = dt; sceCtrlPeekBufferPositive(&gCtrlPad, 1); for (signed int i = sizeof(keyCodeList)/sizeof(keyCodeList[0]) - 1; i >= 0; --i) { if (keyCodeList[i] & gCtrlPad.Buttons) { if (!(keyCodeList[i] & oldButtons)) HoldKey(keyCodeList[i]); } else if (keyCodeList[i] & oldButtons) ReleaseKey(keyCodeList[i]); } oldButtons = gCtrlPad.Buttons; Update(dt); Render(); if (mDebug) { if (strlen(mDebuggingMsg)>0) { pspDebugScreenSetXY(0, 0); pspDebugScreenPrintf(mDebuggingMsg); } } veryOldButtons = gCtrlPad.Buttons; } else sceKernelDelayThread(1); lastTime = curr; } } ////////////////////////////////////////////////////////////////////////// #else ///// Non PSP code void JGE::Init() { mDone = false; mPaused = false; mCriticalAssert = false; //JRenderer::GetInstance(); Lazy loading //JFileSystem::GetInstance(); Lazy loading JSoundSystem::GetInstance(); LeftClickedProcessed(); } #endif ///// Non PSP code ////////////////////////////////////////////////////////////////////////// JGE* JGE::mInstance = NULL; // returns number of milliseconds since game started int JGE::GetTime() { return JGEGetTime(); } void JGE::SetDelta(float delta) { mDeltaTime = delta; } float JGE::GetDelta() { return mDeltaTime; } float JGE::GetFPS() { return mDeltaTime > 0 ? 1.0f / mDeltaTime : 0; } JGE* JGE::GetInstance() { if (mInstance == NULL) mInstance = new JGE(); return mInstance; } void JGE::Destroy() { if (mInstance) { delete mInstance; mInstance = NULL; } } void JGE::SetARGV(int argc, char * argv[]) { for (int i = 0; i < argc; ++i){ string s = argv[i]; mArgv.push_back(s); } } std::vector JGE::GetARGV() { return mArgv; } void JGE::SetApp(JApp *app) { mApp = app; } void JGE::Render() { JRenderer* renderer = JRenderer::GetInstance(); renderer->BeginScene(); if (mApp != NULL) mApp->Render(); renderer->EndScene(); } void JGE::End() { mDone = true; } void JGE::printf(const char *format, ...) { va_list list; va_start(list, format); vsprintf(mDebuggingMsg, format, list); va_end(list); } void JGE::Pause() { if (mPaused) return; mPaused = true; if (mApp != NULL) mApp->Pause(); JFileSystem::GetInstance()->Pause(); } void JGE::Resume() { if (mPaused) { mPaused = false; if (mApp != NULL) mApp->Resume(); } } void JGE::Assert(const char *filename, long lineNumber) { mAssertFile = filename; mAssertLine = lineNumber; mCriticalAssert = true; } void JGE::Scroll(int inXVelocity, int inYVelocity) { if (mApp != NULL) { mApp->OnScroll(inXVelocity, inYVelocity); } } void JGE::SendCommand(string command) { #if defined (ANDROID) sendJNICommand(command); #elif defined (IOS) SendCommand(command, ""); #else cerr << command; #endif } void JGE::SendCommand(std::string command, std::string parameter) { #if defined (IOS) // get the app delegate and have it handle the command wagicAppDelegate *delegate = [ [UIApplication sharedApplication] delegate]; [delegate handleWEngineCommand:[NSString stringWithCString: command.c_str() encoding: NSUTF8StringEncoding] withParameter: [NSString stringWithCString: parameter.c_str() encoding:NSUTF8StringEncoding]]; #else cerr << command << " " << parameter; #endif } // this controls commands meant to modify/interact with UI void JGE::SendCommand(std::string command, float& x, float& y, float& width, float& height) { #ifdef ANDROID #elif IOS wagicAppDelegate *delegate = [[UIApplication sharedApplication] delegate]; [delegate handleWEngineCommand: [NSString stringWithCString: command.c_str() encoding: NSUTF8StringEncoding] withUIParameters: x yCoordinate: y width: width height: height]; #else cerr << command << " " << x << " " << y << " " << width << " " << height; #endif } #if defined (ANDROID) JNIEnv * JGE::getJNIEnv() { JNIEnv *env; int status = mJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4); if(status == JNI_ERR) { DebugTrace("Failed to get JNI environment, assuming native thread"); status = mJavaVM->AttachCurrentThread(&env, NULL); if(status == JNI_ERR) { DebugTrace("callback_handler: failed to attach current thread"); return NULL; } } else if (status == JNI_EDETACHED) { DebugTrace("env is detached. trying to attach it to the current thread"); jint attachSuccess = mJavaVM->AttachCurrentThread(&env,NULL); if(attachSuccess == 0) { return env; } else { return NULL; }//end if/els } else if (status == JNI_OK) { return env; } return NULL; } string JGE::getFileSystemLocation() { JNIEnv * env = getJNIEnv(); if (env == NULL) { DebugTrace("An Error Occurred in getting the JNI Environment whie trying to get the system folder location. Defaulting to /mnt/sdcard/net.wagic.app/Wagic"); return "/mnt/sdcard/Wagic"; }; jclass jniClass = env->FindClass("org/libsdl/app/SDLActivity"); jmethodID methodId = env->GetStaticMethodID( jniClass, "getSystemFolderPath", "()Ljava/lang/String;"); if (methodId == 0) { DebugTrace("An Error Occurred in getting the JNI methodID for getSystemFolderPath. Defaulting to /mnt/sdcard/Wagic"); return "/mnt/sdcard/Wagic"; }; jstring systemPath = (jstring) env->CallStaticObjectMethod(jniClass, methodId); // Now convert the Java String to C++ char array const char* cstr = env->GetStringUTFChars(systemPath, 0); string retVal (cstr); env->ReleaseStringUTFChars(systemPath, cstr); env->DeleteLocalRef(systemPath); return retVal; } /// Access to JNI Environment void JGE::SetJNIEnv(JNIEnv * env, jclass cls) { mJNIEnv = env; mJNIClass = cls; midSendCommand = mJNIEnv->GetStaticMethodID(mJNIClass,"jgeSendCommand","(Ljava/lang/String;)V"); } void JGE::setJVM( JavaVM *vm) { mJavaVM = vm; } void JGE::sendJNICommand(string command) { if (midSendCommand) { mJNIEnv->CallStaticVoidMethod(mJNIClass, midSendCommand, mJNIEnv->NewStringUTF(command.c_str())); } } #endif std::queue< pair< pair, bool> > JGE::keyBuffer; std::multimap JGE::keyBinds;