#include "shell/Shell.h" #include #include #include #include #ifdef __APPLE__ #include #include #else #include #include #endif #include "chipmunk.h" #include "constants/AdhesionConstants.h" #include "game/GameState.h" #include "game/Level.h" #include "game/LevelList.h" #include "graphics/Texture.h" #include "graphics/TextureManager.h" #include "shell/Shell.h" #include "shell/ShellKeyCodes.h" #include "utilities/FixedIntervalRunLoop.h" #include "utilities/IOUtilities.h" #include "utilities/JSONParser.h" #define DEFAULT_WINDOW_WIDTH 800 #define DEFAULT_WINDOW_HEIGHT 600 static FixedIntervalRunLoop * runLoop; static int windowWidth, windowHeight; static GameState * gameState; static LevelList * levelList; static int levelIndex; static bool dragging; static float dragStartX, dragStartY; static float dragEndX, dragEndY; static bool levelCompleted; static bool died; static void run(void * context); static void levelCompletedCallback(void); static void diedCallback(void); static const GLubyte backgroundColor[] = {0x00, 0x00, 0x00}; static const GLubyte stickyWallColor[] = {0xFF, 0xFF, 0xFF}; static const GLubyte bouncyWallColor[] = {0x7F, 0x3F, 0xFF}; static const GLubyte deadlyWallColor[] = {0xFF, 0x00, 0x00}; static const GLubyte exitColor[] = {0x3F, 0xFF, 0x3F}; static const GLubyte ballFrameColor[] = {0xCF, 0xCF, 0xFF}; static const GLubyte ballInteriorColor[] = {0x9F, 0x9F, 0xFF}; static const GLubyte dragColor[] = {0xFF, 0xFF, 0x00}; static void setProjection() { float ratio; glMatrixMode(GL_PROJECTION); glLoadIdentity(); ratio = (float) windowWidth / (float) windowHeight; glOrtho(-ratio, ratio, -1.0f, 1.0f, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); } static void initGL() { glEnableClientState(GL_VERTEX_ARRAY); glClearColor(backgroundColor[0] / 255.0f, backgroundColor[1] / 255.0f, backgroundColor[1] / 255.0f, 0.0f); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); setProjection(); } static void initGame() { JSONNode * node; TextureManager_loadTextureList(resourcePath("textures.json"), Shell_getResourcePath()); node = jsonFromFile(resourcePath("levels.json")); if (node == NULL) { fprintf(stderr, "Couldn't load levels.json; bailing"); exit(EXIT_FAILURE); } levelList = LevelList_fromJSON(node); JSONParser_freeNodeContents(node); free(node); if (levelList->levelCount < 1) { fprintf(stderr, "No levels in levels.json; bailing"); exit(EXIT_FAILURE); } levelIndex = 0; gameState = GameState_create(levelCompletedCallback, diedCallback); GameState_loadLevel(gameState, levelList->levels[levelIndex]); dragging = false; runLoop = FixedIntervalRunLoop_create(ADHESION_UPDATE_INTERVAL, run, NULL); } static void run(void * context) { levelCompleted = false; died = false; GameState_step(gameState, ADHESION_UPDATE_INTERVAL); if (levelCompleted) { levelIndex++; levelIndex %= levelList->levelCount; GameState_loadLevel(gameState, levelList->levels[levelIndex]); } else if (died) { GameState_loadLevel(gameState, levelList->levels[levelIndex]); } } #define BALL_SUBDIVISIONS 32 static void draw() { GLfloat ballCenterVertices[8]; GLfloat ballOutlineVertices[BALL_SUBDIVISIONS * 2]; GLfloat dragVertices[4]; GLfloat anchorVertices[8] = {-0.15f, 0.0f, 0.15f, 0.0f, 0.0f, -0.15f, 0.0f, 0.15f}; GLfloat anchorToBallVertices[4]; unsigned int wallIndex; Level * level; GLenum error; float ratio; unsigned int vertexIndex; unsigned int anchorIndex; Texture * instructionsTexture; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); level = levelList->levels[levelIndex]; glPushMatrix(); ratio = (float) windowWidth / (float) windowHeight; if (ratio > level->size.x / level->size.y) { glScalef(2.0f / level->size.y, 2.0f / level->size.y, 1.0f); } else { glScalef(2.0f / level->size.x * ratio, 2.0f / level->size.x * ratio, 1.0f); } glTranslatef(-level->center.x, -level->center.y, 0.0f); for (wallIndex = 0; wallIndex < level->wallCount; wallIndex++) { switch (level->walls[wallIndex].type) { case WALL_TYPE_STICKY: glColor3ubv(stickyWallColor); break; case WALL_TYPE_EXIT: glColor3ubv(exitColor); break; case WALL_TYPE_BOUNCY: glColor3ubv(bouncyWallColor); break; case WALL_TYPE_DEADLY: glColor3ubv(deadlyWallColor); break; } glVertexPointer(2, GL_FLOAT, 0, level->walls[wallIndex].vertices); glDrawArrays(GL_LINE_STRIP, 0, level->walls[wallIndex].vertexCount); } glPushMatrix(); glTranslatef(gameState->player->p.x, gameState->player->p.y, 0.0f); glRotatef(gameState->player->a * 180.0f / M_PI, 0.0f, 0.0f, 1.0f); ballCenterVertices[0] = -level->radius; ballCenterVertices[1] = 0.0f; ballCenterVertices[2] = level->radius; ballCenterVertices[3] = 0.0f; ballCenterVertices[4] = 0.0f; ballCenterVertices[5] = -level->radius; ballCenterVertices[6] = 0.0f; ballCenterVertices[7] = level->radius; glColor3ubv(ballInteriorColor); glVertexPointer(2, GL_FLOAT, 0, ballCenterVertices); glDrawArrays(GL_LINES, 0, 4); for (vertexIndex = 0; vertexIndex < BALL_SUBDIVISIONS; vertexIndex++) { ballOutlineVertices[vertexIndex * 2 + 0] = cos(M_PI * 2 * vertexIndex / BALL_SUBDIVISIONS) * level->radius; ballOutlineVertices[vertexIndex * 2 + 1] = sin(M_PI * 2 * vertexIndex / BALL_SUBDIVISIONS) * level->radius; } glColor3ubv(ballFrameColor); glVertexPointer(2, GL_FLOAT, 0, ballOutlineVertices); glDrawArrays(GL_LINE_LOOP, 0, BALL_SUBDIVISIONS); glPopMatrix(); glColor3ub(0xFF, 0x00, 0x00); glVertexPointer(2, GL_FLOAT, 0, anchorVertices); for (anchorIndex = 0; anchorIndex < gameState->anchorCount; anchorIndex++) { glPushMatrix(); glTranslatef(gameState->anchors[anchorIndex].x, gameState->anchors[anchorIndex].y, 0.0f); glDrawArrays(GL_LINES, 0, 4); glPopMatrix(); } if (gameState->stuck) { glColor3ub(0x00, 0xFF, 0xFF); glPushMatrix(); glTranslatef(gameState->anchors[gameState->anchorCount - 1].x, gameState->anchors[gameState->anchorCount - 1].y, 0.0f); glRotatef(45.0f, 0.0f, 0.0f, 1.0f); glDrawArrays(GL_LINES, 0, 4); glPopMatrix(); } anchorToBallVertices[2] = gameState->player->p.x; anchorToBallVertices[3] = gameState->player->p.y; glColor3ub(0x5F, 0x5F, 0x5F); glVertexPointer(2, GL_FLOAT, 0, anchorToBallVertices); for (anchorIndex = 0; anchorIndex < gameState->anchorCount; anchorIndex++) { anchorToBallVertices[0] = gameState->anchors[anchorIndex].x; anchorToBallVertices[1] = gameState->anchors[anchorIndex].y; glDrawArrays(GL_LINES, 0, 2); } glPopMatrix(); if (dragging) { dragVertices[0] = dragStartX; dragVertices[1] = dragStartY; dragVertices[2] = dragEndX; dragVertices[3] = dragEndY; glColor3ubv(dragColor); glVertexPointer(2, GL_FLOAT, 0, dragVertices); glDrawArrays(GL_LINES, 0, 2); } instructionsTexture = TextureManager_getTexture("instructions"); if (instructionsTexture != NULL) { GLint instructionsVertices[8] = {4, -36, 516, -36, 516, -4, 4, -4}; GLint instructionsTexCoords[8] = {0, 0, 1, 0, 1, 1, 0, 1}; instructionsVertices[1] += windowHeight; instructionsVertices[3] += windowHeight; instructionsVertices[5] += windowHeight; instructionsVertices[7] += windowHeight; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0f, windowWidth, 0.0f, windowHeight, -1.0f, 1.0f); Texture_activate(instructionsTexture); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_INT, 0, instructionsVertices); glTexCoordPointer(2, GL_INT, 0, instructionsTexCoords); glColor3ub(0xFF, 0xFF, 0xFF); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); Texture_deactivate(instructionsTexture); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } error = glGetError(); if (error != GL_NO_ERROR) { puts((char *) gluErrorString(error)); } } static void levelCompletedCallback(void) { levelCompleted = true; } static void diedCallback(void) { died = true; } const char * Target_getName() { return "Adhesion"; } void Target_init(int argc, char ** argv) { windowWidth = DEFAULT_WINDOW_WIDTH; windowHeight = DEFAULT_WINDOW_HEIGHT; cpInitChipmunk(); initGL(); initGame(); Shell_mainLoop(); } void Target_keyDown(int charCode, int keyCode) { if (keyCode == KEYBOARD_R) { GameState_loadLevel(gameState, levelList->levels[levelIndex]); } } void Target_keyUp(int charCode, int keyCode) { } static float localX(float windowX) { float ratio; ratio = (float) windowWidth / (float) windowHeight; return windowX / (windowHeight * 0.5f) - ratio; } static float localY(float windowY) { return (2.0f - windowY / (windowHeight * 0.5f)) - 1.0f; } void Target_mouseDown(int buttonNumber, float x, float y) { dragging = true; dragStartX = dragEndX = localX(x); dragStartY = dragEndY = localY(y); } #define FORCE_MULTIPLIER 30 void Target_mouseUp(int buttonNumber, float x, float y) { dragging = false; GameState_launchPlayer(gameState, (dragStartX - dragEndX) * FORCE_MULTIPLIER, (dragStartY - dragEndY) * FORCE_MULTIPLIER); } void Target_mouseMoved(float x, float y) { } #define MAX_DISTANCE 1.5 void Target_mouseDragged(int buttonMask, float x, float y) { float distance; Vector2 dragVector; dragEndX = localX(x); dragEndY = localY(y); dragVector = Vector2_withValues(dragEndX - dragStartX, dragEndY - dragStartY); distance = sqrt(dragVector.x * dragVector.x + dragVector.y * dragVector.y); if (distance > MAX_DISTANCE) { dragVector.x /= distance; dragVector.y /= distance; dragVector.x *= MAX_DISTANCE; dragVector.y *= MAX_DISTANCE; dragEndX = dragStartX + dragVector.x; dragEndY = dragStartY + dragVector.y; } } void Target_resized(int newWidth, int newHeight) { windowWidth = newWidth; windowHeight = newHeight; glViewport(0, 0, newWidth, newHeight); setProjection(); } void Target_draw() { runLoop->run(runLoop); draw(); Shell_redisplay(); }