// 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/LevelFileIO.h" #include "watertowerclassic/SettingsScreen.h" #include "watertowerclassic/ShellStateGlobals.h" #include "watertowerclassic/SharedEvents.h" #include #include #define SUPERCLASS Screen enum { MENU_ITEM_CONTROLS, MENU_ITEM_SOUND, MENU_ITEM_MUSIC, MENU_ITEM_SCALE, MENU_ITEM_FILTER, MENU_ITEM_FULLSCREEN, MENU_ITEM_VSYNC, MENU_ITEM_DONE, MENU_ITEM_COUNT }; SettingsScreen * SettingsScreen_create(GameSession * gameSession) { stemobject_create_implementation(SettingsScreen, init, gameSession) } bool SettingsScreen_init(SettingsScreen * self, GameSession * gameSession) { call_super(init, self); self->activate = SettingsScreen_activate; self->deactivate = SettingsScreen_deactivate; self->dispose = SettingsScreen_dispose; self->gameSession = gameSession; self->inputController = InputController_create(gameSession->inputMap, "left", "right", "down", "up", "jump", "action", "pause", NULL); self->menuItemIndex = 0; glGenBuffersARB(1, &self->vertexBufferID); glGenBuffersARB(1, &self->indexBufferID); self->bitmapFont = ResourceManager_referenceResource(self->gameSession->resourceManager, "bitmap_font", "pixelfont.json"); return true; } void SettingsScreen_dispose(SettingsScreen * self) { glDeleteBuffersARB(1, &self->vertexBufferID); glDeleteBuffersARB(1, &self->indexBufferID); ResourceManager_releaseResource(self->gameSession->resourceManager, "bitmap_font", "pixelfont.json"); InputController_dispose(self->inputController); call_super(dispose, self); } static void menuAction(SettingsScreen * self, unsigned int menuItemIndex) { switch (menuItemIndex) { case MENU_ITEM_CONTROLS: InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, "controls"); break; case MENU_ITEM_SOUND: Preferences_set(self->gameSession->preferences, "play_sound", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, "play_sound")))); Shell_redisplay(); break; case MENU_ITEM_MUSIC: Preferences_set(self->gameSession->preferences, "play_music", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, "play_music")))); Shell_redisplay(); break; case MENU_ITEM_SCALE: Preferences_set(self->gameSession->preferences, "integer_scale", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, "integer_scale")))); Shell_redisplay(); break; case MENU_ITEM_FILTER: Preferences_set(self->gameSession->preferences, "linear_filter", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, "linear_filter")))); Shell_redisplay(); break; case MENU_ITEM_FULLSCREEN: Preferences_set(self->gameSession->preferences, "fullscreen", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, "fullscreen")))); if (valueGetBoolean(Preferences_get(self->gameSession->preferences, "fullscreen"))) { Shell_enterFullScreen(Shell_getDisplayIndexFromWindow()); } else { Shell_exitFullScreen(); } Shell_redisplay(); break; case MENU_ITEM_VSYNC: Preferences_set(self->gameSession->preferences, Shell_isFullScreen() ? "vsync_fullscreen" : "vsync_window", valueCreateBoolean(!valueGetBoolean(Preferences_get(self->gameSession->preferences, Shell_isFullScreen() ? "vsync_fullscreen" : "vsync_window")))); Shell_setVSync(valueGetBoolean(Preferences_get(self->gameSession->preferences, Shell_isFullScreen() ? "vsync_fullscreen" : "vsync_window")), Shell_isFullScreen()); Shell_redisplay(); break; case MENU_ITEM_DONE: GameSession_savePreferences(self->gameSession); InputController_reset(self->inputController); ScreenManager_transition(self->screenManager, self->gameSession->viewingSettingsFromGameplay ? "back_to_game" : "back"); break; } } static void menuLeft(SettingsScreen * self) { if (self->menuItemIndex != MENU_ITEM_CONTROLS && self->menuItemIndex != MENU_ITEM_DONE) { menuAction(self, self->menuItemIndex); } } static void menuRight(SettingsScreen * self) { if (self->menuItemIndex != MENU_ITEM_CONTROLS && self->menuItemIndex != MENU_ITEM_DONE) { menuAction(self, self->menuItemIndex); } } static void menuDown(SettingsScreen * self) { self->menuItemIndex += 1; self->menuItemIndex %= MENU_ITEM_COUNT; Shell_redisplay(); } static void menuUp(SettingsScreen * self) { self->menuItemIndex += MENU_ITEM_COUNT - 1; self->menuItemIndex %= MENU_ITEM_COUNT; Shell_redisplay(); } static bool mouseDown(Atom eventID, void * eventData, void * context) { struct mouseEvent * event = eventData; SettingsScreen * self = context; if (event->position.y >= 214.0f) { menuAction(self, (event->position.y - 214.0f) / 32.0f); } return false; } static bool keyDown(Atom eventID, void * eventData, void * context) { struct keyEvent * event = eventData; SettingsScreen * self = context; if (event->isRepeat) { return false; } switch (event->keyCode) { case KEYBOARD_ESCAPE: menuAction(self, MENU_ITEM_DONE); return true; case KEYBOARD_RETURN_OR_ENTER: case KEYBOARD_SPACEBAR: InputController_triggerAction(self->inputController, ATOM("jump")); return true; case KEYBOARD_LEFT_ARROW: InputController_triggerAction(self->inputController, ATOM("left")); return true; case KEYBOARD_RIGHT_ARROW: InputController_triggerAction(self->inputController, ATOM("right")); return true; case KEYBOARD_DOWN_ARROW: InputController_triggerAction(self->inputController, ATOM("down")); return true; case KEYBOARD_UP_ARROW: InputController_triggerAction(self->inputController, ATOM("up")); return true; } return InputController_keyDown(self->inputController, event->keyCode); } static bool keyUp(Atom eventID, void * eventData, void * context) { struct keyEvent * event = eventData; SettingsScreen * 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; SettingsScreen * 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; SettingsScreen * 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; SettingsScreen * 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) { SettingsScreen * self = context; Atom actionID = eventData; if (actionID == ATOM("left")) { menuLeft(self); return true; } if (actionID == ATOM("right")) { menuRight(self); 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; } if (actionID == ATOM("action")) { menuAction(self, MENU_ITEM_DONE); return true; } return false; } #define ITEM_STRING_MAX 32 static void getMenuItemVertices(SettingsScreen * self, struct vertex_p2f_t2f_c4f * outVertices, GLushort * outIndexes, unsigned int * ioVertexCount, unsigned int * ioIndexCount) { Color4f colors[MENU_ITEM_COUNT]; unsigned int colorIndex; char soundString[ITEM_STRING_MAX]; char musicString[ITEM_STRING_MAX]; char scaleString[ITEM_STRING_MAX]; char filterString[ITEM_STRING_MAX]; char fullScreenString[ITEM_STRING_MAX]; char vsyncString[ITEM_STRING_MAX]; snprintf_safe(soundString, ITEM_STRING_MAX, "SOUND: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, "play_sound")) ? "ON" : "OFF"); snprintf_safe(musicString, ITEM_STRING_MAX, "MUSIC: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, "play_music")) ? "ON" : "OFF"); snprintf_safe(scaleString, ITEM_STRING_MAX, "SCALE: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, "integer_scale")) ? "INTEGER" : "FREE"); snprintf_safe(filterString, ITEM_STRING_MAX, "FILTER: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, "linear_filter")) ? "LINEAR" : "NEAREST"); snprintf_safe(fullScreenString, ITEM_STRING_MAX, "FULLSCREEN: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, "fullscreen")) ? "ON" : "OFF"); snprintf_safe(vsyncString, ITEM_STRING_MAX, "VSYNC: %s", valueGetBoolean(Preferences_get(self->gameSession->preferences, Shell_isFullScreen() ? "vsync_fullscreen" : "vsync_window")) ? "ON" : "OFF"); for (colorIndex = 0; colorIndex < MENU_ITEM_COUNT; colorIndex++) { if (colorIndex == self->menuItemIndex) { colors[colorIndex] = MENU_COLOR_HIGHLIGHTED; } else { colors[colorIndex] = MENU_COLOR_ENABLED; } } GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, "CONTROLS", GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 230.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_CONTROLS], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, soundString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 262.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_SOUND], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, musicString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 294.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_MUSIC], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, scaleString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 326.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_SCALE], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, filterString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 358.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_FILTER], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, fullScreenString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 390.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_FULLSCREEN], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, vsyncString, GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 422.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_VSYNC], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, "DONE", GLBITMAPFONT_USE_STRLEN, -32, VECTOR2f(320.0f, 454.0f), VECTOR2f(0.5f, 0.5f), colors[MENU_ITEM_DONE], GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); } static bool draw(Atom eventID, void * eventData, void * context) { SettingsScreen * self = context; struct vertex_p2f_t2f_c4f * vertices; GLushort * indexes; unsigned int vertexCount, indexCount; 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); 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); vertexCount = indexCount = 0; getMenuItemVertices(self, NULL, NULL, &vertexCount, &indexCount); glBufferDataARB(GL_ARRAY_BUFFER, sizeof(struct vertex_p2f_t2f_c4f) * vertexCount, NULL, GL_STREAM_DRAW); glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indexCount, NULL, GL_STREAM_DRAW); vertices = glMapBufferARB(GL_ARRAY_BUFFER, GL_WRITE_ONLY); indexes = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); vertexCount = indexCount = 0; getMenuItemVertices(self, vertices, indexes, &vertexCount, &indexCount); 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); return true; } static bool confirmQuit(Atom eventID, void * eventData, void * context) { SettingsScreen * self = context; if (self->gameSession->testingLevel && self->gameSession-> viewingSettingsFromGameplay && self->gameSession->hasUnsavedLevelChanges) { menuAction(self, MENU_ITEM_DONE); return true; } return false; } void SettingsScreen_activate(SettingsScreen * 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_DRAW), draw, self); EventDispatcher_registerForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_CONFIRM_QUIT), confirmQuit, self); EventDispatcher_registerForEvent(self->inputController->eventDispatcher, ATOM(INPUT_CONTROLLER_EVENT_ACTION_DOWN), actionDown, self); Shell_redisplay(); Shell_setCursorVisible(true); } void SettingsScreen_deactivate(SettingsScreen * 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_DRAW), draw, self); EventDispatcher_unregisterForEvent(self->screenManager->eventDispatcher, ATOM(EVENT_CONFIRM_QUIT), confirmQuit, self); EventDispatcher_unregisterForEvent(self->inputController->eventDispatcher, ATOM(INPUT_CONTROLLER_EVENT_ACTION_DOWN), actionDown, self); }