// Copyright (c) 2014 Alex Diener. All rights reserved. #include "glgraphics/GLIncludes.h" #include "shell/Shell.h" #include "shell/ShellKeyCodes.h" #include "utilities/IOUtilities.h" #include "watertowerclassic/MainMenuScreen.h" #include "watertowerclassic/ShellStateGlobals.h" #include "watertowerclassic/SharedDefinitions.h" #include "watertowerclassic/SharedEvents.h" #include #include #include #define SUPERCLASS Screen enum { MENU_ITEM_PLAY, MENU_ITEM_SETTINGS, MENU_ITEM_MANAGE_LEVELS, MENU_ITEM_ABOUT, MENU_ITEM_EXIT, MENU_ITEM_COUNT }; static const char * const menuItemNames[MENU_ITEM_COUNT] = { "PLAY", "SETTINGS", "MANAGE LEVELS", "ABOUT", "EXIT" }; MainMenuScreen * MainMenuScreen_create(GameSession * gameSession) { stemobject_create_implementation(MainMenuScreen, init, gameSession) } bool MainMenuScreen_init(MainMenuScreen * self, GameSession * gameSession) { call_super(init, self); self->activate = MainMenuScreen_activate; self->deactivate = MainMenuScreen_deactivate; self->dispose = MainMenuScreen_dispose; self->gameSession = gameSession; self->inputController = InputController_create(gameSession->inputMap, "left", "right", "down", "up", "jump", "action", "pause", NULL); self->menuItemIndex = 0; self->backgrounded = false; glGenBuffersARB(1, &self->vertexBufferID); glGenBuffersARB(1, &self->indexBufferID); self->bitmapFont = ResourceManager_referenceResource(self->gameSession->resourceManager, "bitmap_font", "pixelfont.json"); self->backgroundTexture = ResourceManager_referenceResource(self->gameSession->resourceManager, "texture", "splash_background.json"); self->titleTextTexture = ResourceManager_referenceResource(self->gameSession->resourceManager, "texture", "splash_text.json"); return true; } void MainMenuScreen_dispose(MainMenuScreen * self) { glDeleteBuffersARB(1, &self->vertexBufferID); glDeleteBuffersARB(1, &self->indexBufferID); ResourceManager_releaseResource(self->gameSession->resourceManager, "bitmap_font", "pixelfont.json"); ResourceManager_releaseResource(self->gameSession->resourceManager, "texture", "splash_background.json"); ResourceManager_releaseResource(self->gameSession->resourceManager, "texture", "splash_text.json"); InputController_dispose(self->inputController); call_super(dispose, self); } static void menuLeft(MainMenuScreen * self, unsigned int menuItemIndex) { if (menuItemIndex == 0) { self->gameSession->selectedLevelSetIndex += self->gameSession->levelSetCount - 1; self->gameSession->selectedLevelSetIndex %= self->gameSession->levelSetCount; } } static void menuRight(MainMenuScreen * self, unsigned int menuItemIndex) { if (menuItemIndex == 0) { self->gameSession->selectedLevelSetIndex++; self->gameSession->selectedLevelSetIndex %= self->gameSession->levelSetCount; } } static void menuDown(MainMenuScreen * self) { self->menuItemIndex += 1; self->menuItemIndex %= MENU_ITEM_COUNT; } static void menuUp(MainMenuScreen * self) { self->menuItemIndex += MENU_ITEM_COUNT - 1; self->menuItemIndex %= MENU_ITEM_COUNT; } static void menuAction(MainMenuScreen * self, unsigned int menuItemIndex) { switch (menuItemIndex) { case MENU_ITEM_PLAY: self->gameSession->testingLevel = false; self->gameSession->levelSet = self->gameSession->levelSets[self->gameSession->selectedLevelSetIndex]; self->gameSession->startLevelIndex = 0; InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, "start_game"); break; case MENU_ITEM_SETTINGS: InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, "settings"); break; case MENU_ITEM_MANAGE_LEVELS: InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, "manage_levels"); break; case MENU_ITEM_ABOUT: InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, "about"); break; case MENU_ITEM_EXIT: exit(EXIT_SUCCESS); break; } } static bool mouseDown(Atom eventID, void * eventData, void * context) { struct mouseEvent * event = eventData; MainMenuScreen * self = context; if (event->position.y >= 298.0f) { if (event->position.y >= 342.0f) { menuAction(self, (event->position.y - 342.0f) / 32.0f + 1); } else if (event->position.y >= 326.0f) { if (event->position.x < 320.0f) { menuLeft(self, 0); } else { menuRight(self, 0); } } else { menuAction(self, 0); } } return false; } static bool keyDown(Atom eventID, void * eventData, void * context) { struct keyEvent * event = eventData; MainMenuScreen * self = context; if (event->isRepeat) { return false; } switch (event->keyCode) { case KEYBOARD_ESCAPE: menuAction(self, MENU_ITEM_EXIT); break; case KEYBOARD_RETURN_OR_ENTER: case KEYBOARD_SPACEBAR: InputController_triggerAction(self->inputController, ATOM("jump")); break; case KEYBOARD_LEFT_ARROW: InputController_triggerAction(self->inputController, ATOM("left")); break; case KEYBOARD_RIGHT_ARROW: InputController_triggerAction(self->inputController, ATOM("right")); break; case KEYBOARD_DOWN_ARROW: InputController_triggerAction(self->inputController, ATOM("down")); break; case KEYBOARD_UP_ARROW: InputController_triggerAction(self->inputController, ATOM("up")); break; } return InputController_keyDown(self->inputController, event->keyCode); } static bool keyUp(Atom eventID, void * eventData, void * context) { struct keyEvent * event = eventData; MainMenuScreen * self = context; switch (event->keyCode) { case KEYBOARD_RETURN_OR_ENTER: case KEYBOARD_SPACEBAR: InputController_releaseAction(self->inputController, ATOM("jump")); return true; case KEYBOARD_LEFT_ARROW: InputController_releaseAction(self->inputController, ATOM("left")); return true; case KEYBOARD_RIGHT_ARROW: InputController_releaseAction(self->inputController, ATOM("right")); return true; case KEYBOARD_DOWN_ARROW: InputController_releaseAction(self->inputController, ATOM("down")); return true; case KEYBOARD_UP_ARROW: InputController_releaseAction(self->inputController, ATOM("up")); return true; } return InputController_keyUp(self->inputController, event->keyCode); } static bool gamepadButtonDown(Atom eventID, void * eventData, void * context) { struct gamepadButtonEvent * event = eventData; MainMenuScreen * self = context; return InputController_gamepadButtonDown(self->inputController, event->device->vendorID, event->device->productID, event->buttonID); } static bool gamepadButtonUp(Atom eventID, void * eventData, void * context) { struct gamepadButtonEvent * event = eventData; MainMenuScreen * self = context; return InputController_gamepadButtonUp(self->inputController, event->device->vendorID, event->device->productID, event->buttonID); } static bool gamepadAxisMove(Atom eventID, void * eventData, void * context) { struct gamepadAxisEvent * event = eventData; MainMenuScreen * self = context; return InputController_gamepadAxisMoved(self->inputController, event->device->vendorID, event->device->productID, event->axisID, event->value, event->lastValue); } static bool actionDown(Atom eventID, void * eventData, void * context) { MainMenuScreen * self = context; Atom actionID = eventData; if (actionID == ATOM("left")) { menuLeft(self, self->menuItemIndex); return true; } if (actionID == ATOM("right")) { menuRight(self, self->menuItemIndex); return true; } if (actionID == ATOM("down")) { menuDown(self); return true; } if (actionID == ATOM("up")) { menuUp(self); return true; } if (actionID == ATOM("jump")) { menuAction(self, self->menuItemIndex); return true; } return false; } static bool backgrounded(Atom eventID, void * eventData, void * context) { MainMenuScreen * self = context; self->backgrounded = true; return true; } static bool foregrounded(Atom eventID, void * eventData, void * context) { MainMenuScreen * self = context; self->backgrounded = false; Shell_redisplay(); return true; } static bool draw(Atom eventID, void * eventData, void * context) { MainMenuScreen * self = context; struct vertex_p2f_t2f_c4f * vertices = NULL; GLushort * indexes; unsigned int vertexCount, indexCount; double angle; char levelSetNameString[LEVEL_SET_NAME_MAX + 4]; Color4f colors[MENU_ITEM_COUNT + 1]; unsigned int colorIndex; glClearColor(1.0f, 1.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glOrtho(0.0f, 640.0f, 480.0f, 0.0f, -1.0f, 1.0f); angle = Shell_getCurrentTime() * 0.125f; GLTexture_activate(self->backgroundTexture); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glColor4f(1.0f, 1.0f, 1.0f, 0.1875f); glPushMatrix(); glScalef(2.0f, 2.0f, 1.0f); glTranslatef(cos(angle) * 80.0f - 100.0f, sin(angle) * 80.0f - 100.0f, 0.0f); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(512.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(512.0f, 512.0f); glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 512.0f); glEnd(); glPopMatrix(); GLTexture_deactivate(self->backgroundTexture); GLTexture_activate(self->titleTextTexture); glEnable(GL_BLEND); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0.0f, 1.0f); glVertex2f(64.0f, 32.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(576.0f, 32.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(576.0f, 288.0f); glTexCoord2f(0.0f, 0.0f); glVertex2f(64.0f, 288.0f); glEnd(); GLTexture_deactivate(self->titleTextTexture); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER, self->vertexBufferID); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, self->indexBufferID); for (colorIndex = 0; colorIndex < MENU_ITEM_COUNT; colorIndex++) { if (colorIndex == self->menuItemIndex) { colors[colorIndex] = MENU_COLOR_HIGHLIGHTED; } else { colors[colorIndex] = MENU_COLOR_ENABLED; } } colors[MENU_ITEM_COUNT] = colors[MENU_ITEM_PLAY]; vertexCount = indexCount = 0; if (self->menuItemIndex == 0) { snprintf_safe(levelSetNameString, LEVEL_SET_NAME_MAX + 4, "< %s >", self->gameSession->levelSets[self->gameSession->selectedLevelSetIndex]->name); } else { strncpy(levelSetNameString, self->gameSession->levelSets[self->gameSession->selectedLevelSetIndex]->name, LEVEL_SET_NAME_MAX + 4); } GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[0], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 314.0f), VECTOR2f(0.5f, 0.5f), colors[0], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, levelSetNameString, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(320.0f, 334.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_COUNT], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[1], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 358.0f), VECTOR2f(0.5f, 0.5f), colors[1], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[2], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 390.0f), VECTOR2f(0.5f, 0.5f), colors[2], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[3], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 422.0f), VECTOR2f(0.5f, 0.5f), colors[3], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[4], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 454.0f), VECTOR2f(0.5f, 0.5f), colors[4], GL_UNSIGNED_SHORT, NULL, NULL, &vertexCount, &indexCount); glBufferDataARB(GL_ARRAY_BUFFER, sizeof(struct vertex_p2f_t2f_c4f) * vertexCount * 2, NULL, GL_STREAM_DRAW); glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indexCount, NULL, GL_STREAM_DRAW); //printf("Buffered %u vertices (%p -> ", vertexCount, vertices); vertices = glMapBufferARB(GL_ARRAY_BUFFER, GL_WRITE_ONLY); indexes = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); //printf("%p); buffer size %zu, struct size %zu; %p\n", vertices, sizeof(struct vertex_p2f_t2f_c4f) * vertexCount, sizeof(struct vertex_p2f_t2f_c4f), vertices + vertexCount); vertexCount = indexCount = 0; GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[0], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 314.0f), VECTOR2f(0.5f, 0.5f), colors[0], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, levelSetNameString, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(320.0f, 334.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_COUNT], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[1], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 358.0f), VECTOR2f(0.5f, 0.5f), colors[1], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[2], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 390.0f), VECTOR2f(0.5f, 0.5f), colors[2], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[3], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 422.0f), VECTOR2f(0.5f, 0.5f), colors[3], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, menuItemNames[4], GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 454.0f), VECTOR2f(0.5f, 0.5f), colors[4], GL_UNSIGNED_SHORT, vertices, indexes, &vertexCount, &indexCount); //printf("Wrote %u vertices\n", vertexCount); glUnmapBufferARB(GL_ARRAY_BUFFER); glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER); glVertexPointer(2, GL_FLOAT, sizeof(struct vertex_p2f_t2f_c4f), (void *) offsetof(struct vertex_p2f_t2f_c4f, position)); glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex_p2f_t2f_c4f), (void *) offsetof(struct vertex_p2f_t2f_c4f, texCoords)); glColorPointer(4, GL_FLOAT, sizeof(struct vertex_p2f_t2f_c4f), (void *) offsetof(struct vertex_p2f_t2f_c4f, color)); GLTexture_activate(self->bitmapFont->atlas->texture); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0); GLTexture_deactivate(self->bitmapFont->atlas->texture); glBindBufferARB(GL_ARRAY_BUFFER, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); if (!self->backgrounded) { Shell_redisplay(); } return true; } void MainMenuScreen_activate(MainMenuScreen * self) { EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_MOUSE_DOWN), mouseDown, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_KEY_DOWN), keyDown, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_KEY_UP), keyUp, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_BUTTON_DOWN), gamepadButtonDown, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_BUTTON_UP), gamepadButtonUp, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_AXIS_MOVE), gamepadAxisMove, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_BACKGROUNDED), backgrounded, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_FOREGROUNDED), foregrounded, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_DRAW), draw, self); EventDispatcher_registerForEvent(self->inputController->eventDispatcher, ATOM(INPUT_CONTROLLER_EVENT_ACTION_DOWN), actionDown, self); Shell_redisplay(); Shell_setCursorVisible(true); } void MainMenuScreen_deactivate(MainMenuScreen * self) { EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_MOUSE_DOWN), mouseDown, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_KEY_DOWN), keyDown, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_KEY_UP), keyUp, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_BUTTON_DOWN), gamepadButtonDown, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_BUTTON_UP), gamepadButtonUp, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_GAMEPAD_AXIS_MOVE), gamepadAxisMove, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_BACKGROUNDED), backgrounded, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_FOREGROUNDED), foregrounded, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_DRAW), draw, self); EventDispatcher_unregisterForEvent(self->inputController->eventDispatcher, ATOM(INPUT_CONTROLLER_EVENT_ACTION_DOWN), actionDown, self); }