/*************************************************************************** * Copyright (C) 2001-2023 by Tom Hicks * * headhunter3@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "level.h" #include "version.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #ifdef WIN32 #pragma warning(disable : 4996) #endif #pragma clang diagnostic pop using namespace std; namespace OpenArena { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" Level::Level(EventManager* eventManager) { _eventManager = eventManager; _window = NULL; textureNames = NULL; numTextures = 0; screen.SetName(OPENARENA_VERSION); showFPS = false; showConsole = false; nextLevel = "intro.map"; gamedir = "oa/"; sound = true; defaultPlayer = new Player; numTriangles = 0; triangles = NULL; /* for(int x=0; x<4; x++) LightAmbient[x] = 5; */ // Player Stuff mouseSpeed = 5; turnSpeed = 1.0f; moveSpeed = 0.2f; mlook = true; } Level::~Level() { if (defaultPlayer) { delete defaultPlayer; defaultPlayer = NULL; } } bool Level::LoadMap(string mapname) { ifstream input; string readBuffer; // players.Insert(*defaultPlayer); mapname = gamedir + "maps/" + mapname; if (tolower(Right(mapname, 4)) != ".map") mapname = mapname + ".map"; input.open(mapname.c_str()); if (!input) { char* tmpChar = new char[mapname.length() + 41]; strcpy(tmpChar, "Unable to load level file "); strcat(tmpChar, mapname.c_str()); strcat(tmpChar, " doesn't exist."); // This needs to be abstracted somehow #ifdef WIN32 MessageBox(NULL, tmpChar, "ERROR", MB_OK | MB_ICONEXCLAMATION); #endif delete[] tmpChar; return false; } else { ConsolePrint("map file \"" + mapname + "\" opened successfully"); } // Gravity input >> readBuffer; gravity = (float)atof(readBuffer.c_str()); // Number of triangles input >> readBuffer; numTriangles = Integer(readBuffer); // Triangle Data triangles = new Triangle[numTriangles]; for (unsigned int i = 0; i < numTriangles; i++) { // TextureID input >> readBuffer; triangles[i].texID = Integer(readBuffer); for (unsigned int i2 = 0; i2 < 3; i2++) { // Vertex Data input >> readBuffer; triangles[i].vertecies[i2].coordinates.x = atof(readBuffer.c_str()); input >> readBuffer; triangles[i].vertecies[i2].coordinates.y = atof(readBuffer.c_str()); input >> readBuffer; triangles[i].vertecies[i2].coordinates.z = atof(readBuffer.c_str()); input >> readBuffer; triangles[i].vertecies[i2].textureCoordinates.x = (float)atof(readBuffer.c_str()); input >> readBuffer; triangles[i].vertecies[i2].textureCoordinates.y = (float)atof(readBuffer.c_str()); } // Normal input >> readBuffer; triangles[i].normal.x = atof(readBuffer.c_str()); input >> readBuffer; triangles[i].normal.y = atof(readBuffer.c_str()); input >> readBuffer; triangles[i].normal.z = atof(readBuffer.c_str()); } char lpszNumTriangles[6]; sprintf(lpszNumTriangles, "%d", numTriangles); ConsolePrint(lpszNumTriangles + string(" triangles successfully read")); // Number of textures; input >> readBuffer; numTextures = Integer(readBuffer); // Texture data textureNames = new string[numTextures]; for (unsigned int i = 0; i < numTextures; i++) { input >> textureNames[i]; } LoadGLTextures(); char lpszNumTextures[6]; sprintf(lpszNumTextures, "%d", numTextures); ConsolePrint(lpszNumTextures + string(" textures successfully read")); // BGM input >> bgm; // Build display list // Sound if (sound) { ConsolePrint("Starting sound"); // This needs to be abstracted somehow #ifdef WIN32 BASS_Init(-1, 44100, BASS_DEVICE_LEAVEVOL, g_hWnd); BASS_Start(); BASS_CDInit(NULL, BASS_DEVICE_LEAVEVOL); #endif if (bgm.length() >= 4) { if (toupper(bgm[0]) == 'C' && toupper(bgm[1]) == 'D' && toupper(bgm[2]) == 'A') { bgmCDA = Integer(bgm.substr(3, bgm.length())); // This needs to be abstracted somehow #ifdef WIN32 BASS_CDPlay(bgmCDA, 1, 0); #endif } else { bgmCDA = 0; string tmpstr = gamedir + "music/bgm/" + bgm; // This needs to be abstracted somehow #ifdef WIN32 bgmStream = BASS_StreamCreateFile(0, (void*)tmpstr.c_str(), 0, 0, BASS_STREAM_AUTOFREE); BASS_StreamPlay(bgmStream, 1, BASS_SAMPLE_LOOP); #endif } } ConsolePrint("Sound init complete"); } else { ConsolePrint("Sound disabled"); } return true; } bool Level::LoadMap() { return LoadMap(nextLevel); } void Level::SaveMap(string mapname) { ofstream output; mapname = gamedir + "maps/" + mapname; if (tolower(Right(mapname, 4)) != ".map") mapname = mapname + ".map"; output.open(mapname.c_str()); if (!output) { char* tmpChar = new char[mapname.length() + 42]; strcpy(tmpChar, "Unable to save level file "); strcat(tmpChar, mapname.c_str()); strcat(tmpChar, " already exists."); // This needs to be abstracted somehow #ifdef WIN32 MessageBox(NULL, tmpChar, "ERROR", MB_OK | MB_ICONEXCLAMATION); #endif delete[] tmpChar; return; } // Gravity output << gravity << endl; // Number of triangles output << numTriangles << endl; // Triangle Data for (unsigned int i = 0; i < numTriangles; i++) { output << triangles[i].texID << endl; for (unsigned int i2 = 0; i2 < 3; i2++) { output << triangles[i].vertecies[i2].coordinates.x << " " << triangles[i].vertecies[i2].coordinates.y << " " << triangles[i].vertecies[i2].coordinates.z << " " << triangles[i].vertecies[i2].textureCoordinates.x << " " << triangles[i].vertecies[i2].textureCoordinates.y << endl; } output << triangles[i].normal.x << " " << triangles[i].normal.y << " " << triangles[i].normal.z << endl; } // Number of textures; output << numTextures; // Texture data for (unsigned int i = 0; i < numTextures; i++) { output << textureNames[i]; } // BGM output << bgm; } void Level::Render() { glPushMatrix(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // Move the camera to where the player is defaultPlayer->camera.Look(); for (uint32_t i = 0; i < numTriangles; i++) { glBindTexture(GL_TEXTURE_2D, textures[triangles[i].texID]->ID()); // Bind this triangle's texture glBegin(GL_TRIANGLES); // glNormal3f((GLfloat)triangles[i].normal.x, (GLfloat)triangles[i].normal.y, //(GLfloat)triangles[i].normal.z); for (uint32_t i2 = 0; i2 < 3; i2++) { glTexCoord2f(triangles[i].vertecies[i2].textureCoordinates.x, triangles[i].vertecies[i2].textureCoordinates.y); glVertex3f((GLfloat)triangles[i].vertecies[i2].coordinates.x, (GLfloat)triangles[i].vertecies[i2].coordinates.y, (GLfloat)triangles[i].vertecies[i2].coordinates.z); } glEnd(); } glPopMatrix(); // Draw HUD // ummm nothing here yet // Draw FPS // This may not work yet if (showFPS) { char fps[20]; sprintf(fps, "fps %d", FPS()); glPushMatrix(); glLoadIdentity(); glColor3f(1, 1, 1); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glFont.Print(glFont.ScreenWidth() - 120, glFont.ScreenHeight() - 30, fps, 0); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glPopMatrix(); } if (showConsole) { glPushMatrix(); glLoadIdentity(); glColor3f(1, 1, 1); // disable lighting and depth testing glDisable(GL_DEPTH_TEST); glBindTexture(GL_TEXTURE_2D, menuTextures[GL_MY_TEXTURE_CONSOLEBACKGROUND].ID()); // glBindTexture(GL_TEXTURE_2D, 4); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, screen.GetWidth(), 0, screen.GetHeight(), -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(0, screen.GetHeight() / 2); glTexCoord2f(0, 1); glVertex2i(0, screen.GetHeight()); glTexCoord2f(1, 1); glVertex2i(screen.GetWidth(), screen.GetHeight()); glTexCoord2f(1, 0); glVertex2i(screen.GetWidth(), screen.GetHeight() / 2); glEnd(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glDisable(GL_LIGHTING); // enable blending glEnable(GL_BLEND); // render the console background // render the console output text unsigned int i; for (i = 0; i < MAX_CONSOLE_LINES - 1; i++) { Print(30, glFont.ScreenHeight() - i * 20, consoleOutput[MAX_CONSOLE_LINES - i - 2].c_str(), 0); } // Render the currently typed command Print(30, glFont.ScreenHeight() - i * 20, consoleHistory[0].c_str(), 0); // disable blending glDisable(GL_BLEND); // re-enable lighting and depth testing // glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); } } void Level::UnloadMap() { // Stop audio // TODO: Free audio resources. // Delete display list // Free all polygon data if (triangles) { delete[] triangles; triangles = NULL; } // Free all map textures if (!textures.IsEmpty()) { // TODOiterate over list and delete all entries } textures.Clear(); // Free the array of texture names if (textureNames) { delete[] textureNames; textureNames = NULL; } } void Level::LoadGLTextures() { if (_window != NULL) { /* GLfloat light[4] = {1.0f,1.0f,1.0f,0.5f}; glLightfv(GL_LIGHT1, GL_AMBIENT, light); glEnable(GL_LIGHT1); glEnable(GL_LIGHTING); */ textures.Clear(); for (uint32_t i = 0; i < numTextures; i++) { Texture* texture = new Texture(); if (!texture->Load(gamedir + "textures/" + textureNames[i])) texture->Load(DEFAULT_TEXTURE_NAME); textures[i] = texture; } if (!glFont.BuildFont((gamedir + "textures/menu/font.bmp").c_str())) glFont.BuildFont("oa/textures/menu/font.bmp"); // Load the console background image if (!menuTextures[GL_MY_TEXTURE_CONSOLEBACKGROUND].Load(gamedir + "textures/menu/con_back.tga")) { menuTextures[GL_MY_TEXTURE_CONSOLEBACKGROUND].Load("oa/textures/menu/con_back.bmp"); } } } uint32_t Level::FPS() { static uint32_t fps = 0; static uint32_t fps2 = 0; static float last = 0.0f; // might need to change to double // hmmm what the hell is this called in linux I should possibly abstract this #ifdef WIN32 float time = GetTickCount() * .001f; #else float time = 0; #endif ++fps; if (time > last + 1) { last = time; fps2 = fps; fps = 0; } return fps2; } void Level::Execute(string cmd) { string command; uint32_t i = 0xFFFFFFFF; while (word(cmd, ++i) != "") { command = tolower(word(cmd, i)); if (command == "set") { command = tolower(word(cmd, ++i)); if (command == "turnspeed") { turnSpeed = (float)atof(word(cmd, ++i).c_str()); // Floating(word(cmd, ++i)); } else if (command == "movespeed") { moveSpeed = (float)atof(word(cmd, ++i).c_str()); // FLoating(word(cmd, ++i)); } else if (command == "mousespeed") { mouseSpeed = (float)atof(word(cmd, ++i).c_str()); } else if (command == "mouselook") { mlook = Truth(word(cmd, ++i)); } else if (command == "maxfps") { maxFPS = Integer(word(cmd, ++i)); } else if (command == "sound") { sound = Truth(word(cmd, ++i)); } else if (command == "game") { gamedir = word(cmd, ++i); } else if (command == "screenwidth") { screen.SetWidth(Integer(word(cmd, ++i))); } else if (command == "screenheight") { screen.SetHeight(Integer(word(cmd, ++i))); } else if (command == "fullscreen") { screen.SetFullscreen(Truth(word(cmd, ++i))); } else if (command == "colordepth") { screen.SetColorDepth(Integer(word(cmd, ++i))); } else { ConsolePrint("Variable " + command + " doesn't exist"); } } else if (command == "bind") { command = tolower(word(cmd, ++i)); string key = tolower(word(cmd, ++i)); if (ControlScheme::GetAction(command.c_str()) == OpenArena::ControlScheme::ACTION_NONE) { ConsolePrint("No action identified by " + command); } else if (GetKey(key.c_str()) == OpenArena::KEY_UNKNOWN) { ConsolePrint("No key identified by " + key); } else { defaultPlayer[0].controls.Bind(GetKey(key.c_str()), ControlScheme::GetAction(command.c_str())); } } else if (command == "map" || command == "map_load") { nextLevel = word(cmd, ++i); UnloadMap(); if (!LoadMap()) { ConsolePrint("Unable to load level " + command); nextLevel = "intro.map"; LoadMap(); } } else if (command == "unbind") { command = tolower(word(cmd, ++i)); if (command == "all") { defaultPlayer[0].controls.UnBindAll(); } else { defaultPlayer[0].controls.UnBind(GetKey(command.c_str())); } } else if (command == "exec" || command == "config_load") { command = tolower(word(cmd, ++i)); if (!LoadConfig(command)) { ConsolePrint("Unable to load config file " + word(cmd, i)); } } else if (command == "map_save") { command = tolower(word(cmd, ++i)); SaveMap(command); } else if (command == "config_save") { command = tolower(word(cmd, ++i)); SaveConfig(command); } /* else if(command == "map_add_triangle") { if(triangles != NULL) { TRIANGLE* temp = triangles; triangles = new TRIANGLE[numTriangles + 1]; for(int i=0; icontrols.WriteToStream(output); } void Level::Print(int x, int y, const char* str, unsigned int set) { glFont.Print(x, y, str, set); } void Level::UpdateConsole(char newChar) { if (newChar == '\n') { for (int i = MAX_CONSOLE_HISTORY_LINES - 1; i > 0; i--) { consoleHistory[i] = consoleHistory[i - 1]; } consoleHistory[0] = ""; ConsolePrint(consoleHistory[1]); Execute(tolower(consoleHistory[1])); } else if (newChar == OpenArena::KEY_BACK) { consoleHistory[0] = Left(consoleHistory[0], consoleHistory[0].length() - 1); } else if (!(defaultPlayer->controls.IsBound(GetKey(&newChar), ControlScheme::ACTION_TOGGLE_CONSOLE))) { consoleHistory[0] = consoleHistory[0] + newChar; /* char errmsg[256]; sprintf(errmsg, "Unhangled keypress: %d", newChar); MessageBox(NULL, errmsg, "Balls", MB_OK); */ } } void Level::ConsolePrint(string line) { for (int i = MAX_CONSOLE_OUTPUT_LINES - 1; i > 0; i--) { consoleOutput[i] = consoleOutput[i - 1]; } consoleOutput[0] = line; } void Level::SetWindow(Window* window) { _window = window; } Window* Level::GetWindow() { return _window; } #pragma clang diagnostic pop } // End namespace OpenArena