/** * Update the state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_update_flash(gameCtx *pGame) { gfmRV rv; int elapsed; introCtx *pIntro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Get the current state pIntro = (introCtx*)(pGame->pState); rv = gfmTilemap_update(pIntro->pFlashFx, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Update this animation's time so we can move to the next one rv = gfm_getElapsedTime(&elapsed, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); pIntro->flashAnimTime += elapsed; // Change the state after 2 seconds if (pIntro->flashAnimTime > 2000) { // TODO Change to the last part of the animation pIntro->state = intro_game; } rv = GFMRV_OK; __ret: return rv; }
/** * Draw the state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_draw_flash(gameCtx *pGame) { gfmRV rv; introCtx *pIntro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Get the current state pIntro = (introCtx*)(pGame->pState); // TODO Draw everything rv = gfmTilemap_draw(pGame->common.pTMap, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = player_draw(pIntro->pPl, pGame); ASSERT_NR(rv == GFMRV_OK); rv = doc_draw(pIntro->pDoc, pGame); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_draw(pIntro->pFlashFx, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = GFMRV_OK; __ret: return rv; }
/** * Draw the state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_draw_game(gameCtx *pGame) { gfmRV rv; introCtx *pIntro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Get the current state pIntro = (introCtx*)(pGame->pState); rv = gfmTilemap_draw(pGame->common.pTMap, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = player_draw(pIntro->pPl, pGame); ASSERT_NR(rv == GFMRV_OK); rv = doc_draw(pIntro->pDoc, pGame); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_draw(pIntro->pBullet1, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_draw(pIntro->pBullet2, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // TODO Draw the bullets and the enemies... rv = GFMRV_OK; __ret: return rv; }
/** * Subdivides a quadtree * * @param pCtx The node to be subdivided * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_ALLOC_FAILED */ static gfmRV gfmQuadtree_subdivide(gfmQuadtreeRoot *pCtx, gfmQuadtree *pNode) { gfmQuadtree *pChild; gfmQuadtreePosition i; gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); ASSERT(pNode, GFMRV_ARGUMENTS_BAD); // Alloc and initialize all the children i = gfmQT_nw; while (i < gfmQT_max) { gfmGenArr_getNextRef(gfmQuadtree, pCtx->pQTPool, 5, pChild, gfmQuadtreeNode_getNew); gfmGenArr_push(pCtx->pQTPool); // Initialize the child rv = gfmQuadtree_init(pChild, pNode, i); ASSERT_NR(rv == GFMRV_OK); // Set the node's child pNode->ppChildren[i] = pChild; // Go to the next one i++; } // Insert every child to the tree it's contained while (pNode->pNodes) { gfmQuadtreeLL *pTmp; // Get the current node pTmp = pNode->pNodes; // Add it to every child (that it overlaps) i = gfmQT_nw; while (i < gfmQT_max) { // Get the current child pChild = pNode->ppChildren[i]; // Check if the object collides this node rv = gfmQuadtree_overlap(pChild, pTmp->pSelf); if (rv == GFMRV_TRUE) { // Add it to the child rv = gfmQuadtree_insertObject(pCtx, pChild, pTmp->pSelf); ASSERT_NR(rv == GFMRV_OK); } i++; } // Go to the next node pNode->pNodes = pTmp->pNext; // Prepend the node to the free list pTmp->pNext = pCtx->pAvailable; pCtx->pAvailable = pTmp; } rv = GFMRV_OK; __ret: return rv; }
/** * Adds a new gfmObject to the quadtree, subdividing it as necessary and * colliding with every possible node * * @param pCtx The quadtree's root * @param pObj The gfmObject * @return GFMRV_ARGUMENTS_BAD, GFMRV_QUADTREE_NOT_INITIALIZED, * GFMRV_QUADTREE_OVERLAPED, GFMRV_QUADTREE_DONE */ gfmRV gfmQuadtree_collideObject(gfmQuadtreeRoot *pCtx, gfmObject *pObj) { gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); ASSERT(pObj, GFMRV_ARGUMENTS_BAD); // Check if initialized ASSERT(pCtx->maxDepth > 0, GFMRV_QUADTREE_NOT_INITIALIZED); // Check if this node overlaps the root rv = gfmQuadtree_overlap(pCtx->pSelf, pObj); if (rv != GFMRV_TRUE) { rv = GFMRV_QUADTREE_DONE; goto __ret; } //ASSERT(rv == GFMRV_TRUE, GFMRV_QUADTREE_DONE); // Store the object to be added pCtx->pObject = pObj; // Clear the call stack pCtx->stack.pushPos = 0; // Clear any previous overlap pCtx->pOther = 0; // Push the root node to start colliding rv = gfmQuadtree_pushNode(pCtx, pCtx->pSelf); ASSERT_NR(rv == GFMRV_OK); // Collide it rv = gfmQuadtree_continue(pCtx); __ret: return rv; }
/** * Change the current input scheme, ignoring if it clashes with the other * player (but checking for controller presence) * * @param ID ID of the player being set * @param mode The new controle mode * @param Whether the mode was switched successfully */ int ctr_setModeForce(int ID, ctr_mode mode) { int ret; ret = 0; // Check that the mode is valid ASSERT_NR(ID == ID_PL1 || ID == ID_PL2); ASSERT_NR(mode < CTR_PAD1_A || GFraMe_controller_max > 0); ASSERT_NR(mode < CTR_PAD2_A || GFraMe_controller_max > 1); if (ID == ID_PL1) _ctr_pl1 = mode; else _ctr_pl2 = mode; ret = 1; __ret: return ret; }
/** * @return GFMRV_TRUE (if did shoot), GFMRV_FALSE (otherwise), ... */ gfmRV shoot_bullet(gameCtx *pGame, int x, int y) { gfmRV rv; gfmSprite *pSpr; int bX, bY, height, width; introCtx *pIntro; // Set default values pSpr = 0; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Check that the current state is the intro state ASSERT(pGame->state == state_intro, GFMRV_FUNCTION_FAILED); // Get the current state pIntro = (introCtx*)(pGame->pState); // Check that the bullet isn't on screen rv = gfmSprite_getPosition(&bX, &bY, pIntro->pBullet1); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getCameraDimensions(&width, &height, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); if (bX < 0 || bX > width) { pSpr = pIntro->pBullet1; } else { rv = gfmSprite_getPosition(&bX, &bY, pIntro->pBullet2); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getCameraDimensions(&width, &height, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); if (bX < 0 || bX > width) { pSpr = pIntro->pBullet2; } } ASSERT(pSpr, GFMRV_FALSE); // Set the bullet's position rv = gfmSprite_setPosition(pSpr, x, y); ASSERT_NR(rv == GFMRV_OK); // Set it's velocity rv = gfmSprite_setHorizontalVelocity(pSpr, 200); ASSERT_NR(rv == GFMRV_OK); // Restart the animation rv = gfmSprite_playAnimation(pSpr, 1); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_playAnimation(pSpr, 0); ASSERT_NR(rv == GFMRV_OK); rv = GFMRV_TRUE; __ret: return rv; }
/** * Populates a quadtree with tilemap's areas * * @param pCtx The quadtree's root * @param pTMap The tilemap * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_QUADTREE_NOT_INITIALIZED */ gfmRV gfmQuadtree_populateTilemap(gfmQuadtreeRoot *pCtx, gfmTilemap *pTMap) { gfmObject *pList; gfmRV rv; int len; /* Sanitize arguments */ ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); ASSERT(pTMap, GFMRV_ARGUMENTS_BAD); /* Get how many areas there are */ rv = gfmTilemap_getAreasLength(&len, pTMap); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_getArea(&pList, pTMap, 0); ASSERT_NR(rv == GFMRV_OK); rv = gfmHitbox_populateQuadtree((gfmHitbox*)pList, pCtx, len); __ret: return rv; }
/** * Checks if a quadtree node overlaps an object * * @param pCtx The quadtree node * @param pObj The gfmObject * @return GFMRV_TRUE, GFMRV_FALSE, GFMRV_ARGUMENTS_BAD */ static gfmRV gfmQuadtree_overlap(gfmQuadtree *pCtx, gfmObject *pObj) { gfmRV rv; int cX, cY, dist, hWidth, hHeight, maxDist; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); ASSERT(pObj, GFMRV_ARGUMENTS_BAD); // Get the object's dimensions rv = gfmObject_getCenter(&cX, &cY, pObj); ASSERT_NR(rv == GFMRV_OK); rv = gfmObject_getDimensions(&hWidth, &hHeight, pObj); ASSERT_NR(rv == GFMRV_OK); // Get half the dimensions (rounded up) hWidth = hWidth / 2 + (hWidth % 2); hHeight = hHeight / 2 + (hHeight % 2); // Check that they are overlaping (horizontally) dist = cX - pCtx->centerX; if (dist < 0) { dist = -dist; } maxDist = hWidth + pCtx->halfWidth; if (dist > maxDist) { return GFMRV_FALSE; } //ASSERT(dist <= maxDist, GFMRV_FALSE); // Check vertically... dist = cY - pCtx->centerY; if (dist < 0) { dist = -dist; } maxDist = hHeight + pCtx->halfHeight; if (dist > maxDist) { return GFMRV_FALSE; } //ASSERT(dist <= maxDist, GFMRV_FALSE); rv = GFMRV_TRUE; __ret: return rv; }
/** * Draw the state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_draw_begin(gameCtx *pGame) { gfmRV rv; introCtx *pIntro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Get the current state pIntro = (introCtx*)(pGame->pState); // TODO Draw everything rv = gfmTilemap_draw(pGame->common.pTMap, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = player_draw(pIntro->pPl, pGame); ASSERT_NR(rv == GFMRV_OK); rv = doc_draw(pIntro->pDoc, pGame); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_draw(pIntro->pMachineFx, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfmText_draw(pGame->common.pText, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); #if 0 rv = gfmQuadtree_drawBounds(pGame->common.pQt, pGame->pCtx, 0/*colors*/); ASSERT_NR(rv == GFMRV_OK); #endif rv = GFMRV_OK; __ret: return rv; }
/** * Draw the FPS counter on the screen; It's position should be defined before * hand * * @param pCounter The FPS counter * @param pCtx The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, * GFMRV_FPSCOUNTER_NOT_INITIALIZED */ gfmRV gfmFPSCounter_draw(gfmFPSCounter *pCounter, gfmCtx *pCtx) { gfmRV rv; int dps, ups; unsigned int curTime; /* Sanitize arguments */ ASSERT(pCounter, GFMRV_ARGUMENTS_BAD); ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); /* Calculate how long it took to draw */ rv = gfmTimer_getCurTimeMs(&curTime); ASSERT_NR(rv == GFMRV_OK); /* Check if the fps should be updated */ if (curTime - pCounter->lastTime >= 1000) { pCounter->updateCount = pCounter->updateAcc; pCounter->updateAcc = 0; pCounter->drawCount = pCounter->drawAcc; pCounter->drawAcc = 0; pCounter->lastTime = curTime; } /* Get the expected FPS */ rv = gfm_getStateFrameRate(&ups, &dps, pCtx); ASSERT_NR(rv == GFMRV_OK); /* Draw the information */ gfmDebug_printf(pCtx, pCounter->x, pCounter->y, " UPD %02i/%02i - %04iMS\n" "DRAW %02i/%02i - %04iMS\n", pCounter->updateCount, ups, pCounter->updateTime, pCounter->drawCount, dps, curTime - pCounter->drawInit); pCounter->updateTime = 0; rv = GFMRV_OK; __ret: return rv; }
/** * The game's main menu * * @param pGame The game "global" context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_ALLOC_FAILED, ... */ gfmRV state_mainMenu(gameCtx *pGame) { gfmRV rv; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); // Initialize the main menu rv = state_mainMenu_init(pGame); ASSERT_NR(rv == GFMRV_OK); // Run until the window is closed while (!pGame->switchState && pGame->isRunning && gfm_didGetQuitFlag(pGame->pCtx) == GFMRV_FALSE) { rv = gfm_handleEvents(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Update stuff while (gfm_isUpdating(pGame->pCtx) == GFMRV_TRUE) { rv = gfm_fpsCounterUpdateBegin(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Update everything rv = state_mainMenu_update(pGame); ASSERT_NR(rv == GFMRV_OK); rv = gfm_fpsCounterUpdateEnd(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); } // Draw stuff while (gfm_isDrawing(pGame->pCtx) == GFMRV_TRUE) { rv = gfm_drawBegin(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw everything rv = state_mainMenu_draw(pGame); ASSERT_NR(rv == GFMRV_OK); rv = gfm_drawEnd(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); } } rv = GFMRV_OK; __ret: state_mainMenu_clean(pGame); return rv; }
/** * Update the state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_update_gameover(gameCtx *pGame) { gfmRV rv; gfmInputState reset; int nreset; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); rv = gfmText_update(pGame->common.pText, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getKeyState(&reset, &nreset, pGame->pCtx, pGame->resetHnd); ASSERT_NR(rv == GFMRV_OK); if ((reset & gfmInput_justPressed) == gfmInput_justPressed) { pGame->state = state_reset; return GFMRV_OK; } rv = GFMRV_OK; __ret: return rv; }
/** * Signal when an update started, to calculate how long it took * * @param pCtx The FPS counter * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD */ gfmRV gfmFPSCounter_updateBegin(gfmFPSCounter *pCtx) { gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); // Store the moment an update was initialized rv = gfmTimer_getCurTimeMs(&(pCtx->updateInit)); ASSERT_NR(rv == GFMRV_OK); rv = GFMRV_OK; __ret: return rv; }
/** * Add a sprite to the quadtree without collinding it against the tree's objs * * @param pCtx The quadtree's root * @param pSpr The gfmSprite * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_QUADTREE_NOT_INITIALIZED */ gfmRV gfmQuadtree_populateSprite(gfmQuadtreeRoot *pCtx, gfmSprite *pSpr) { gfmObject *pObj; gfmRV rv; // Sanitize sprite (other checks are done in sub-functions) ASSERT(pSpr, GFMRV_ARGUMENTS_BAD); // Retrieve the sprite's object rv = gfmSprite_getObject(&pObj, pSpr); ASSERT_NR(rv == GFMRV_OK); // Add it to the quadtree rv = gfmQuadtree_populateObject(pCtx, pObj); __ret: return rv; }
/** * Called on gfm_drawBegin to calculate how long it takes to render a frame * * @param pCtx The FPS counter * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV gfmFPSCounter_initDraw(gfmFPSCounter *pCtx) { gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); // Store the moment a draw was initialized rv = gfmTimer_getCurTimeMs(&(pCtx->drawInit)); ASSERT_NR(rv == GFMRV_OK); // Increase the draw count pCtx->drawAcc++; rv = GFMRV_OK; __ret: return rv; }
/** * Draw everything in this state * * @param pGame The game "global" context */ static gfmRV state_mainMenu_draw(gameCtx *pGame) { gfmRV rv; mainMenuCtx *pMainMenu; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pMainMenu, GFMRV_ARGUMENTS_BAD); // Retrieve the main menu context pMainMenu = pGame->pMainMenu; // Draw the background rv = gfmTilemap_draw(pMainMenu->pBackground, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the floor rv = gfmTilemap_draw(pMainMenu->pFloor, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the title rv = gfmTilemap_draw(pMainMenu->pTitle, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the text if (pMainMenu->textTimer >= 0) { rv = gfmText_draw(pMainMenu->pText, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); } // If the animation finished playing if (pMainMenu->titleAnimation >= sizeof(titleData) / sizeof(int)) { // Draw my icon rv = gfm_drawTile(pGame->pCtx, pGame->pSset32x32, 272/*x*/, /*y*/200, 15/*tile*/, 0/*isFlipped*/); ASSERT_NR(rv == GFMRV_OK); // Draw the options button rv = gfm_drawTile(pGame->pCtx, pGame->pSset32x32, 8/*x*/, /*y*/200, 14/*tile*/, 0/*isFlipped*/); ASSERT_NR(rv == GFMRV_OK); // Draw the options icon rv = gfm_drawTile(pGame->pCtx, pGame->pSset16x16, 16/*x*/, /*y*/205, 41/*tile*/, 0/*isFlipped*/); ASSERT_NR(rv == GFMRV_OK); } rv = GFMRV_OK; __ret: return rv; }
int main(int arg, char *argv[]) { double volume; gfmCtx *pCtx; gfmRV rv; int handle; // Initialize every variable pCtx = 0; // Try to get a new context rv = gfm_getNew(&pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_initStatic(pCtx, "com.gfmgamecorner", "gfmAudioTest"); ASSERT_NR(rv == GFMRV_OK); // Initialize the audio sub-system //rv = gfm_initAudio(pCtx, gfmAudio_lowQuality); //rv = gfm_initAudio(pCtx, gfmAudio_medQuality); rv = gfm_initAudio(pCtx, gfmAudio_defQuality); //rv = gfm_initAudio(pCtx, gfmAudio_highQuality); ASSERT_NR(rv == GFMRV_OK); // Load an audio rv = gfm_loadAudio(&handle, pCtx, "bossBattle.wav", sizeof("bossBattle.wav") - 1); ASSERT_NR(rv == GFMRV_OK); // Make the audio loop rv = gfm_setRepeat(pCtx, handle, 0 /* pos */); ASSERT_NR(rv == GFMRV_OK); // Play the audio volume = 0.8; rv = gfm_playAudio(0/* ignore the instance */, pCtx, handle, volume); ASSERT_NR(rv == GFMRV_OK); // Wait for some time while the song should be playing... #if defined(WIN32) Sleep(10); #else sleep(10); #endif rv = GFMRV_OK; __ret: gfm_free(&pCtx); return rv; }
/** * Signal that the update ended, and actually calculate how long it took * * @param pCtx The FPS counter * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD */ gfmRV gfmFPSCounter_updateEnd(gfmFPSCounter *pCtx) { gfmRV rv; unsigned int curTime; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); // Store the moment an update was initialized rv = gfmTimer_getCurTimeMs(&curTime); ASSERT_NR(rv == GFMRV_OK); // Update the update operation time pCtx->updateTime += curTime - pCtx->updateInit; // Update the number of updates pCtx->updateAcc++; rv = GFMRV_OK; __ret: return rv; }
/** * Add an object to the quadtree without collinding it against the tree's objs * * @param pCtx The quadtree's root * @param pObj The gfmObject * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_QUADTREE_NOT_INITIALIZED */ gfmRV gfmQuadtree_populateObject(gfmQuadtreeRoot *pCtx, gfmObject *pObj) { gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); ASSERT(pObj, GFMRV_ARGUMENTS_BAD); // Check if initialized ASSERT(pCtx->maxDepth > 0, GFMRV_QUADTREE_NOT_INITIALIZED); // Check that the object overlaps the root node rv = gfmQuadtree_overlap(pCtx->pSelf, pObj); ASSERT(rv == GFMRV_TRUE, GFMRV_OK); // Clear the call stack pCtx->stack.pushPos = 0; // Clear any previous overlap pCtx->pOther = 0; // Push the root node to start overlaping rv = gfmQuadtree_pushNode(pCtx, pCtx->pSelf); ASSERT_NR(rv == GFMRV_OK); // Continue adding the object while (pCtx->stack.pushPos > 0) { gfmQuadtree *pNode; // Pop the current node rv = gfmQuadtree_popNode(&pNode, pCtx); ASSERT_NR(rv == GFMRV_OK); // If it has children, push its children if (pNode->ppChildren[gfmQT_nw]) { gfmQuadtreePosition i; gfmQuadtree *pChild; i = gfmQT_nw; while (i < gfmQT_max) { // Get the current child pChild = pNode->ppChildren[i]; // Check if the object overlaps this node rv = gfmQuadtree_overlap(pChild, pObj); if (rv == GFMRV_TRUE) { // Push it (so it will collide later) rv = gfmQuadtree_pushNode(pCtx, pChild); ASSERT_NR(rv == GFMRV_OK); } i++; } } else { // Check if adding the node will subdivide the tree and if it // can still be subdivided if (pNode->numObjects + 1 > pCtx->maxNodes && pNode->depth + 1 < pCtx->maxDepth) { // Subdivide the tree rv = gfmQuadtree_subdivide(pCtx, pNode); ASSERT_NR(rv == GFMRV_OK); // Push the node again so its children are overlapped/pushed rv = gfmQuadtree_pushNode(pCtx, pNode); ASSERT_NR(rv == GFMRV_OK); } else { // Add the object to this node rv = gfmQuadtree_insertObject(pCtx, pNode, pObj); ASSERT_NR(rv == GFMRV_OK); } } } rv = GFMRV_OK; __ret: return rv; }
/** * The game's first state * * @param gameCtx The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro(gameCtx *pGame, int jumpIntro) { gfmRV rv; introCtx _intro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); // Clean the state's context (the stack may have "trash") memset(&_intro, 0x0, sizeof(introCtx)); // Set the current state date pGame->pState = &_intro; // Initialize this state rv = intro_init(pGame); ASSERT_NR(rv == GFMRV_OK); if (jumpIntro) { _intro.state = intro_game; } // Run until the window is closed (or the state changes) while (pGame->state == state_intro && pGame->isRunning && gfm_didGetQuitFlag(pGame->pCtx) == GFMRV_FALSE) { rv = gfm_handleEvents(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Update stuff while (gfm_isUpdating(pGame->pCtx) == GFMRV_TRUE) { rv = gfm_fpsCounterUpdateBegin(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Update everything switch (_intro.state) { case intro_begin: { rv = intro_update_begin(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_flash: { rv = intro_update_flash(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_game: { rv = intro_update_game(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_gameover: { rv = intro_update_gameover(pGame); ASSERT_NR(rv == GFMRV_OK); } break; default: break; } rv = gfm_fpsCounterUpdateEnd(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); } // Draw stuff while (gfm_isDrawing(pGame->pCtx) == GFMRV_TRUE) { rv = gfm_drawBegin(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw everything switch (_intro.state) { case intro_begin: { rv = intro_draw_begin(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_flash: { rv = intro_draw_flash(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_game: { rv = intro_draw_game(pGame); ASSERT_NR(rv == GFMRV_OK); } break; case intro_gameover: { rv = intro_draw_gameover(pGame); ASSERT_NR(rv == GFMRV_OK); } break; default: break; } rv = gfm_drawEnd(pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); } } rv = GFMRV_OK; __ret: intro_clean(pGame); return rv; }
/** * Initializes (and loads) everything used by this state * * @param pGame The game's context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, ... */ gfmRV intro_init(gameCtx *pGame) { gfmRV rv; int height, width; introCtx *pIntro; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pState, GFMRV_ARGUMENTS_BAD); // Get the current state pIntro = (introCtx*)(pGame->pState); // Initialize the background rv = gfmTilemap_init(pGame->common.pTMap, pGame->pSset8x8, 1/*width*/, 1/*height*/, -1/*defTile*/); ASSERT_NR(rv == GFMRV_OK); // Load the background from a file rv = gfmTilemap_loadf(pGame->common.pTMap, pGame->pCtx, "lab.gfm", 7/*filenameLen*/, pDictNames, pDictTypes, dictLen); ASSERT_NR(rv == GFMRV_OK); // Alloc the fx tilemap rv = gfmTilemap_getNew(&(pIntro->pMachineFx)); ASSERT_NR(rv == GFMRV_OK); // Initialize and load the fx rv = gfmTilemap_init(pIntro->pMachineFx, pGame->pSset8x8, 5/*width*/, 9/*height*/, -1/*defTile*/); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_loadf(pIntro->pMachineFx, pGame->pCtx, "machine-fx.gfm", 14/*filenameLen*/, pDictNames, pDictTypes, dictLen); ASSERT_NR(rv == GFMRV_OK); // Set its position rv = gfmTilemap_setPosition(pIntro->pMachineFx, 24/*x*/, 120/*y*/); ASSERT_NR(rv == GFMRV_OK); // Add the tilemap animations rv = gfmTilemap_addAnimations(pIntro->pMachineFx, pMachineFxAnim, sizeof(pMachineFxAnim) / sizeof(int) - 1); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_recacheAnimations(pIntro->pMachineFx); ASSERT_NR(rv == GFMRV_OK); // Alloc the fx tilemap rv = gfmTilemap_getNew(&(pIntro->pFlashFx)); ASSERT_NR(rv == GFMRV_OK); // Initialize and load the flash fx rv = gfmTilemap_init(pIntro->pFlashFx, pGame->pSset8x8, 40/*width*/, 30/*height*/, -1/*defTile*/); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_loadf(pIntro->pFlashFx, pGame->pCtx, "flash-fx.gfm", 14/*filenameLen*/, pDictNames, pDictTypes, dictLen); ASSERT_NR(rv == GFMRV_OK); // Add the tilemap animations rv = gfmTilemap_addAnimations(pIntro->pFlashFx, pFlashFxAnim, sizeof(pFlashFxAnim) / sizeof(int) - 1); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_recacheAnimations(pIntro->pFlashFx); ASSERT_NR(rv == GFMRV_OK); // Get the camera's dimensions (to limit the text's dimensions) rv = gfm_getCameraDimensions(&width, &height, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Prepare the text rv = gfmText_init(pGame->common.pText, 0/*x*/, 16/*y*/, width / 8, 3/*maxLines*/, 50/*delay*/, 0/*dontBindToWorld*/, pGame->pSset8x8, 0/*firstTile*/); ASSERT_NR(rv == GFMRV_OK); // Initialize the player rv = player_init(&(pIntro->pPl), pGame, 36/*x*/, 150/*y*/); ASSERT_NR(rv == GFMRV_OK); // Initialize the doc rv = doc_init(&(pIntro->pDoc), pGame, 145/*x*/, 186/*y*/); ASSERT_NR(rv == GFMRV_OK); // Alloc and initialize the bullet rv = gfmSprite_getNew(&(pIntro->pBullet1)); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_init(pIntro->pBullet1, 1000, -100, 14/*width*/, 8/*height*/, pGame->pSset16x16, -1/*offX*/, -4/*offY*/, 0/*child*/, BULLET); ASSERT_NR(rv == GFMRV_OK); // Stupid trick, add the animation twice so we can reset it rv = gfmSprite_addAnimations(pIntro->pBullet1, pBulletAnimData, bulletAnimDataLen); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_getNew(&(pIntro->pBullet2)); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_init(pIntro->pBullet2, 1000, -100, 14/*width*/, 8/*height*/, pGame->pSset16x16, -1/*offX*/, -4/*offY*/, 0/*child*/, BULLET); ASSERT_NR(rv == GFMRV_OK); // Stupid trick, add the animation twice so we can reset it rv = gfmSprite_addAnimations(pIntro->pBullet2, pBulletAnimData, bulletAnimDataLen); ASSERT_NR(rv == GFMRV_OK); pIntro->state = intro_begin; rv = GFMRV_OK; __ret: return rv; }
int main(int arg, char *argv[]) { gfmCtx *pCtx; gfmObject *pObj; gfmQuadtreeRoot *pQTRoot; gfmRV rv; gfmSprite *pSpr; gfmSpriteset *pSset8, *pSset16; int iTex; // Initialize every variable pCtx = 0; pObj = 0; pQTRoot = 0; pSpr = 0; // Try to get a new context rv = gfm_getNew(&pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_initStatic(pCtx, "com.gfmgamecorner", "gframe_quadtree_basic"); ASSERT_NR(rv == GFMRV_OK); // Initialize the window rv = gfm_initGameWindow(pCtx, WNDW, WNDH, 640, 480, 0, 0); ASSERT_NR(rv == GFMRV_OK); // Load the texture rv = gfm_loadTextureStatic(&iTex, pCtx, "big_atlas.bmp", 0xff00ff); ASSERT_NR(rv == GFMRV_OK); // Set it as the default rv = gfm_setDefaultTexture(pCtx, iTex); ASSERT_NR(rv == GFMRV_OK); // Create the spritesets rv = gfm_createSpritesetCached(&pSset8, pCtx, iTex, 8/*tw*/, 8/*th*/); ASSERT_NR(rv == GFMRV_OK); rv = gfm_createSpritesetCached(&pSset16, pCtx, iTex, 16/*tw*/, 16/*th*/); ASSERT_NR(rv == GFMRV_OK); // Initalize the FPS counter rv = gfm_initFPSCounter(pCtx, pSset8, 64/*firstTile*/); ASSERT_NR(rv == GFMRV_OK); // Create an object rv = gfmObject_getNew(&pObj); ASSERT_NR(rv == GFMRV_OK); // Initialize it rv = gfmObject_init(pObj, 0/*x*/, WNDH - 16/*y*/, WNDW, 16, 0/*pChild*/, 0); ASSERT_NR(rv == GFMRV_OK); // Make it immovable rv = gfmObject_setFixed(pObj); ASSERT_NR(rv == GFMRV_OK); // Create a sprite rv = gfmSprite_getNew(&pSpr); ASSERT_NR(rv == GFMRV_OK); // Initialize it rv = gfmSprite_init(pSpr, 16/*x*/, 16/*y*/, 6/*width*/, 12/*height*/, pSset16, -4/*offX*/, -4/*offY*/, 0/*pChild*/, 0/*type*/); ASSERT_NR(rv == GFMRV_OK); // Add the animations rv = gfmSprite_addAnimationsStatic(pSpr, pSprAnims); ASSERT_NR(rv == GFMRV_OK); // Set the sprite's gravity rv = gfmSprite_setVerticalAcceleration(pSpr, 500.0); ASSERT_NR(rv == GFMRV_OK); // Play an animation rv = gfmSprite_playAnimation(pSpr, ANIM_FALL); ASSERT_NR(rv == GFMRV_OK); // Alloc the quadtree rv = gfmQuadtree_getNew(&pQTRoot); ASSERT_NR(rv == GFMRV_OK); // Set the main loop framerate rv = gfm_setStateFrameRate(pCtx, FPS, FPS); ASSERT_NR(rv == GFMRV_OK); // Initialize the timer rv = gfm_setFPS(pCtx, FPS); ASSERT_NR(rv == GFMRV_OK); // Run until the window is closed while (gfm_didGetQuitFlag(pCtx) == GFMRV_FALSE) { rv = gfm_handleEvents(pCtx); ASSERT_NR(rv == GFMRV_OK); // Update stuff while (gfm_isUpdating(pCtx) == GFMRV_TRUE) { rv = gfm_fpsCounterUpdateBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); // Update the sprite's physics rv = gfmSprite_update(pSpr, pCtx); ASSERT_NR(rv == GFMRV_OK); // Update the object's physics rv = gfmObject_update(pObj, pCtx); ASSERT_NR(rv == GFMRV_OK); // Initialize the root of the quadtree //rv = gfmQuadtree_initRoot(pQTRoot, -16, -16, WNDW+32, WNDH+32, 2, 4); //ASSERT_NR(rv == GFMRV_OK); // These parameters forces the qt to be subdivided even with 2 objs // Also, it's a little smaller than the screen to be visible rv = gfmQuadtree_initRoot(pQTRoot, 2, 2, WNDW-4, WNDH-4, 2, 1); ASSERT_NR(rv == GFMRV_OK); // Populate the quadtree with the object rv = gfmQuadtree_populateObject(pQTRoot, pObj); ASSERT_NR(rv == GFMRV_OK); // Add the sprite, colliding with everything rv = gfmQuadtree_collideSprite(pQTRoot, pSpr); ASSERT_NR(rv == GFMRV_QUADTREE_DONE || rv == GFMRV_QUADTREE_OVERLAPED); while (rv != GFMRV_QUADTREE_DONE) { gfmObject *pObj1, *pObj2; gfmSprite *pSpr1, *pSpr2; int pType1, pType2; // Get the objects that collided rv = gfmQuadtree_getOverlaping(&pObj1, &pObj2, pQTRoot); ASSERT_NR(rv == GFMRV_OK); // Separate both objects rv = gfmObject_separateHorizontal(pObj1, pObj2); rv = gfmObject_separateVertical(pObj1, pObj2); // Try to get both sprites children rv = gfmObject_getChild((void**)&pSpr1, &pType1, pObj1); ASSERT_NR(rv == GFMRV_OK); rv = gfmObject_getChild((void**)&pSpr2, &pType2, pObj2); ASSERT_NR(rv == GFMRV_OK); // Change the sprite animation and cap its velocity if (pType1 == gfmType_sprite) { rv = gfmSprite_playAnimation(pSpr1, ANIM_STAND); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_setVerticalVelocity(pSpr1, 0.0); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_setVerticalAcceleration(pSpr1, 0.0); ASSERT_NR(rv == GFMRV_OK); } if (pType2 == gfmType_sprite) { rv = gfmSprite_playAnimation(pSpr2, ANIM_STAND); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_setVerticalVelocity(pSpr2, 0.0); ASSERT_NR(rv == GFMRV_OK); rv = gfmSprite_setVerticalAcceleration(pSpr2, 0.0); ASSERT_NR(rv == GFMRV_OK); } // Continue colliding rv = gfmQuadtree_continue(pQTRoot); ASSERT_NR(rv == GFMRV_QUADTREE_DONE || rv == GFMRV_QUADTREE_OVERLAPED); } rv = gfm_fpsCounterUpdateEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } // Draw stuff while (gfm_isDrawing(pCtx) == GFMRV_TRUE) { rv = gfm_drawBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the sprite rv = gfmSprite_draw(pSpr, pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the quadtree's bounds rv = gfmQuadtree_drawBounds(pQTRoot, pCtx, 0); ASSERT_NR(rv == GFMRV_OK); rv = gfm_drawEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } } rv = GFMRV_OK; __ret: gfmQuadtree_free(&pQTRoot); gfmSprite_free(&pSpr); gfmObject_free(&pObj); gfm_free(&pCtx); return rv; }
/** * Draw the quadtree to the screen. * * The object's type acts as index for the color. The first five colors are * reserved to represent the framework's types. It loops at gfmType_max * (currently 32). * * It's possible (and quite easy) to force different types to use the same * color. The following bitmask combination can be used: * * 27 bits for dif. entities | 5 bits for the different types * |---------------------------|-----| * * This way, the user may specify 27 different types, each of which may be used * in any of 2^27 different entities. * NOTE: This functions will be most likely slow!! Be careful when calling it! * * @param pQt The quadtree's root * @param pCtx The game's context * @param pColors The colors to be used; Must have 32 colors, each with RGB * components; The first set is for quadtree nodes, while the * others respect the types on gfmTypes.h * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD */ gfmRV gfmQuadtree_drawBounds(gfmQuadtreeRoot *pQt, gfmCtx *pCtx, unsigned char *pColors) { gfmRV rv; // Sanitize arguments ASSERT(pQt, GFMRV_ARGUMENTS_BAD); ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); // If no color was set, use the default ones if (pColors == 0) { pColors = gfmQt_defColors; } // Clear the call stack pQt->stack.pushPos = 0; // Push the root node to start colliding rv = gfmQuadtree_pushNode(pQt, pQt->pSelf); ASSERT_NR(rv == GFMRV_OK); // Iterate through all nodes while (pQt->stack.pushPos > 0 || pQt->pColliding) { gfmQuadtree *pNode; unsigned char *pNodeColor; // Pop the current node rv = gfmQuadtree_popNode(&pNode, pQt); ASSERT_NR(rv == GFMRV_OK); // Get the colors for the qt node pNodeColor = pColors; // Draw the current node rv = gfm_drawRect(pCtx, pNode->centerX - pNode->halfWidth, pNode->centerY - pNode->halfHeight, pNode->halfWidth * 2, pNode->halfHeight * 2, pNodeColor[0], pNodeColor[1], pNodeColor[2]); ASSERT_NR(rv == GFMRV_OK); // If it has children, push its children if (pNode->ppChildren[gfmQT_nw]) { gfmQuadtreePosition i; gfmQuadtree *pChild; i = gfmQT_nw; while (i < gfmQT_max) { // Get the current child pChild = pNode->ppChildren[i]; // Push it (so it will be drawn later) rv = gfmQuadtree_pushNode(pQt, pChild); ASSERT_NR(rv == GFMRV_OK); i++; } } else { gfmQuadtreeLL *pTmp; // Otherwise, draw its nodes pTmp = pNode->pNodes; while (pTmp) { unsigned int type; int height, width, x, y; void *pChild; // Get the object's child rv = gfmObject_getChild(&pChild, (int*)&type, pTmp->pSelf); ASSERT_NR(rv == GFMRV_OK); if (type == gfmType_sprite) { rv = gfmSprite_getChild(&pChild, (int*)&type, (gfmSprite*)pChild); ASSERT_NR(rv == GFMRV_OK); if (type == gfmType_none) { // If no custom type was specified, set it to sprite type = gfmType_sprite; } } else if (type == gfmType_none) { // If no custom type was specified, set it to object type = gfmType_object; } // Get the object's color if (type >= gfmType_max) type = (type % gfmType_max); pNodeColor = pColors + type * 3; // Get the object's position rv = gfmObject_getPosition(&x, &y, pTmp->pSelf); ASSERT_NR(rv == GFMRV_OK); // Get the object's dimensions rv = gfmObject_getDimensions(&width, &height, pTmp->pSelf); ASSERT_NR(rv == GFMRV_OK); // Draw the current node rv = gfm_drawRect(pCtx, x, y, width, height, pNodeColor[0], pNodeColor[1], pNodeColor[2]); ASSERT_NR(rv == GFMRV_OK); pTmp = pTmp->pNext; } } } rv = GFMRV_OK; __ret: return rv; }
/** * Continue colliding and adding the node to the quadtree * * @param pCtx The quadtree's root * @return GFMRV_ARGUMENTS_BAD, GFMRV_QUADTREE_OPERATION_NOT_ACTIVE, * GFMRV_QUADTREE_OVERLAPED, GFMRV_QUADTREE_DONE */ gfmRV gfmQuadtree_continue(gfmQuadtreeRoot *pCtx) { gfmRV rv; // Sanitize arguments ASSERT(pCtx, GFMRV_ARGUMENTS_BAD); // Check that the operation is active ASSERT(pCtx->pGroupList || pCtx->pObject, GFMRV_QUADTREE_OPERATION_NOT_ACTIVE); // Continue adding the object while (pCtx->stack.pushPos > 0 || pCtx->pColliding) { gfmQuadtree *pNode; // If we were colliding againts objects if (pCtx->pColliding) { gfmQuadtreeLL *pTmp; // Retrieve the current object and update the list pTmp = pCtx->pColliding; pCtx->pColliding = pCtx->pColliding->pNext; // Check if both objects overlaps pCtx->pOther = pTmp->pSelf; rv = gfmObject_isOverlaping(pCtx->pObject, pCtx->pOther); // -- Exit point -- // If they did overlap, return with that status if (rv == GFMRV_TRUE) { return GFMRV_QUADTREE_OVERLAPED; } //ASSERT(rv != GFMRV_TRUE, GFMRV_QUADTREE_OVERLAPED); } else { // Pop the current node rv = gfmQuadtree_popNode(&pNode, pCtx); ASSERT_NR(rv == GFMRV_OK); // If it has children, push its children if (pNode->ppChildren[gfmQT_nw]) { gfmQuadtreePosition i; gfmQuadtree *pChild; i = gfmQT_nw; while (i < gfmQT_max) { // Get the current child pChild = pNode->ppChildren[i]; // Check if the object overlaps this node rv = gfmQuadtree_overlap(pChild, pCtx->pObject); if (rv == GFMRV_TRUE) { // Push it (so it will collide later) rv = gfmQuadtree_pushNode(pCtx, pChild); ASSERT_NR(rv == GFMRV_OK); } i++; } } else { // If it's static, collide against the node's children if (pCtx->isStatic) { pCtx->pColliding = pNode->pNodes; } // Otherwise, check if inserting the node would subdivide // the node (and if there's still room for that else if (pNode->numObjects + 1 > pCtx->maxNodes && pNode->depth + 1 < pCtx->maxDepth) { // Subdivide the tree rv = gfmQuadtree_subdivide(pCtx, pNode); ASSERT_NR(rv == GFMRV_OK); // Push the node again so its children are overlapped/pushed rv = gfmQuadtree_pushNode(pCtx, pNode); ASSERT_NR(rv == GFMRV_OK); } else { // Otherwise, collide with its nodes pCtx->pColliding = pNode->pNodes; // Add the object to this node // NOTE: It's added to the begin, so it won't overlap itself rv = gfmQuadtree_insertObject(pCtx, pNode, pCtx->pObject); ASSERT_NR(rv == GFMRV_OK); } } } } // Check if there's another object from a group if (pCtx->pGroupList) { gfmSprite *pSpr; while (pCtx->pGroupList) { // Retrieve the next sprite... rv = gfmGroup_getNextSprite(&pSpr, &(pCtx->pGroupList)); ASSERT(rv == GFMRV_OK, rv); // Collide it! rv = gfmQuadtree_collideSprite(pCtx, pSpr); if (rv != GFMRV_QUADTREE_DONE) { // If this sprite didn't finish, return its status return rv; } // If this sprite didn't collide, go to the next } } // If the loop stoped, the operation finished pCtx->pObject = 0; rv = GFMRV_QUADTREE_DONE; __ret: return rv; }
/** * Updates everything in this state * * @param pGame The game "global" context */ static gfmRV state_mainMenu_update(gameCtx *pGame) { gfmInputState actionState, gifState; gfmRV rv; int actionCount, camWidth, camHeight, elapsed, gifCount, y; mainMenuCtx *pMainMenu; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); ASSERT(pGame->pMainMenu, GFMRV_ARGUMENTS_BAD); // Retrieve the main menu context pMainMenu = pGame->pMainMenu; // Get how long elapsed since the last frame rv = gfm_getElapsedTime(&elapsed, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); pMainMenu->animationTime += elapsed; while (pMainMenu->animationTime > 16) { // Get the tile data and animate it if (pMainMenu->titleAnimation < sizeof(titleData) / sizeof(int)) { int *pData; // Go to the next valid (i.e., non-invisible) tile while (pMainMenu->titleAnimation + 1 < sizeof(titleData) && titleData[pMainMenu->titleAnimation] == -1) { pMainMenu->titleAnimation++; } // Get its data rv = gfmTilemap_getData(&pData, pMainMenu->pTitle); ASSERT_NR(rv == GFMRV_OK); // Update the tile pData[pMainMenu->titleAnimation] = titleData[pMainMenu->titleAnimation]; pMainMenu->titleAnimation++; pMainMenu->frameCount++; } pMainMenu->animationTime -= 16; } // Get the game's dimensions rv = gfm_getCameraDimensions(&camWidth, &camHeight, pGame->pCtx); ASSERT_NR(rv == GFMRV_OK); // Animate the BG and floor position // BG position is calculate as "-height + (height + pos) * alpha" to start // it outside the screen and move until 'pos' y = -128 + (128 + 56) * pMainMenu->frameCount / pMainMenu->numAnimFrames; rv = gfmTilemap_setPosition(pMainMenu->pBackground, 0, y); ASSERT_NR(rv == GFMRV_OK); // For the same reason, the floor position is calculated as // "WND_HEIGHT - (WND_HEIGHT - pos) * alpha" y = camHeight - (camHeight - 176) * pMainMenu->frameCount / pMainMenu->numAnimFrames; rv = gfmTilemap_setPosition(pMainMenu->pFloor, 0, y); ASSERT_NR(rv == GFMRV_OK); // Update the text timer (it will only render on positive times, and should // flick every half second) pMainMenu->textTimer += elapsed; if (pMainMenu->textTimer >= 750) { pMainMenu->textTimer -= 1000; } // Get the action button state to, possibly, go to the play state rv = gfm_getKeyState(&actionState, &actionCount, pGame->pCtx, pGame->actionHnd); ASSERT_NR(rv == GFMRV_OK); // TODO Check for button presses if (actionState & gfmInput_pressed) { // TODO Actually switch to the game_stageSelect pGame->state = game_playState; pGame->switchState = 1; } // Get the gif button state rv = gfm_getKeyState(&gifState, &gifCount, pGame->pCtx, pGame->gifHnd); ASSERT_NR(rv == GFMRV_OK); //if ((gifState & gfmInput_justPressed) == gfmInput_justPressed) { // rv = gfm_recordGif(pGame->pCtx, 10000, "./promo/mainMenu.gif", 20/*len*/, // 0/*saveToCurrentPath*/); // ASSERT_NR(rv == GFMRV_OK); //} rv = GFMRV_OK; __ret: return rv; }
/** * Initialize the menustate * * @return GFraMe error code */ static GFraMe_ret ms_init(struct stMenustate *ms) { GFraMe_ret rv; GFraMe_save sv; int tmp, ctrPl1, ctrPl2; ms->pO = 0; ms->pM = 0; // Check if there's already a saved game rv = GFraMe_save_bind(&sv, SAVEFILE); ASSERT_NR(rv == GFraMe_ret_ok); // Check if something other than the version is written if (sv.size > 50) ms->hasSave = 1; else ms->hasSave = 0; GFraMe_save_close(&sv); // Check the configurations rv = GFraMe_save_bind(&sv, CONFFILE); GFraMe_assertRet(rv == GFraMe_ret_ok, "Error reading config file", __ret); // Try to read both players control mode ctrPl1 = -1; ctrPl2 = -1; rv = GFraMe_save_read_int(&sv, "ctr_pl1", &tmp); if (rv == GFraMe_ret_ok) ctrPl1 = tmp; rv = GFraMe_save_read_int(&sv, "ctr_pl2", &tmp); if (rv == GFraMe_ret_ok) ctrPl2 = tmp; // set the control scheme if (ctrPl1 != -1 && ctrPl2 != -1) { tmp = ctr_setModeForce(ID_PL1, ctrPl1); if (tmp) tmp = ctr_setModeForce(ID_PL2, ctrPl2); // In case any error happened, set the default if (!tmp) ctr_setDef(); } else ctr_setDef(); // Set the song volume rv = GFraMe_save_read_int(&sv, "music", &tmp); if (rv == GFraMe_ret_ok) audio_setVolume(tmp); else audio_setVolume(60); // Set the sfx volume rv = GFraMe_save_read_int(&sv, "sfx", &tmp); if (rv == GFraMe_ret_ok) sfx_setVolume(tmp); else sfx_setVolume(50); GFraMe_save_close(&sv); // Zero some variables ms->lastPressedTime = 0; ms->firstPress = 0; // Set the selected option if (ms->hasSave) ms->curOpt = 0; else ms->curOpt = 1; // Set text position ms->textX = 112; ms->textY = 176; // Set the dev icon position ms->iconX = 35 * 8; ms->iconY = 25 * 8; // Start the menu ms->runMenu = 1; // Set the title position ms->isTitleSet = 0; ms->j1X = 82; ms->j1Yf = -32.0f; ms->j1Y = -32; ms->j2X = 122; ms->j2Yf = -40.0f; ms->j2Y = -40; ms->aX = 154; ms->aYf = -48.0f; ms->aY = -48; ms->tX = 194; ms->tYf = -54.0f; ms->tY = -54; ms->idleTime = 0; rv = map_init(&ms->pM); GFraMe_assertRet(rv == GFraMe_ret_ok, "Failed to load background", __ret); rv = map_loadf(ms->pM, "maps/mainmenu.gfm"); GFraMe_assertRet(rv == GFraMe_ret_ok, "Failed to load background", __ret); rv = obj_getNew(&ms->pO); GFraMe_assertRet(rv == GFraMe_ret_ok, "Failed to load background", __ret); obj_setZero(ms->pO); obj_setBounds(ms->pO, 8*8, 17*8, 2*8, 2*8); obj_setID(ms->pO, ID_HEARTUP); obj_setCommonEvent(ms->pO, CE_NONE); cam_x = 0; cam_y = 0; cam_setMapDimension(40, 30); audio_playMenu(); rv = GFraMe_ret_ok; __ret: return rv; }
int main(int arg, char *argv[]) { gfmCtx *pCtx; gfmGroup *pGrp; gfmRV rv; gfmQuadtreeRoot *pQt; gfmSpriteset *pSset4, *pSset8; int click, gif, frame, iTex; // Initialize every variable pCtx = 0; pGrp = 0; pQt = 0; // Try to get a new context rv = gfm_getNew(&pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_initStatic(pCtx, "com.gfmgamecorner", "gframe_particle_collision"); ASSERT_NR(rv == GFMRV_OK); // Initialize the window rv = gfm_initGameWindow(pCtx, WNDW, WNDH, 640, 480, 0, 0); ASSERT_NR(rv == GFMRV_OK); // Create the inputs rv = gfm_addVirtualKey(&click, pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_bindInput(pCtx, click, gfmPointer_button); ASSERT_NR(rv == GFMRV_OK); rv = gfm_addVirtualKey(&gif, pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_bindInput(pCtx, gif, gfmKey_space); ASSERT_NR(rv == GFMRV_OK); // Load the texture rv = gfm_loadTextureStatic(&iTex, pCtx, "rainbow_atlas.bmp", 0xff00ff); ASSERT_NR(rv == GFMRV_OK); // Set it as the default rv = gfm_setDefaultTexture(pCtx, iTex); ASSERT_NR(rv == GFMRV_OK); // Create the spritesets rv = gfm_createSpritesetCached(&pSset8, pCtx, iTex, 8/*tw*/, 8/*th*/); ASSERT_NR(rv == GFMRV_OK); rv = gfm_createSpritesetCached(&pSset4, pCtx, iTex, 4/*tw*/, 4/*th*/); ASSERT_NR(rv == GFMRV_OK); // Initalize the FPS counter rv = gfm_initFPSCounter(pCtx, pSset8, 0/*firstTile*/); ASSERT_NR(rv == GFMRV_OK); // Create the group rv = gfmGroup_getNew(&pGrp); ASSERT_NR(rv == GFMRV_OK); // Set the group attributes rv = gfmGroup_setDefSpriteset(pGrp, pSset4); ASSERT_NR(rv == GFMRV_OK); rv = gfmGroup_setDefDimensions(pGrp, 4/*width*/, 4/*height*/, -2/*offX*/, -2/*offY*/); ASSERT_NR(rv == GFMRV_OK); // Create the group's sprite rv = gfmGroup_preCache(pGrp, 0/*initLen*/, 8192/*maxLen*/); ASSERT_NR(rv == GFMRV_OK); // Those can be set after caching everything, since they are global rv = gfmGroup_setDeathOnLeave(pGrp, 1); ASSERT_NR(rv == GFMRV_OK); // Set the draw order (FUN!!!) rv = gfmGroup_setDrawOrder(pGrp, gfmDrawOrder_oldestFirst); ASSERT_NR(rv == GFMRV_OK); rv = gfmGroup_setDefType(pGrp, PARTICLE); ASSERT_NR(rv == GFMRV_OK); // Set collision (EVEN MORE FUN!!!) rv = gfmGroup_setCollisionQuality(pGrp, gfmCollisionQuality_collideEverything); ASSERT_NR(rv == GFMRV_OK); rv = gfmQuadtree_getNew(&pQt); ASSERT_NR(rv == GFMRV_OK); // Set the main loop framerate rv = gfm_setStateFrameRate(pCtx, FPS, FPS); ASSERT_NR(rv == GFMRV_OK); // Initialize the timer rv = gfm_setFPS(pCtx, FPS); ASSERT_NR(rv == GFMRV_OK); // Run until the window is closed frame = 0; while (gfm_didGetQuitFlag(pCtx) == GFMRV_FALSE) { rv = gfm_handleEvents(pCtx); ASSERT_NR(rv == GFMRV_OK); // Update stuff while (gfm_isUpdating(pCtx) == GFMRV_TRUE) { gfmInput *pInput; gfmInputState kclick, kgif; int nclick, ngif; rv = gfm_fpsCounterUpdateBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getInput(&pInput, pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getKeyState(&kclick, &nclick, pCtx, click); ASSERT_NR(rv == GFMRV_OK); rv = gfm_getKeyState(&kgif, &ngif, pCtx, gif); ASSERT_NR(rv == GFMRV_OK); if ((kgif & gfmInput_justPressed) == gfmInput_justPressed) { gfm_didExportGif(pCtx); rv = gfm_recordGif(pCtx, 10000/*ms*/, "anim.gif", 8, 0); ASSERT_NR(rv == GFMRV_OK); } // Spawn particles! if ((kclick & gfmInput_pressed) == gfmInput_pressed && nclick == 2) { int i, x, y; rv = gfmInput_getPointerPosition(&x, &y, pInput); ASSERT_NR(rv == GFMRV_OK); i = 0; while (i < 8) { gfmSprite *pSpr; rv = gfmGroup_recycle(&pSpr, pGrp); ASSERT_NR(rv == GFMRV_OK || rv == GFMRV_GROUP_MAX_SPRITES); if (rv == GFMRV_OK) { int sprX, sprY; float fvx, fvy; if (i < 4) { int vx, vy; // Set X = 1 on even and Y = 1 on odd vx = (i % 2); vy = 1 - vx; // Set to positive on 0,1 and negative on 2,3 fvx = vx * (1 - i / 2) - vx * (i / 2); fvy = vy * (1 - i / 2) - vy * (i / 2); } else if (i >= 4) { int j; // Set to positive on 0,1 and negative on 2,3 j = i % 4; fvx = (1 - j / 2) - (j / 2); // Set to positive on 1,2 and negative on 0,3 j = (i - 1) % 4; fvy = (1 - j / 2) - (j / 2); fvx *= 0.7071; fvy *= 0.7071; } sprX = x + 16 * fvx; sprY = y + 16 * fvy; fvx *= 100; fvy *= 100; rv = gfmGroup_setPosition(pGrp, sprX, sprY); ASSERT_NR(rv == GFMRV_OK); rv = gfmGroup_setFrame(pGrp, frame); ASSERT_NR(rv == GFMRV_OK); rv = gfmGroup_setVelocity(pGrp, fvx, fvy); ASSERT_NR(rv == GFMRV_OK); frame = (frame + 1) % 7; } i++; } } // Update group rv = gfmGroup_update(pGrp, pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfmQuadtree_initRoot(pQt, 0, 0, WNDW, WNDH, 6, 10); ASSERT_NR(rv == GFMRV_OK); // Collide everything rv = gfmQuadtree_collideGroup(pQt, pGrp); ASSERT(rv == GFMRV_QUADTREE_OVERLAPED || rv == GFMRV_QUADTREE_DONE, rv); while (rv != GFMRV_QUADTREE_DONE) { gfmObject *pObj1, *pObj2; gfmSprite *pSpr1, *pSpr2; gfmGroupNode *pNode1, *pNode2; int type1, type2; // Retrieve the colliding objects rv = gfmQuadtree_getOverlaping(&pObj1, &pObj2, pQt); ASSERT(rv == GFMRV_OK, rv); // Retrieve its child and the child's type rv = gfmObject_getChild((void**)&pSpr1, &type1, pObj1); ASSERT(rv == GFMRV_OK, rv); rv = gfmObject_getChild((void**)&pSpr2, &type2, pObj2); ASSERT(rv == GFMRV_OK, rv); // Retrieve the child's child and type if (type1 == gfmType_sprite) { rv = gfmSprite_getChild((void**)&pNode1, &type1, pSpr1); ASSERT(rv == GFMRV_OK, rv); } if (type2 == gfmType_sprite) { rv = gfmSprite_getChild((void**)&pNode2, &type2, pSpr2); ASSERT(rv == GFMRV_OK, rv); } // If two particles are colliding, do fun stuff if (type1 == PARTICLE && type2 == PARTICLE) { rv = gfmObject_collide(pObj1, pObj2); ASSERT(rv == GFMRV_TRUE || rv == GFMRV_FALSE, rv); if (rv == GFMRV_TRUE) { double vx, vy; rv = gfmObject_getVelocity(&vx, &vy, pObj1); ASSERT(rv == GFMRV_OK, rv); rv = gfmObject_setVelocity(pObj1, vy * 1.01, vx * 1.01); ASSERT(rv == GFMRV_OK, rv); rv = gfmObject_getVelocity(&vx, &vy, pObj2); ASSERT(rv == GFMRV_OK, rv); rv = gfmObject_setVelocity(pObj2, vy * 1.01, vx * 1.01); ASSERT(rv == GFMRV_OK, rv); } } rv = gfmQuadtree_continue(pQt); ASSERT(rv == GFMRV_QUADTREE_OVERLAPED || rv == GFMRV_QUADTREE_DONE, rv); } rv = gfm_fpsCounterUpdateEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } // Draw stuff while (gfm_isDrawing(pCtx) == GFMRV_TRUE) { rv = gfm_drawBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw group rv = gfmGroup_draw(pGrp, pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_drawEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } } rv = GFMRV_OK; __ret: gfmGroup_free(&pGrp); gfmQuadtree_free(&pQt); gfm_free(&pCtx); return rv; }
/** * Initialize everything needed by the main menu * * @param pGame The game "global" context * @return GFMRV_OK, GFMRV_ARGUMENTS_BAD, GFMRV_ALLOC_FAILED, ... */ static gfmRV state_mainMenu_init(gameCtx *pGame) { gfmRV rv; int i; mainMenuCtx *pMainMenu; // Sanitize arguments ASSERT(pGame, GFMRV_ARGUMENTS_BAD); // Alloc the main menu pGame->pMainMenu = (mainMenuCtx*)malloc(sizeof(mainMenuCtx)); ASSERT(pGame->pMainMenu, GFMRV_ALLOC_FAILED); pMainMenu = pGame->pMainMenu; // Clean it memset(pMainMenu, 0x0, sizeof(mainMenuCtx)); // Alloc and initialize the title rv = gfmTilemap_getNew(&(pMainMenu->pTitle)); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_init(pMainMenu->pTitle, pGame->pSset8x8, 32, 16, -1); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_setPosition(pMainMenu->pTitle, 32, 0); ASSERT_NR(rv == GFMRV_OK); // Alloc and load the background rv = gfmTilemap_getNew(&(pMainMenu->pBackground)); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_init(pMainMenu->pBackground, pGame->pSset8x16, 40, 8, -1); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_loadStatic(pMainMenu->pBackground, backgroundData, 40, 8); ASSERT_NR(rv == GFMRV_OK); // Alloc and load the floor rv = gfmTilemap_getNew(&(pMainMenu->pFloor)); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_init(pMainMenu->pFloor, pGame->pSset16x16, 20, 8, -1); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_loadStatic(pMainMenu->pFloor, floorData, 20, 4); ASSERT_NR(rv == GFMRV_OK); // Alloc and initialize the "touch to play" text rv = gfmText_getNew(&(pMainMenu->pText)); ASSERT_NR(rv == GFMRV_OK); rv = gfmText_init(pMainMenu->pText, 96/*x*/, 168/*y*/, 17/*maxWidth*/, 1/*maxLines*/, 0/*delay*/, 0/*doBindToScreen*/, pGame->pSset8x8, 0/*firstTile*/); ASSERT_NR(rv == GFMRV_OK); // TODO Initialize accordingly with desktop or mobile rv = gfmText_setTextStatic(pMainMenu->pText, "--TOUCH TO PLAY--", 1); ASSERT_NR(rv == GFMRV_OK); // Count how many frames there will be in the animation pMainMenu->numAnimFrames = 0; i = 0; while (i < sizeof(titleData) / sizeof(int)) { if (titleData[i] != -1) { pMainMenu->numAnimFrames++; } i++; } // TODO initialize everything else rv = GFMRV_OK; __ret: return rv; }
int main(int arg, char *argv[]) { gfmCtx *pCtx; gfmRV rv; gfmQuadtreeRoot *pQTRoot; gfmSpriteset *pSset; gfmTilemap *pTMap; int iTex; // Initialize every variable pCtx = 0; pTMap = 0; pSset = 0; pQTRoot = 0; // Try to get a new context rv = gfm_getNew(&pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfm_initStatic(pCtx, "com.gfmgamecorner", "gframe_test_tilemap"); ASSERT_NR(rv == GFMRV_OK); // Initialize the window rv = gfm_initGameWindow(pCtx, 160, 120, 640, 480, 0, 0); ASSERT_NR(rv == GFMRV_OK); // Load the texture rv = gfm_loadTextureStatic(&iTex, pCtx, "ld32-atlas.bmp", 0xff00ff); ASSERT_NR(rv == GFMRV_OK); // Set it as the default rv = gfm_setDefaultTexture(pCtx, iTex); ASSERT_NR(rv == GFMRV_OK); // Create a spriteset rv = gfmSpriteset_getNew(&pSset); ASSERT_NR(rv == GFMRV_OK); rv = gfmSpriteset_initCached(pSset, pCtx, iTex, 8/*tw*/, 8/*th*/); ASSERT_NR(rv == GFMRV_OK); // Create and load the tilemap rv = gfmTilemap_getNew(&pTMap); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_init(pTMap, pSset, 1/*mapWidth*/, 1/*mapHeight*/, 0/*defTile*/); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_loadf(pTMap, pCtx, "map.gfm", 7/*strLen*/, pDictStr, pDictTypes, 2); ASSERT_NR(rv == GFMRV_OK); // Alloc the quadtree rv = gfmQuadtree_getNew(&pQTRoot); ASSERT_NR(rv == GFMRV_OK); // Set the main loop framerate rv = gfm_setStateFrameRate(pCtx, FPS, FPS); ASSERT_NR(rv == GFMRV_OK); // Initialize the timer rv = gfm_setFPS(pCtx, FPS); ASSERT_NR(rv == GFMRV_OK); // Run until the window is closed while (gfm_didGetQuitFlag(pCtx) == GFMRV_FALSE) { rv = gfm_handleEvents(pCtx); ASSERT_NR(rv == GFMRV_OK); // Update stuff while (gfm_isUpdating(pCtx) == GFMRV_TRUE) { rv = gfm_fpsCounterUpdateBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); // These parameters forces the qt to be subdivided even with 2 objs // Also, it's a little smaller than the screen to be visible rv = gfmQuadtree_initRoot(pQTRoot, 2, 2, WNDW-4, WNDH-4, 2, 1); ASSERT_NR(rv == GFMRV_OK); // Populate the quadtree with the object rv = gfmQuadtree_populateTilemap(pQTRoot, pTMap); ASSERT_NR(rv == GFMRV_OK); rv = gfm_fpsCounterUpdateEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } // Draw stuff while (gfm_isDrawing(pCtx) == GFMRV_TRUE) { rv = gfm_drawBegin(pCtx); ASSERT_NR(rv == GFMRV_OK); rv = gfmTilemap_draw(pTMap, pCtx); ASSERT_NR(rv == GFMRV_OK); // Draw the quadtree's bounds rv = gfmQuadtree_drawBounds(pQTRoot, pCtx, 0); ASSERT_NR(rv == GFMRV_OK); rv = gfm_drawEnd(pCtx); ASSERT_NR(rv == GFMRV_OK); } } rv = GFMRV_OK; __ret: gfmQuadtree_free(&pQTRoot); gfmTilemap_free(&pTMap); gfmSpriteset_free(&pSset); gfm_free(&pCtx); return rv; }