// Copyright (c) 2014 Alex Diener. All rights reserved. #include "shell/Shell.h" #include "utilities/IOUtilities.h" #include "watertowerclassic/LevelEditorInspectorView.h" #include #include #define SUPERCLASS StemObject #define INSPECTOR_WIDTH 128 #define PIXELS_PER_DRAG_UNIT 8 LevelEditorInspectorView * LevelEditorInspectorView_create(LevelEditorScreen * levelEditorScreen, ResourceManager * resourceManager, int offsetX, int offsetY) { stemobject_create_implementation(LevelEditorInspectorView, init, levelEditorScreen, resourceManager, offsetX, offsetY) } bool LevelEditorInspectorView_init(LevelEditorInspectorView * self, LevelEditorScreen * levelEditorScreen, ResourceManager * resourceManager, int offsetX, int offsetY) { call_super(init, self); self->dispose = LevelEditorInspectorView_dispose; self->levelEditorScreen = levelEditorScreen; self->resourceManager = resourceManager; self->bitmapFont = ResourceManager_referenceResource(self->resourceManager, "bitmap_font", "pixelfont.json"); self->offsetX = offsetX; self->offsetY = offsetY; self->block = NULL; self->draggingFamilyID = false; self->draggingInitialDelay = false; self->draggingOnPeriod = false; self->draggingOffPeriod = false; self->draggingSpeed = false; glGenBuffersARB(1, &self->vertexBufferID); glGenBuffersARB(1, &self->indexBufferID); return true; } void LevelEditorInspectorView_dispose(LevelEditorInspectorView * self) { glDeleteBuffersARB(1, &self->vertexBufferID); glDeleteBuffersARB(1, &self->indexBufferID); ResourceManager_releaseResource(self->resourceManager, "bitmap_font", "pixelfont.json"); call_super(dispose, self); } bool LevelEditorInspectorView_mouseDown(LevelEditorInspectorView * self, struct mouseEvent * event) { if (self->block == NULL) { return false; } switch (self->block->type) { case BLOCK_TYPE_LEVER: if (event->position.y >= self->offsetY + 24 && event->position.y <= self->offsetY + 40) { self->draggingFamilyID = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.lever.id * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } break; case BLOCK_TYPE_TELEPORT: if (event->position.y >= self->offsetY + 24 && event->position.y <= self->offsetY + 40) { self->draggingFamilyID = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.teleport.id * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } break; case BLOCK_TYPE_SWITCHABLE: if (event->position.y >= self->offsetY + 24 && event->position.y <= self->offsetY + 40) { self->block->properties.switchable.on = !self->block->properties.switchable.on; Shell_redisplay(); return true; } if (event->position.y >= self->offsetY + 56 && event->position.y <= self->offsetY + 88) { int switchIDIndex; for (switchIDIndex = 0; switchIDIndex < SWITCH_ID_COUNT_MAX; switchIDIndex++) { if (event->position.x >= self->offsetX + 4 + 20 * (switchIDIndex % 6) && event->position.x <= self->offsetX + 4 + 20 * (switchIDIndex % 6 + 1) && event->position.y >= self->offsetY + 56 + 16 * (switchIDIndex / 6) && event->position.y <= self->offsetY + 56 + 16 * (switchIDIndex / 6 + 1)) { self->draggingFamilyID = true; self->switchIDIndex = switchIDIndex; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.switchable.switchIDs[switchIDIndex] * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } } } break; case BLOCK_TYPE_PHASING: if (event->position.y >= self->offsetY + 24 && event->position.y <= self->offsetY + 40) { self->block->properties.phasing.initiallyOn = !self->block->properties.phasing.initiallyOn; Shell_redisplay(); return true; } if (event->position.y >= self->offsetY + 40 && event->position.y <= self->offsetY + 56) { self->draggingInitialDelay = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.phasing.initialDelay * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } if (event->position.y >= self->offsetY + 56 && event->position.y <= self->offsetY + 72) { self->draggingOnPeriod = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.phasing.onPeriod * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } if (event->position.y >= self->offsetY + 72 && event->position.y <= self->offsetY + 88) { self->draggingOffPeriod = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + self->block->properties.phasing.offPeriod * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } break; case BLOCK_TYPE_MOVING: if (event->position.y >= self->offsetY + 24 && event->position.y <= self->offsetY + 40) { self->block->properties.moving.direction++; self->block->properties.moving.direction %= 4; Shell_redisplay(); return true; } if (event->position.y >= self->offsetY + 40 && event->position.y <= self->offsetY + 56) { self->draggingSpeed = true; self->rawDragOrigin = VECTOR2f(roundf(event->rawPosition.x), roundf(event->rawPosition.y) + xtof(self->block->properties.moving.speed) / 0.125f * PIXELS_PER_DRAG_UNIT); Shell_redisplay(); return true; } break; default: break; } return false; } bool LevelEditorInspectorView_mouseUp(LevelEditorInspectorView * self, struct mouseEvent * event) { if (self->draggingFamilyID) { self->draggingFamilyID = false; Shell_redisplay(); return true; } if (self->draggingInitialDelay) { self->draggingInitialDelay = false; Shell_redisplay(); return true; } if (self->draggingOnPeriod) { self->draggingOnPeriod = false; Shell_redisplay(); return true; } if (self->draggingOffPeriod) { self->draggingOffPeriod = false; Shell_redisplay(); return true; } if (self->draggingSpeed) { self->draggingSpeed = false; Shell_redisplay(); return true; } return false; } bool LevelEditorInspectorView_mouseDragged(LevelEditorInspectorView * self, struct mouseEvent * event) { if (self->block == NULL) { return false; } switch (self->block->type) { case BLOCK_TYPE_LEVER: if (self->draggingFamilyID) { int newID = roundf((self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT); if (newID < 1) { newID = 1; } else if (newID > 15) { newID = 15; } self->block->properties.lever.id = newID; Shell_redisplay(); return true; } break; case BLOCK_TYPE_TELEPORT: if (self->draggingFamilyID) { int newID = roundf((self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT); if (newID < 1) { newID = 1; } else if (newID > 15) { newID = 15; } self->block->properties.teleport.id = newID; Shell_redisplay(); return true; } break; case BLOCK_TYPE_SWITCHABLE: if (self->draggingFamilyID) { int newID = roundf((self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT); if (newID < 0) { newID = 0; } else if (newID > 15) { newID = 15; } self->block->properties.switchable.switchIDs[self->switchIDIndex] = newID; Shell_redisplay(); return true; } break; case BLOCK_TYPE_PHASING: if (self->draggingInitialDelay) { int newValue = roundf(self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT; if (newValue < 0) { newValue = 0; } else if (newValue > 30) { newValue = 30; } self->block->properties.phasing.initialDelay = newValue; Shell_redisplay(); return true; } if (self->draggingOnPeriod) { int newValue = roundf(self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT; if (newValue < 0) { newValue = 0; } else if (newValue > 30) { newValue = 30; } self->block->properties.phasing.onPeriod = newValue; Shell_redisplay(); return true; } if (self->draggingOffPeriod) { int newValue = roundf(self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT; if (newValue < 0) { newValue = 0; } else if (newValue > 30) { newValue = 30; } self->block->properties.phasing.offPeriod = newValue; Shell_redisplay(); return true; } break; case BLOCK_TYPE_MOVING: if (self->draggingSpeed) { float newSpeed = roundf((self->rawDragOrigin.y - event->rawPosition.y) / PIXELS_PER_DRAG_UNIT) * 0.125f; if (newSpeed < 0.0f) { newSpeed = 0.0f; } else if (newSpeed > 3.875f) { newSpeed = 3.875f; } self->block->properties.moving.speed = ftox(newSpeed); Shell_redisplay(); return true; } break; default: break; } return false; } static const char * humanReadableBlockType(enum LevelBlockType blockType) { switch (blockType) { case BLOCK_TYPE_EMPTY: return ""; case BLOCK_TYPE_STONE: return "Stone block"; case BLOCK_TYPE_DRY_SOLID: return "Blue sponge"; case BLOCK_TYPE_WET_SOLID: return "Yellow sponge"; case BLOCK_TYPE_CRUMBLE: return "Crumbling block"; case BLOCK_TYPE_SWITCHABLE: return "Switchable block"; case BLOCK_TYPE_DEATH: return "Deadly block"; case BLOCK_TYPE_MOVING: return "Moving block"; case BLOCK_TYPE_FLOATING: return "Floating block"; case BLOCK_TYPE_PHASING: return "Phasing block"; case BLOCK_TYPE_LOCK: return "Locked block"; case BLOCK_TYPE_LADDER: return "Ladder"; case BLOCK_TYPE_LEVER: return "Switch"; case BLOCK_TYPE_PUSHABLE: return "Pushable block"; case BLOCK_TYPE_TRAMPOLINE: return "Trampoline"; case BLOCK_TYPE_TELEPORT: return "Teleport"; case BLOCK_TYPE_FIRE: return "Fire"; case BLOCK_TYPE_ICE: return "Ice block"; case BLOCK_TYPE_SAND: return "Sand block"; case BLOCK_TYPE_STICKY: return "Sticky block"; case BLOCK_TYPE_FALLING: return "Falling block"; default: return "[unknown]"; } } static const char * direction4ToString(direction4 direction) { switch (direction) { case DIRECTION_LEFT: return "LEFT"; case DIRECTION_RIGHT: return "RIGHT"; case DIRECTION_DOWN: return "DOWN"; case DIRECTION_UP: return "UP"; } return NULL; } static void getTextVertices(LevelEditorInspectorView * self, struct vertex_p2f_t2f_c4f * outVertices, GLushort * outIndexes, unsigned int * ioVertexCount, unsigned int * ioIndexCount) { char string[32]; unsigned int switchIDIndex; GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, humanReadableBlockType(self->block->type), GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + INSPECTOR_WIDTH / 2, self->offsetY), VECTOR2f(0.5f, 1.0f), MENU_COLOR_NONINTERACTIVE, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); switch (self->block->type) { case BLOCK_TYPE_LEVER: snprintf_safe(string, 32, "Switch ID: %u", self->block->properties.lever.id); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 24), VECTOR2f(0.0f, 1.0f), self->draggingFamilyID ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); break; case BLOCK_TYPE_TELEPORT: snprintf_safe(string, 32, "Teleport ID: %u", self->block->properties.teleport.id); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 24), VECTOR2f(0.0f, 1.0f), self->draggingFamilyID ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); break; case BLOCK_TYPE_SWITCHABLE: snprintf_safe(string, 32, "Initially on: %s", self->block->properties.switchable.on ? "YES" : "NO"); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 24), VECTOR2f(0.0f, 1.0f), MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, "Switch IDs:", GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 40), VECTOR2f(0.0f, 1.0f), MENU_COLOR_NONINTERACTIVE, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); for (switchIDIndex = 0; switchIDIndex < SWITCH_ID_COUNT_MAX; switchIDIndex++) { snprintf_safe(string, 32, "%u", self->block->properties.switchable.switchIDs[switchIDIndex]); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4 + 20 * (switchIDIndex % 6), self->offsetY + 56 + 16 * (switchIDIndex / 6)), VECTOR2f(0.0f, 1.0f), self->draggingFamilyID && self->switchIDIndex == switchIDIndex ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); } break; case BLOCK_TYPE_PHASING: snprintf_safe(string, 32, "Initially on: %s", self->block->properties.phasing.initiallyOn ? "YES" : "NO"); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 24), VECTOR2f(0.0f, 1.0f), MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); snprintf_safe(string, 32, "Initial delay: %d", self->block->properties.phasing.initialDelay); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 40), VECTOR2f(0.0f, 1.0f), self->draggingInitialDelay ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); snprintf_safe(string, 32, "On period: %d", self->block->properties.phasing.onPeriod); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 56), VECTOR2f(0.0f, 1.0f), self->draggingOnPeriod ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); snprintf_safe(string, 32, "Off period: %d", self->block->properties.phasing.offPeriod); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 72), VECTOR2f(0.0f, 1.0f), self->draggingOffPeriod ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); break; case BLOCK_TYPE_MOVING: snprintf_safe(string, 32, "Direction: %s", direction4ToString(self->block->properties.moving.direction)); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 24), VECTOR2f(0.0f, 1.0f), MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); snprintf_safe(string, 32, "Speed: %.3f", xtof(self->block->properties.moving.speed)); GLBitmapFont_getStringVerticesWithColor(self->bitmapFont, string, GLBITMAPFONT_USE_STRLEN, -16, VECTOR2f(self->offsetX + 4, self->offsetY + 40), VECTOR2f(0.0f, 1.0f), self->draggingSpeed ? MENU_COLOR_SELECTED : MENU_COLOR_ENABLED, GL_UNSIGNED_SHORT, outVertices, outIndexes, ioVertexCount, ioIndexCount); break; default: break; } } void LevelEditorInspectorView_draw(LevelEditorInspectorView * self) { struct vertex_p2f_t2f_c4f * vertices; GLushort * indexes; unsigned int vertexCount, indexCount; if (self->block == NULL) { return; } 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; getTextVertices(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; getTextVertices(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); }